diff --git a/SL/bus/Amily2Bus.js b/SL/bus/Amily2Bus.js index bf193bb..bb2c0ed 100644 --- a/SL/bus/Amily2Bus.js +++ b/SL/bus/Amily2Bus.js @@ -56,4 +56,9 @@ if (!window.Amily2Bus) { window.Amily2Bus = new Amily2Bus(); } -export default Amily2Bus; \ No newline at end of file +export function initializeAmilyBus() { + if (!window.Amily2Bus) { + window.Amily2Bus = new Amily2Bus(); + console.log('[Amily2] Amily2Bus 已成功初始化并附加到 window 对象'); + } +} \ No newline at end of file diff --git a/core/bus.js b/core/bus.js index cd5077b..74c9271 100644 --- a/core/bus.js +++ b/core/bus.js @@ -1,11 +1,11 @@ import { eventSource, event_types } from '/script.js'; export const LOG_LEVELS = { - DEBUG: 0, - INFO: 1, - WARN: 2, - ERROR: 3, - NONE: 99 + DEBUG: 0b0001, + INFO: 0b0010, + WARN: 0b0100, + ERROR: 0b1000, + NONE: 0 }; class FilePipe { diff --git a/imports.js b/imports.js new file mode 100644 index 0000000..6dfc6ff --- /dev/null +++ b/imports.js @@ -0,0 +1,41 @@ +// Side-effect imports (独立模块/自初始化模块) +import "./PresetSettings/index.js"; +import "./PreOptimizationViewer/index.js"; +import "./WorldEditor/WorldEditor.js"; +import './core/amily2-updater.js'; +import './SL/bus/Amily2Bus.js' + +// Re-exports (重新导出供 index.js 使用) +export { createDrawer } from "./ui/drawer.js"; +export { showPlotOptimizationProgress, updatePlotOptimizationProgress, hidePlotOptimizationProgress } from './ui/optimization-progress.js'; +export { registerSlashCommands } from "./core/commands.js"; +export { onMessageReceived, handleTableUpdate } from "./core/events.js"; +export { processPlotOptimization } from "./core/summarizer.js"; + +// External SillyTavern scripts (外部脚本) +export { getContext, extension_settings } from "/scripts/extensions.js"; +export { characters, this_chid, eventSource, event_types, saveSettingsDebounced } from '/script.js'; + +// Core Systems +export { injectTableData, generateTableContent } from "./core/table-system/injector.js"; +export { initialize as initializeRagProcessor } from "./core/rag-processor.js"; +export { loadTables, clearHighlights, rollbackAndRefill, rollbackState, commitPendingDeletions, saveStateToMessage, getMemoryState, clearUpdatedTables } from './core/table-system/manager.js'; +export { fillWithSecondaryApi } from './core/table-system/secondary-filler.js'; +export { renderTables } from './ui/table-bindings.js'; +export { log } from './core/table-system/logger.js'; +export { checkForUpdates, fetchMessageBoardContent } from './core/api.js'; +export { setUpdateInfo, applyUpdateIndicator } from './ui/state.js'; +export { pluginVersion, extensionName, defaultSettings } from './utils/settings.js'; +export { checkAuthorization, refreshUserInfo } from './utils/auth.js'; +export { tableSystemDefaultSettings } from './core/table-system/settings.js'; +export { manageLorebookEntriesForChat } from './core/lore.js'; + +// Feature Modules +export { initializeCharacterWorldBook } from './CharacterWorldBook/cwb_index.js'; +export { cwbDefaultSettings } from './CharacterWorldBook/src/cwb_config.js'; +export { bindGlossaryEvents } from './glossary/GT_bindings.js'; +export { updateOrInsertTableInChat, startContinuousRendering, stopContinuousRendering } from './ui/message-table-renderer.js'; +export { initializeRenderer } from './core/tavern-helper/renderer.js'; +export { initializeApiListener, registerApiHandler, amilyHelper, initializeAmilyHelper } from './core/tavern-helper/main.js'; +export { registerContextOptimizerMacros, resetContextBuffer } from './core/context-optimizer.js'; +export { initializeSuperMemory } from './core/super-memory/manager.js'; diff --git a/index.js b/index.js index d6f4a3d..2edecbe 100644 --- a/index.js +++ b/index.js @@ -1,36 +1,33 @@ -import { createDrawer } from "./ui/drawer.js"; -import "./PresetSettings/index.js"; // 【预设设置】独立模块 -import "./PreOptimizationViewer/index.js"; // 【优化前文查看器】独立模块 -import "./WorldEditor/WorldEditor.js"; // 【世界编辑器】独立模块 -import { showPlotOptimizationProgress, updatePlotOptimizationProgress, hidePlotOptimizationProgress } from './ui/optimization-progress.js'; -import { registerSlashCommands } from "./core/commands.js"; -import { onMessageReceived, handleTableUpdate } from "./core/events.js"; -import { processPlotOptimization } from "./core/summarizer.js"; -import { getContext } from "/scripts/extensions.js"; -import { characters, this_chid } from '/script.js'; -import { injectTableData, generateTableContent } from "./core/table-system/injector.js"; -import { initialize as initializeRagProcessor } from "./core/rag-processor.js"; -import { loadTables, clearHighlights, rollbackAndRefill, rollbackState, commitPendingDeletions, saveStateToMessage, getMemoryState, clearUpdatedTables } from './core/table-system/manager.js'; -import { fillWithSecondaryApi } from './core/table-system/secondary-filler.js'; -import { renderTables } from './ui/table-bindings.js'; -import { log } from './core/table-system/logger.js'; -import { eventSource, event_types, saveSettingsDebounced } from '/script.js'; -import { checkForUpdates, fetchMessageBoardContent } from './core/api.js'; -import { setUpdateInfo, applyUpdateIndicator } from './ui/state.js'; -import { pluginVersion, extensionName, defaultSettings } from './utils/settings.js'; -import { checkAuthorization, refreshUserInfo } from './utils/auth.js'; -import { tableSystemDefaultSettings } from './core/table-system/settings.js'; -import { extension_settings } from '/scripts/extensions.js'; -import { manageLorebookEntriesForChat } from './core/lore.js'; -import { initializeCharacterWorldBook } from './CharacterWorldBook/cwb_index.js'; -import { cwbDefaultSettings } from './CharacterWorldBook/src/cwb_config.js'; -import { bindGlossaryEvents } from './glossary/GT_bindings.js'; -import './core/amily2-updater.js'; -import { updateOrInsertTableInChat, startContinuousRendering, stopContinuousRendering } from './ui/message-table-renderer.js'; -import { initializeRenderer } from './core/tavern-helper/renderer.js'; -import { initializeApiListener, registerApiHandler, amilyHelper, initializeAmilyHelper } from './core/tavern-helper/main.js'; -import { registerContextOptimizerMacros, resetContextBuffer } from './core/context-optimizer.js'; -import { initializeSuperMemory } from './core/super-memory/manager.js'; +import { + createDrawer, + showPlotOptimizationProgress, updatePlotOptimizationProgress, hidePlotOptimizationProgress, + registerSlashCommands, + onMessageReceived, handleTableUpdate, + processPlotOptimization, + getContext, extension_settings, + characters, this_chid, eventSource, event_types, saveSettingsDebounced, + injectTableData, generateTableContent, + initializeRagProcessor, + loadTables, clearHighlights, rollbackAndRefill, rollbackState, commitPendingDeletions, saveStateToMessage, getMemoryState, clearUpdatedTables, + fillWithSecondaryApi, + renderTables, + log, + checkForUpdates, fetchMessageBoardContent, + setUpdateInfo, applyUpdateIndicator, + pluginVersion, extensionName, defaultSettings, + checkAuthorization, refreshUserInfo, + tableSystemDefaultSettings, + manageLorebookEntriesForChat, + initializeCharacterWorldBook, + cwbDefaultSettings, + bindGlossaryEvents, + updateOrInsertTableInChat, startContinuousRendering, stopContinuousRendering, + initializeRenderer, + initializeApiListener, registerApiHandler, amilyHelper, initializeAmilyHelper, + registerContextOptimizerMacros, resetContextBuffer, + initializeSuperMemory +} from './imports.js'; +import { initializeAmilyBus } from './SL/bus/Amily2Bus.js'; const DOMPURIFY_CDN = "https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.2.7/purify.min.js"; @@ -409,146 +406,506 @@ window.addEventListener("error", (event) => { }); -jQuery(async () => { - console.log("[Amily2号-帝国枢密院] 开始执行开国大典..."); +let isProcessingPlotOptimization = false; - // 启动外部库加载 (DOMPurify) - loadExternalScript(DOMPURIFY_CDN, 'DOMPurify').catch(e => console.warn("[Amily2] DOMPurify 加载失败,将使用内置净化器:", e)); - - // 【V146.2 紧急优化】优先注册上下文优化器,确保它在密折司之前拦截并处理 Prompt - try { - console.log("[Amily2号-开国大典] 步骤0:优先注册上下文优化器..."); - registerContextOptimizerMacros(); - } catch (e) { - console.error("[Amily2号-开国大典] 上下文优化器注册失败:", e); - } +/** + * 加载必要的外部库(如 DOMPurify)。 + * 如果加载失败,会回退到内置的简单净化器。 + */ +function loadExternalLibraries() { + loadExternalScript(DOMPURIFY_CDN, 'DOMPurify').catch(e => console.warn("[Amily2] DOMPurify 加载失败,将使用内置净化器:", e)); +} - // 【密折司】延迟加载,确保它排在优化器之后 - try { - await import("./MiZheSi/index.js"); - console.log("[Amily2号-开国大典] 密折司模块已就位。"); - } catch (e) { - console.error("[Amily2号-开国大典] 密折司加载失败:", e); - } - - initializeApiListener(); - - registerApiHandler('getChatMessages', async (data) => { - return amilyHelper.getChatMessages(data.range, data.options); - }); - - registerApiHandler('setChatMessages', async (data) => { - return await amilyHelper.setChatMessages(data.messages, data.options); - }); - - registerApiHandler('setChatMessage', async (data) => { - const field_values = data.field_values || data.content; - const message_id = data.message_id !== undefined ? data.message_id : data.index; - const options = data.options || {}; - - console.log('[Amily2-API] setChatMessage 收到参数:', { field_values, message_id, options, raw_data: data }); - - return await amilyHelper.setChatMessage(field_values, message_id, options); - }); - - registerApiHandler('createChatMessages', async (data) => { - return await amilyHelper.createChatMessages(data.messages, data.options); - }); - - registerApiHandler('deleteChatMessages', async (data) => { - return await amilyHelper.deleteChatMessages(data.ids, data.options); - }); - - registerApiHandler('getLorebooks', async (data) => { - return await amilyHelper.getLorebooks(); - }); - - registerApiHandler('getCharLorebooks', async (data) => { - return await amilyHelper.getCharLorebooks(data.options); - }); - - registerApiHandler('getLorebookEntries', async (data) => { - return await amilyHelper.getLorebookEntries(data.bookName); - }); - - registerApiHandler('setLorebookEntries', async (data) => { - return await amilyHelper.setLorebookEntries(data.bookName, data.entries); - }); - - registerApiHandler('createLorebookEntries', async (data) => { - return await amilyHelper.createLorebookEntries(data.bookName, data.entries); - }); - - registerApiHandler('createLorebook', async (data) => { - return await amilyHelper.createLorebook(data.bookName); - }); - - registerApiHandler('triggerSlash', async (data) => { - return await amilyHelper.triggerSlash(data.command); - }); - - registerApiHandler('getLastMessageId', async (data) => { - return amilyHelper.getLastMessageId(); - }); - - registerApiHandler('toastr', async (data) => { - if (window.toastr && typeof window.toastr[data.type] === 'function') { - window.toastr[data.type](data.message, data.title); - } - return true; - }); - - registerApiHandler('switchSwipe', async (data) => { - const { messageIndex, swipeIndex } = data; - const messages = await amilyHelper.getChatMessages(messageIndex, { include_swipes: true }); - - if (messages && messages.length > 0 && messages[0].swipes) { - const content = messages[0].swipes[swipeIndex]; - if (content !== undefined) { - await amilyHelper.setChatMessages([{ - message_id: messageIndex, - message: content - }], { refresh: 'affected' }); - - const context = getContext(); - if (context.chat[messageIndex]) { - context.chat[messageIndex].swipe_id = swipeIndex; - } - - return { success: true, message: `已切换至开场白 ${swipeIndex}` }; - } - } - - throw new Error(`无法切换到开场白 ${swipeIndex}`); - }); - - initializeAmilyHelper(); - - console.log("[Amily2号-帝国枢密院] 开始执行开国大典..."); - - if (!extension_settings[extensionName]) { - extension_settings[extensionName] = {}; - } - const combinedDefaultSettings = { ...defaultSettings, ...tableSystemDefaultSettings, ...cwbDefaultSettings, render_on_every_message: false, amily_render_enabled: false }; - - for (const key in combinedDefaultSettings) { - if (extension_settings[extensionName][key] === undefined) { - extension_settings[extensionName][key] = combinedDefaultSettings[key]; +/** + * 初始化上下文优化器模块。 + * 优先注册宏,确保其在其他处理之前生效。 + */ +function initializeContextOptimizer() { + try { + console.log("[Amily2号-开国大典] 步骤0:优先注册上下文优化器..."); + registerContextOptimizerMacros(); + } catch (e) { + console.error("[Amily2号-开国大典] 上下文优化器注册失败:", e); } - } - console.log("[Amily2号-帝国枢密院] 帝国基本法已确认,档案室已与国库对接完毕。"); +} - let attempts = 0; - const maxAttempts = 100; - const checkInterval = 100; - const targetSelector = "#sys-settings-button"; +/** + * 异步初始化“密折司”模块。 + * 该模块通常用于处理机密或特殊的后台逻辑。 + */ +async function initializeMiZheSi() { + try { + await import("./MiZheSi/index.js"); + console.log("[Amily2号-开国大典] 密折司模块已就位。"); + } catch (e) { + console.error("[Amily2号-开国大典] 密折司加载失败:", e); + } +} - const deploymentInterval = setInterval(async () => { - if ($(targetSelector).length > 0) { - clearInterval(deploymentInterval); - console.log("[Amily2号-帝国枢密院] SillyTavern宫殿主体已确认,开国大典正式开始!"); +/** + * 注册所有与 SillyTavern 交互的 API 处理器。 + * 包括消息获取、设置、删除,以及 Lorebook 管理等功能。 + */ +function registerAllApiHandlers() { + initializeApiListener(); - try { + registerApiHandler('getChatMessages', async (data) => amilyHelper.getChatMessages(data.range, data.options)); + registerApiHandler('setChatMessages', async (data) => amilyHelper.setChatMessages(data.messages, data.options)); + registerApiHandler('setChatMessage', async (data) => { + const field_values = data.field_values || data.content; + const message_id = data.message_id !== undefined ? data.message_id : data.index; + const options = data.options || {}; + console.log('[Amily2-API] setChatMessage 收到参数:', { field_values, message_id, options, raw_data: data }); + return await amilyHelper.setChatMessage(field_values, message_id, options); + }); + registerApiHandler('createChatMessages', async (data) => amilyHelper.createChatMessages(data.messages, data.options)); + registerApiHandler('deleteChatMessages', async (data) => amilyHelper.deleteChatMessages(data.ids, data.options)); + registerApiHandler('getLorebooks', async (data) => amilyHelper.getLorebooks()); + registerApiHandler('getCharLorebooks', async (data) => amilyHelper.getCharLorebooks(data.options)); + registerApiHandler('getLorebookEntries', async (data) => amilyHelper.getLorebookEntries(data.bookName)); + registerApiHandler('setLorebookEntries', async (data) => amilyHelper.setLorebookEntries(data.bookName, data.entries)); + registerApiHandler('createLorebookEntries', async (data) => amilyHelper.createLorebookEntries(data.bookName, data.entries)); + registerApiHandler('createLorebook', async (data) => amilyHelper.createLorebook(data.bookName)); + registerApiHandler('triggerSlash', async (data) => amilyHelper.triggerSlash(data.command)); + registerApiHandler('getLastMessageId', async (data) => amilyHelper.getLastMessageId()); + registerApiHandler('toastr', async (data) => { + if (window.toastr && typeof window.toastr[data.type] === 'function') { + window.toastr[data.type](data.message, data.title); + } + return true; + }); + registerApiHandler('switchSwipe', async (data) => { + const { messageIndex, swipeIndex } = data; + const messages = await amilyHelper.getChatMessages(messageIndex, { include_swipes: true }); + if (messages && messages.length > 0 && messages[0].swipes) { + const content = messages[0].swipes[swipeIndex]; + if (content !== undefined) { + await amilyHelper.setChatMessages([{ + message_id: messageIndex, + message: content + }], { refresh: 'affected' }); + const context = getContext(); + if (context.chat[messageIndex]) { + context.chat[messageIndex].swipe_id = swipeIndex; + } + return { success: true, message: `已切换至开场白 ${swipeIndex}` }; + } + } + throw new Error(`无法切换到开场白 ${swipeIndex}`); + }); +} + +/** + * 合并插件的默认设置与用户设置。 + * 确保即使在升级后,新增加的设置项也有默认值。 + */ +function mergePluginSettings() { + if (!extension_settings[extensionName]) { + extension_settings[extensionName] = {}; + } + const combinedDefaultSettings = { ...defaultSettings, ...tableSystemDefaultSettings, ...cwbDefaultSettings, render_on_every_message: false, amily_render_enabled: false }; + for (const key in combinedDefaultSettings) { + if (extension_settings[extensionName][key] === undefined) { + extension_settings[extensionName][key] = combinedDefaultSettings[key]; + } + } + console.log("[Amily2号-帝国枢密院] 帝国基本法已确认,档案室已与国库对接完毕。"); +} + +/** + * 等待术语表面板加载完毕并绑定事件。 + * 包含重试机制,防止面板尚未渲染导致绑定失败。 + */ +function waitForGlossaryPanelAndBindEvents() { + let attempts = 0; + const maxAttempts = 50; + const interval = 100; + + const checker = setInterval(() => { + const glossaryPanel = document.getElementById('amily2_glossary_panel'); + + if (glossaryPanel) { + clearInterval(checker); + try { + console.log("[Amily2号-开国大典] 步骤3.6:侦测到术语表停泊位,开始绑定事件..."); + bindGlossaryEvents(); + console.log("[Amily2号-开国大典] 术语表事件已成功绑定。"); + } catch (error) { + console.error("!!!【术语表事件绑定失败】:", error); + } + } else { + attempts++; + if (attempts >= maxAttempts) { + clearInterval(checker); + console.error("!!!【术语表事件绑定失败】: 等待面板 #amily2_glossary_panel 超时。"); + } + } + }, interval); +} + +/** + * 等待角色世界书面板加载完毕并进行初始化。 + * 包含重试机制。 + */ +function waitForCwbPanelAndInitialize() { + let attempts = 0; + const maxAttempts = 50; + const interval = 100; + + const checker = setInterval(async () => { + const $cwbPanel = $('#amily2_character_world_book_panel'); + + if ($cwbPanel.length > 0) { + clearInterval(checker); + try { + console.log("[Amily2号-开国大典] 步骤3.5:侦测到角色世界书停泊位,开始构建..."); + await initializeCharacterWorldBook($cwbPanel); + console.log("[Amily2号-开国大典] 角色世界书已成功构建并融入帝国。"); + } catch (error) { + console.error("!!!【角色世界书构建失败】:", error); + } + } else { + attempts++; + if (attempts >= maxAttempts) { + clearInterval(checker); + console.error("!!!【角色世界书构建失败】: 等待面板 #amily2_character_world_book_panel 超时。"); + } + } + }, interval); +} + +/** + * 注册用于表格内容的 SillyTavern 宏。 + * 允许在 Prompt 中使用 {{Amily2EditContent}} 来插入动态生成的表格数据。 + */ +function registerTableMacros() { + console.log("[Amily2号-开国大典] 步骤3.8:注册表格占位符宏..."); + try { + eventSource.on(event_types.GENERATION_STARTED, () => { + resetContextBuffer(); + if (isProcessingPlotOptimization) { + console.warn("[Amily2-剧情优化] 检测到生成开始,但优化标志位仍为 true。这可能是并发生成或状态未及时重置。"); + } + }); + + const context = getContext(); + if (context && typeof context.registerMacro === 'function') { + context.registerMacro('Amily2EditContent', () => { + const content = generateTableContent(); + if (content) { + window.AMILY2_MACRO_REPLACED = true; + } + return content; + }); + console.log('[Amily2-核心引擎] 已成功注册表格占位符宏: {{Amily2EditContent}}'); + } else { + console.warn('[Amily2-核心引擎] 无法注册表格宏,可能是 SillyTavern 版本不兼容。'); + } + } catch (error) { + console.error('[Amily2-核心引擎] 注册表格宏时发生错误:', error); + } +} + +/** + * 处理用户发送消息前的逻辑(剧情优化)。 + * 拦截消息发送,进行剧情梳理和总结,然后注入到 Prompt 中。 + * + * @param {string} type - 触发类型 (例如 'send') + * @param {object} params - 参数对象 + * @param {boolean} dryRun - 是否为试运行 + * @returns {Promise} - 返回 false 以阻止默认行为(如果已异步处理),或不做阻拦。 + */ +async function onPlotGenerationAfterCommands(type, params, dryRun) { + clearUpdatedTables(); + if (isProcessingPlotOptimization) { + console.log("[Amily2-剧情优化] 优化正在进行中,拦截重复触发。"); + return; + } + console.log("[Amily2-剧情优化] Generation after commands triggered", { type, params, dryRun }); + if (type === 'regenerate' || dryRun) { + console.log("[Amily2-剧情优化] Skipping due to regenerate or dryRun."); + return false; + } + const globalSettings = extension_settings[extensionName]; + if (globalSettings?.plotOpt_enabled === false) return false; + + const isJqyhEnabled = globalSettings?.jqyhEnabled === true; + const isMainApiConfigured = !!globalSettings?.apiUrl || !!globalSettings?.tavernProfile; + + if (!isJqyhEnabled && !isMainApiConfigured) { + console.log("[Amily2-剧情优化] 优化已启用,但Jqyh API已禁用且主页API未配置。"); + return false; + } + + let userMessage = $('#send_textarea').val(); + let isFromTextarea = true; + const context = getContext(); + if (!userMessage) { + if (context.chat && context.chat.length > 0) { + const lastMsg = context.chat[context.chat.length - 1]; + if (lastMsg.is_user) { + userMessage = lastMsg.mes; + isFromTextarea = false; + console.log("[Amily2-剧情优化] Detected empty textarea, processing last user message."); + } + } + } + if (!userMessage) return false; + + isProcessingPlotOptimization = true; + const cancellationState = { isCancelled: false }; + showPlotOptimizationProgress(cancellationState); + + const onProgress = (message, isDone = false, isSkipped = false) => { + updatePlotOptimizationProgress(message, isDone, isSkipped); + }; + + try { + const cancellationPromise = new Promise((_, reject) => { + const checkCancel = setInterval(() => { + if (cancellationState.isCancelled) { + clearInterval(checkCancel); + reject(new Error("Optimization cancelled by user")); + } + }, 100); + }); + + const contextTurnCount = globalSettings.plotOpt_contextLimit || 10; + const contextSource = isFromTextarea ? context.chat : context.chat.slice(0, -1); + const slicedContext = contextTurnCount > 0 ? contextSource.slice(-contextTurnCount) : contextSource; + + const optimizationPromise = processPlotOptimization({ mes: userMessage }, slicedContext, cancellationState, onProgress); + const result = await Promise.race([optimizationPromise, cancellationPromise]); + + if (cancellationState.isCancelled) throw new Error("Optimization cancelled by user"); + + if (result && result.contentToAppend) { + const finalMessage = userMessage + '\n' + result.contentToAppend; + if (params && typeof params === 'object') { + try { + if (params.prompt) params.prompt = finalMessage; + if (Array.isArray(params.messages)) { + const lastMsg = params.messages[params.messages.length - 1]; + if (lastMsg && lastMsg.role === 'user') { + lastMsg.content = finalMessage; + } + } + } catch (e) { + console.warn("[Amily2-剧情优化] 尝试修改 params 失败:", e); + } + } + if (isFromTextarea) { + $('#send_textarea').val(finalMessage).trigger('input'); + } else { + const targetMessageId = context.chat.length - 1; + await amilyHelper.setChatMessage(finalMessage, targetMessageId, { refresh: 'none' }); + } + toastr.success('剧情优化已完成并注入,继续生成...', '操作成功'); + isProcessingPlotOptimization = false; + hidePlotOptimizationProgress(); + return false; + } else { + console.log("[Amily2-剧情优化] Plot optimization returned no result. Sending original message."); + isProcessingPlotOptimization = false; + hidePlotOptimizationProgress(); + return false; + } + + } catch (error) { + if (cancellationState.isCancelled || error.message === "Optimization cancelled by user") { + console.log("[Amily2-剧情优化] 优化流程已被用户中止。发送原始消息。"); + toastr.warning('记忆管理任务已中止。', '操作取消', { timeOut: 2000 }); + } else { + console.error(`[Amily2-剧情优化] 处理发送前事件时出错:`, error); + toastr.error('记忆管理处理失败,将发送原始消息。', '错误'); + } + isProcessingPlotOptimization = false; + hidePlotOptimizationProgress(); + return false; + } +} + +/** + * 注册核心事件监听器。 + * 包含对消息接收、编辑、删除、滑动等事件的处理,以及剧情优化的触发。 + */ +function registerEventListeners() { + console.log("[Amily2号-开国大典] 步骤四:部署帝国哨兵网络..."); + if (!window.amily2EventsRegistered) { + eventSource.on(event_types.GENERATION_AFTER_COMMANDS, onPlotGenerationAfterCommands); + eventSource.on(event_types.MESSAGE_RECEIVED, onMessageReceived); + eventSource.on(event_types.IMPERSONATE_READY, onMessageReceived); + eventSource.on(event_types.MESSAGE_RECEIVED, (chat_id) => handleTableUpdate(chat_id)); + eventSource.on(event_types.MESSAGE_SWIPED, async (chat_id) => { + const context = getContext(); + if (context.chat.length < 2) { + log('【监察系统】检测到消息滑动,但聊天记录不足,已跳过状态回退。', 'info'); + return; + } + log('【监察系统】检测到消息滑动 (SWIPED),开始执行状态回退...', 'warn'); + rollbackState(); + const latestMessage = context.chat[chat_id] || context.chat[context.chat.length - 1]; + if (latestMessage.is_user) { + log('【监察系统】滑动后最新消息是用户,跳过填表。', 'info'); + renderTables(); + return; + } + const settings = extension_settings[extensionName]; + const fillingMode = settings.filling_mode || 'main-api'; + if (fillingMode === 'main-api') { + log(`【监察系统】主填表模式,回退后强制刷新消息ID: ${chat_id}。`, 'info'); + await handleTableUpdate(chat_id, true); + } else if (fillingMode === 'secondary-api' || fillingMode === 'optimized') { + log('【监察系统】分步/优化模式,回退后强制二次填表最新消息。', 'info'); + await fillWithSecondaryApi(latestMessage, true); + } else { + log('【监察系统】未配置填表模式,跳过填表。', 'info'); + } + renderTables(); + log('【监察系统】滑动后填表完成,UI 已刷新。', 'success'); + }); + eventSource.on(event_types.MESSAGE_EDITED, (mes_id) => { + handleTableUpdate(mes_id); + updateOrInsertTableInChat(); + }); + eventSource.on(event_types.CHAT_CHANGED, () => { + window.lastPreOptimizationResult = null; + document.dispatchEvent(new CustomEvent('preOptimizationTextUpdated')); + manageLorebookEntriesForChat(); + setTimeout(() => { + log("【监察系统】检测到“朝代更迭”(CHAT_CHANGED),开始重修史书并刷新宫殿...", 'info'); + clearHighlights(); + clearUpdatedTables(); + loadTables(); + renderTables(); + if (extension_settings[extensionName].render_on_every_message) { + startContinuousRendering(); + } else { + stopContinuousRendering(); + } + }, 100); + }); + eventSource.on(event_types.MESSAGE_DELETED, (message, index) => { + log(`【监察系统】检测到消息 ${index} 被删除,开始精确回滚UI状态。`, 'warn'); + clearHighlights(); + loadTables(index); + renderTables(); + }); + eventSource.on(event_types.MESSAGE_RECEIVED, updateOrInsertTableInChat); + eventSource.on(event_types.chat_updated, updateOrInsertTableInChat); + + window.amily2EventsRegistered = true; + } +} + +/** + * 执行 Amily2 的统一注入逻辑。 + * 同时兼容表格数据注入和 RAG 上下文重排。 + * @param {...any} args - 传递给 injectTableData 和 rearrangeChat 的参数 + */ +async function executeAmily2Injection(...args) { + console.log('[Amily2-核心引擎] 开始执行统一注入 (聊天长度:', args[0]?.length || 0, ')'); + try { + await injectTableData(...args); + } catch (error) { + console.error('[Amily2-内存储司] 表格注入失败:', error); + } + if (window.hanlinyuanRagProcessor && typeof window.hanlinyuanRagProcessor.rearrangeChat === 'function') { + try { + console.log('[Amily2-核心引擎] 执行内置RAG注入。'); + await window.hanlinyuanRagProcessor.rearrangeChat(...args); + } catch (error) { + console.error('[Amily2-翰林院] RAG注入失败:', error); + } + } +} + +/** + * 初始化 RAG 处理器并设置注入策略。 + * 覆盖 `vectors_rearrangeChat` 以确保 Amily2 的注入逻辑优先执行。 + */ +function initializeRagAndInjection() { + console.log("[Amily2号-开国大典] 步骤五:初始化RAG处理器..."); + try { + initializeRagProcessor(); + console.log('[Amily2-翰林院] RAG处理器已成功初始化'); + } catch (error) { + console.error('[Amily2-翰林院] RAG处理器初始化失败:', error); + } + + console.log("[Amily2号-开国大典] 步骤六:智能冲突检测与注入策略..."); + console.log('[Amily2-策略] 采用“完全主导”策略,覆盖 `vectors_rearrangeChat`。'); + window['vectors_rearrangeChat'] = executeAmily2Injection; + if (window['amily2HanlinyuanInjector']) { + window['amily2HanlinyuanInjector'] = null; + } +} + +/** + * 执行部署完成后的后续任务。 + * 包括:版本检查、在线人数统计、本地联动、超级记忆初始化、渲染器启动和主题应用。 + */ +function performPostDeploymentTasks() { + console.log("【Amily2号】帝国秩序已完美建立。Amily2号的府邸已恭候陛下的莅临。"); + if (checkAuthorization()) { + const userType = localStorage.getItem("plugin_user_type") || "未知"; + const userNote = localStorage.getItem("plugin_user_note"); + const displayNote = userNote || userType; + toastr.success(`欢迎回来!授权状态有效 (用户: ${displayNote})`, "Amily2 插件已就绪"); + refreshUserInfo().then(data => { + if (data && data.note && data.note !== userNote) { + console.log("[Amily2] 用户信息已更新:", data.note); + } + }).catch(e => console.warn("[Amily2] 后台刷新用户信息失败:", e)); + } + + console.log("[Amily2号-开国大典] 步骤七:初始化版本显示系统..."); + if (typeof window.amily2Updater !== 'undefined') { + setTimeout(() => { + console.log("[Amily2号-版本系统] 正在启动版本检测器..."); + window.amily2Updater.initialize(); + }, 2000); + } else { + console.warn("[Amily2号-版本系统] 版本检测器未找到,可能加载失败"); + } + + handleUpdateCheck(); + handleMessageBoard(); + initializeOnlineTracker(); + initializeLocalLinkage(); + + setTimeout(() => initializeSuperMemory(), 3000); + + initializeRenderer(); + + if (extension_settings[extensionName].render_on_every_message) { + startContinuousRendering(); + } + + setTimeout(() => { + try { + loadAndApplyStyles(); + const importThemeBtn = document.getElementById('amily2-import-theme-btn'); + const exportThemeBtn = document.getElementById('amily2-export-theme-btn'); + const resetThemeBtn = document.getElementById('amily2-reset-theme-btn'); + if (importThemeBtn) importThemeBtn.addEventListener('click', importStyles); + if (exportThemeBtn) exportThemeBtn.addEventListener('click', exportStyles); + if (resetThemeBtn) resetThemeBtn.addEventListener('click', resetToDefaultStyles); + log('【凤凰阁】内联主题系统已通过延迟加载成功初始化并绑定事件。', 'success'); + } catch (error) { + log(`【凤凰阁】内联主题系统初始化失败: ${error}`, 'error'); + } + }, 500); +} + +/** + * Amily2 核心部署流程(开国大典)。 + * 只有当 SillyTavern 基础 UI 加载完成后才会执行此函数。 + * 负责按顺序初始化插件的各个子系统。 + */ +async function runAmily2Deployment() { + console.log("[Amily2号-帝国枢密院] SillyTavern宫殿主体已确认,开国大典正式开始!"); + try { console.log("[Amily2号-开国大典] 步骤一:为宫殿披上华服..."); loadPluginStyles(); @@ -558,416 +915,48 @@ jQuery(async () => { console.log("[Amily2号-开国大典] 步骤三:开始召唤府邸..."); createDrawer(); - function waitForGlossaryPanelAndBindEvents() { - let attempts = 0; - const maxAttempts = 50; - const interval = 100; - - const checker = setInterval(() => { - const glossaryPanel = document.getElementById('amily2_glossary_panel'); - - if (glossaryPanel) { - clearInterval(checker); - try { - console.log("[Amily2号-开国大典] 步骤3.6:侦测到术语表停泊位,开始绑定事件..."); - bindGlossaryEvents(); - console.log("[Amily2号-开国大典] 术语表事件已成功绑定。"); - } catch (error) { - console.error("!!!【术语表事件绑定失败】:", error); - } - } else { - attempts++; - if (attempts >= maxAttempts) { - clearInterval(checker); - console.error("!!!【术语表事件绑定失败】: 等待面板 #amily2_glossary_panel 超时。"); - } - } - }, interval); - } waitForGlossaryPanelAndBindEvents(); - - function waitForCwbPanelAndInitialize() { - let attempts = 0; - const maxAttempts = 50; - const interval = 100; - - const checker = setInterval(async () => { - const $cwbPanel = $('#amily2_character_world_book_panel'); - - if ($cwbPanel.length > 0) { - clearInterval(checker); - try { - console.log("[Amily2号-开国大典] 步骤3.5:侦测到角色世界书停泊位,开始构建..."); - await initializeCharacterWorldBook($cwbPanel); - console.log("[Amily2号-开国大典] 角色世界书已成功构建并融入帝国。"); - } catch (error) { - console.error("!!!【角色世界书构建失败】:", error); - } - } else { - attempts++; - if (attempts >= maxAttempts) { - clearInterval(checker); - console.error("!!!【角色世界书构建失败】: 等待面板 #amily2_character_world_book_panel 超时。"); - } - } - }, interval); - } - waitForCwbPanelAndInitialize(); + registerTableMacros(); - console.log("[Amily2号-开国大典] 步骤3.8:注册表格占位符宏..."); - try { - // 【V144.0】注册上下文优化器宏 (已移至开国大典步骤0优先执行,此处仅保留重置逻辑) - // registerContextOptimizerMacros(); - - // 注册生成开始事件以重置缓冲区 - eventSource.on(event_types.GENERATION_STARTED, () => { - resetContextBuffer(); - - // 故障恢复:如果生成开始了,说明之前的优化肯定结束了(或者被绕过了),强制重置标志位 - if (isProcessingPlotOptimization) { - console.warn("[Amily2-剧情优化] 检测到生成开始,但优化标志位仍为 true。这可能是并发生成或状态未及时重置。"); - // 我们不在这里强制重置,因为优化可能正在进行中,我们希望它完成并修改输入框。 - } - }); + registerEventListeners(); + initializeRagAndInjection(); + performPostDeploymentTasks(); - const context = getContext(); - if (context && typeof context.registerMacro === 'function') { - context.registerMacro('Amily2EditContent', () => { - const content = generateTableContent(); - if (content) { - window.AMILY2_MACRO_REPLACED = true; - } - return content; - }); - console.log('[Amily2-核心引擎] 已成功注册表格占位符宏: {{Amily2EditContent}}'); - } else { - console.warn('[Amily2-核心引擎] 无法注册表格宏,可能是 SillyTavern 版本不兼容。'); - } - } catch (error) { - console.error('[Amily2-核心引擎] 注册表格宏时发生错误:', error); - } - - console.log("[Amily2号-开国大典] 步骤四:部署帝国哨兵网络..."); - - let isProcessingPlotOptimization = false; - - async function onPlotGenerationAfterCommands(type, params, dryRun) { - clearUpdatedTables(); - - // 如果正在处理中,拦截所有其他触发(防止意外的双重触发) - if (isProcessingPlotOptimization) { - console.log("[Amily2-剧情优化] 优化正在进行中,拦截重复触发。"); - return; - } - - console.log("[Amily2-剧情优化] Generation after commands triggered", { type, params, dryRun }); - - // Skip for regenerations or dry runs - if (type === 'regenerate' || dryRun) { - console.log("[Amily2-剧情优化] Skipping due to regenerate or dryRun."); - return false; - } - - const globalSettings = extension_settings[extensionName]; - if (globalSettings?.plotOpt_enabled === false) { - return false; - } - - const isJqyhEnabled = globalSettings?.jqyhEnabled === true; - const isMainApiConfigured = !!globalSettings?.apiUrl || !!globalSettings?.tavernProfile; - - if (!isJqyhEnabled && !isMainApiConfigured) { - console.log("[Amily2-剧情优化] 优化已启用,但Jqyh API已禁用且主页API未配置。"); - return false; - } - - // Determine the message to be processed - let userMessage = $('#send_textarea').val(); - let isFromTextarea = true; - const context = getContext(); - if (!userMessage) { - if (context.chat && context.chat.length > 0) { - const lastMsg = context.chat[context.chat.length - 1]; - if (lastMsg.is_user) { - userMessage = lastMsg.mes; - isFromTextarea = false; - console.log("[Amily2-剧情优化] Detected empty textarea, processing last user message."); - } - } - } - - if (!userMessage) { - return false; // Nothing to process - } - - // Set the flag to prevent loops and show progress - isProcessingPlotOptimization = true; - const cancellationState = { isCancelled: false }; - showPlotOptimizationProgress(cancellationState); - - const onProgress = (message, isDone = false, isSkipped = false) => { - updatePlotOptimizationProgress(message, isDone, isSkipped); - }; - - try { - const cancellationPromise = new Promise((_, reject) => { - const checkCancel = setInterval(() => { - if (cancellationState.isCancelled) { - clearInterval(checkCancel); - reject(new Error("Optimization cancelled by user")); - } - }, 100); - }); - - const contextTurnCount = globalSettings.plotOpt_contextLimit || 10; - const contextSource = isFromTextarea ? context.chat : context.chat.slice(0, -1); - const slicedContext = contextTurnCount > 0 ? contextSource.slice(-contextTurnCount) : contextSource; - - const optimizationPromise = processPlotOptimization({ mes: userMessage }, slicedContext, cancellationState, onProgress); - const result = await Promise.race([optimizationPromise, cancellationPromise]); - - if (cancellationState.isCancelled) { - throw new Error("Optimization cancelled by user"); - } - - if (result && result.contentToAppend) { - const finalMessage = userMessage + '\n' + result.contentToAppend; - - if (params && typeof params === 'object') { - try { - if (params.prompt) params.prompt = finalMessage; - if (Array.isArray(params.messages)) { - const lastMsg = params.messages[params.messages.length - 1]; - if (lastMsg && lastMsg.role === 'user') { - lastMsg.content = finalMessage; - } - } - } catch (e) { - console.warn("[Amily2-剧情优化] 尝试修改 params 失败:", e); - } - } - - if (isFromTextarea) { - $('#send_textarea').val(finalMessage).trigger('input'); - } else { - const targetMessageId = context.chat.length - 1; - await amilyHelper.setChatMessage(finalMessage, targetMessageId, { refresh: 'none' }); - } - - toastr.success('剧情优化已完成并注入,继续生成...', '操作成功'); - - isProcessingPlotOptimization = false; - hidePlotOptimizationProgress(); - - return false; - } else { - console.log("[Amily2-剧情优化] Plot optimization returned no result. Sending original message."); - isProcessingPlotOptimization = false; - hidePlotOptimizationProgress(); - return false; - } - - } catch (error) { - if (cancellationState.isCancelled || error.message === "Optimization cancelled by user") { - console.log("[Amily2-剧情优化] 优化流程已被用户中止。发送原始消息。"); - toastr.warning('记忆管理任务已中止。', '操作取消', { timeOut: 2000 }); - } else { - console.error(`[Amily2-剧情优化] 处理发送前事件时出错:`, error); - toastr.error('记忆管理处理失败,将发送原始消息。', '错误'); - } - isProcessingPlotOptimization = false; - hidePlotOptimizationProgress(); - return false; - } - } - if (!window.amily2EventsRegistered) { - eventSource.on(event_types.GENERATION_AFTER_COMMANDS, onPlotGenerationAfterCommands); - eventSource.on(event_types.MESSAGE_RECEIVED, onMessageReceived); - eventSource.on(event_types.IMPERSONATE_READY, onMessageReceived); - eventSource.on(event_types.MESSAGE_RECEIVED, (chat_id) => handleTableUpdate(chat_id)); - eventSource.on(event_types.MESSAGE_SWIPED, async (chat_id) => { - const context = getContext(); - if (context.chat.length < 2) { - log('【监察系统】检测到消息滑动,但聊天记录不足,已跳过状态回退。', 'info'); - return; - } - - log('【监察系统】检测到消息滑动 (SWIPED),开始执行状态回退...', 'warn'); - rollbackState(); - - const latestMessage = context.chat[chat_id] || context.chat[context.chat.length - 1]; - if (latestMessage.is_user) { - log('【监察系统】滑动后最新消息是用户,跳过填表。', 'info'); - renderTables(); - return; - } - - const settings = extension_settings[extensionName]; - const fillingMode = settings.filling_mode || 'main-api'; - - if (fillingMode === 'main-api') { - log(`【监察系统】主填表模式,回退后强制刷新消息ID: ${chat_id}。`, 'info'); - await handleTableUpdate(chat_id, true); - } else if (fillingMode === 'secondary-api' || fillingMode === 'optimized') { - log('【监察系统】分步/优化模式,回退后强制二次填表最新消息。', 'info'); - await fillWithSecondaryApi(latestMessage, true); - } else { - log('【监察系统】未配置填表模式,跳过填表。', 'info'); - } - - renderTables(); - log('【监察系统】滑动后填表完成,UI 已刷新。', 'success'); - }); - eventSource.on(event_types.MESSAGE_EDITED, (mes_id) => { - handleTableUpdate(mes_id); - updateOrInsertTableInChat(); - }); - - eventSource.on(event_types.CHAT_CHANGED, () => { - window.lastPreOptimizationResult = null; - document.dispatchEvent(new CustomEvent('preOptimizationTextUpdated')); - - manageLorebookEntriesForChat(); - setTimeout(() => { - log("【监察系统】检测到“朝代更迭”(CHAT_CHANGED),开始重修史书并刷新宫殿...", 'info'); - clearHighlights(); - clearUpdatedTables(); - loadTables(); - renderTables(); - - if (extension_settings[extensionName].render_on_every_message) { - startContinuousRendering(); - } else { - stopContinuousRendering(); - } - }, 100); - }); - - eventSource.on(event_types.MESSAGE_DELETED, (message, index) => { - log(`【监察系统】检测到消息 ${index} 被删除,开始精确回滚UI状态。`, 'warn'); - clearHighlights(); - loadTables(index); - renderTables(); - }); - - eventSource.on(event_types.MESSAGE_RECEIVED, updateOrInsertTableInChat); - eventSource.on(event_types.chat_updated, updateOrInsertTableInChat); - - window.amily2EventsRegistered = true; - } - - console.log("[Amily2号-开国大典] 步骤五:初始化RAG处理器..."); - - try { - initializeRagProcessor(); - console.log('[Amily2-翰林院] RAG处理器已成功初始化'); - } catch (error) { - console.error('[Amily2-翰林院] RAG处理器初始化失败:', error); - } - - console.log("[Amily2号-开国大典] 步骤六:智能冲突检测与注入策略..."); - - async function executeAmily2Injection(...args) { - console.log('[Amily2-核心引擎] 开始执行统一注入 (聊天长度:', args[0]?.length || 0, ')'); - - try { - await injectTableData(...args); - } catch (error) { - console.error('[Amily2-内存储司] 表格注入失败:', error); - } - - if (window.hanlinyuanRagProcessor && typeof window.hanlinyuanRagProcessor.rearrangeChat === 'function') { - try { - console.log('[Amily2-核心引擎] 执行内置RAG注入。'); - await window.hanlinyuanRagProcessor.rearrangeChat(...args); - } catch (error) { - console.error('[Amily2-翰林院] RAG注入失败:', error); - } - } - } - - console.log('[Amily2-策略] 采用“完全主导”策略,覆盖 `vectors_rearrangeChat`。'); - window['vectors_rearrangeChat'] = executeAmily2Injection; - - if (window['amily2HanlinyuanInjector']) { - window['amily2HanlinyuanInjector'] = null; - } - - console.log("【Amily2号】帝国秩序已完美建立。Amily2号的府邸已恭候陛下的莅临。"); - - if (checkAuthorization()) { - const userType = localStorage.getItem("plugin_user_type") || "未知"; - const userNote = localStorage.getItem("plugin_user_note"); - - const displayNote = userNote || userType; - toastr.success(`欢迎回来!授权状态有效 (用户: ${displayNote})`, "Amily2 插件已就绪"); - - refreshUserInfo().then(data => { - if (data && data.note && data.note !== userNote) { - console.log("[Amily2] 用户信息已更新:", data.note); - } - }).catch(e => { - console.warn("[Amily2] 后台刷新用户信息失败:", e); - }); - } - - console.log("[Amily2号-开国大典] 步骤七:初始化版本显示系统..."); - if (typeof window.amily2Updater !== 'undefined') { - setTimeout(() => { - console.log("[Amily2号-版本系统] 正在启动版本检测器..."); - window.amily2Updater.initialize(); - }, 2000); - } else { - console.warn("[Amily2号-版本系统] 版本检测器未找到,可能加载失败"); - } - - handleUpdateCheck(); - handleMessageBoard(); - initializeOnlineTracker(); // 【Amily2号-在线统计】启动在线人数统计 - initializeLocalLinkage(); // 【Amily2号-本地联动】启动本地联动服务 - - // 【V146.4】自动初始化超级记忆系统 - setTimeout(() => { - initializeSuperMemory(); - }, 3000); // 延迟3秒以确保 ST 环境完全就绪 - - initializeRenderer(); - - if (extension_settings[extensionName].render_on_every_message) { - startContinuousRendering(); - } - - setTimeout(() => { - try { - loadAndApplyStyles(); - - const importThemeBtn = document.getElementById('amily2-import-theme-btn'); - const exportThemeBtn = document.getElementById('amily2-export-theme-btn'); - const resetThemeBtn = document.getElementById('amily2-reset-theme-btn'); - - if (importThemeBtn) importThemeBtn.addEventListener('click', importStyles); - if (exportThemeBtn) exportThemeBtn.addEventListener('click', exportStyles); - if (resetThemeBtn) resetThemeBtn.addEventListener('click', resetToDefaultStyles); - - log('【凤凰阁】内联主题系统已通过延迟加载成功初始化并绑定事件。', 'success'); - } catch (error) { - log(`【凤凰阁】内联主题系统初始化失败: ${error}`, 'error'); - } - }, 500); - - } catch (error) { + } catch (error) { console.error("!!!【开国大典失败】在执行系列法令时发生严重错误:", error); - } - - } else { - attempts++; - if (attempts >= maxAttempts) { - clearInterval(deploymentInterval); - console.error(`[Amily2号] 部署失败:等待 ${targetSelector} 超时。`); - } } - }, checkInterval); +} + +jQuery(async () => { + console.log("[Amily2号-帝国枢密院] 开始执行开国大典..."); + + loadExternalLibraries(); + initializeContextOptimizer(); + await initializeMiZheSi(); + + registerAllApiHandlers(); + initializeAmilyHelper(); + mergePluginSettings(); + initializeAmilyBus(); + + let attempts = 0; + const maxAttempts = 100; + const checkInterval = 100; + const targetSelector = "#sys-settings-button"; + + const deploymentInterval = setInterval(async () => { + if ($(targetSelector).length > 0) { + clearInterval(deploymentInterval); + await runAmily2Deployment(); + } else { + attempts++; + if (attempts >= maxAttempts) { + clearInterval(deploymentInterval); + console.error(`[Amily2号] 部署失败:等待 ${targetSelector} 超时。`); + } + } + }, checkInterval); }); function applyMessageLimit() {