import { createDrawer } from "./ui/drawer.js"; import "./PresetSettings/index.js"; // 【预设设置】独立模块 import "./PreOptimizationViewer/index.js"; // 【优化前文查看器】独立模块 import "./WorldEditor/WorldEditor.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'; const DOMPURIFY_CDN = "https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.2.7/purify.min.js"; function loadExternalScript(url, globalName) { return new Promise((resolve, reject) => { if (window[globalName]) { resolve(window[globalName]); return; } const existingScript = document.querySelector(`script[src="${url}"]`); if (existingScript) { existingScript.addEventListener('load', () => resolve(window[globalName])); existingScript.addEventListener('error', reject); return; } const script = document.createElement('script'); script.src = url; script.async = true; script.onload = () => { console.log(`[Amily2-核心] 外部库加载成功: ${globalName}`); resolve(window[globalName]); }; script.onerror = (err) => { console.error(`[Amily2-核心] 外部库加载失败: ${globalName}`, err); reject(err); }; document.head.appendChild(script); }); } const STYLE_SETTINGS_KEY = 'amily2_custom_styles'; const STYLE_ROOT_SELECTOR = '#amily2_memorisation_forms_panel'; let styleRoot = null; function getStyleRoot() { if (!styleRoot) { styleRoot = document.querySelector(STYLE_ROOT_SELECTOR); } return styleRoot; } function applyStyles(styleObject) { const root = getStyleRoot(); if (!root || !styleObject) return; delete styleObject._comment; for (const [key, value] of Object.entries(styleObject)) { if (key.startsWith('--am2-')) { root.style.setProperty(key, value); } } } function loadAndApplyStyles() { const savedStyles = extension_settings[extensionName]?.[STYLE_SETTINGS_KEY]; if (savedStyles && typeof savedStyles === 'object' && Object.keys(savedStyles).length > 0) { applyStyles(savedStyles); } } function saveStyles(styleObject) { if (!extension_settings[extensionName]) { extension_settings[extensionName] = {}; } extension_settings[extensionName][STYLE_SETTINGS_KEY] = styleObject; saveSettingsDebounced(); } function resetToDefaultStyles() { const root = getStyleRoot(); if (!root) return; const savedStyles = extension_settings[extensionName]?.[STYLE_SETTINGS_KEY]; if (savedStyles && typeof savedStyles === 'object') { for (const key of Object.keys(savedStyles)) { if (key.startsWith('--am2-')) { root.style.removeProperty(key); } } } saveStyles(null); toastr.success('已恢复默认界面样式。'); } function getDefaultCssVars() { return { "--am2-font-size-base": "14px", "--am2-gap-main": "10px", "--am2-padding-main": "8px 5px", "--am2-container-bg": "rgba(0,0,0,0.1)", "--am2-container-border": "1px solid rgba(255, 255, 255, 0.2)", "--am2-container-border-radius": "12px", "--am2-container-padding": "10px", "--am2-container-shadow": "inset 0 0 15px rgba(0,0,0,0.2)", "--am2-title-font-size": "1.1em", "--am2-title-font-weight": "bold", "--am2-title-text-shadow": "0 0 5px rgba(200, 200, 255, 0.3)", "--am2-title-gradient-start": "#c0bde4", "--am2-title-gradient-end": "#dfdff0", "--am2-title-icon-color": "#9e8aff", "--am2-title-icon-margin": "10px", "--am2-table-bg": "rgba(0,0,0,0.2)", "--am2-table-border": "1px solid rgba(255, 255, 255, 0.25)", "--am2-table-cell-padding": "6px 8px", "--am2-table-cell-font-size": "0.95em", "--am2-header-bg": "rgba(255, 255, 255, 0.1)", "--am2-header-color": "#e0e0e0", "--am2-header-editable-bg": "rgba(172, 216, 255, 0.1)", "--am2-header-editable-focus-bg": "rgba(172, 216, 255, 0.25)", "--am2-header-editable-focus-outline": "1px solid #79b8ff", "--am2-cell-editable-bg": "rgba(255, 255, 172, 0.1)", "--am2-cell-editable-focus-bg": "rgba(255, 255, 172, 0.25)", "--am2-cell-editable-focus-outline": "1px solid #ffc107", "--am2-index-col-bg": "rgba(0, 0, 0, 0.3) !important", "--am2-index-col-color": "#aaa !important", "--am2-index-col-width": "40px", "--am2-index-col-padding": "10px 5px !important", "--am2-controls-gap": "5px", "--am2-controls-margin-bottom": "10px", "--am2-cell-highlight-bg": "rgba(144, 238, 144, 0.3)" }; } function exportStyles() { const root = getStyleRoot(); if (!root) { toastr.error('无法导出样式:找不到根元素。'); return; } const computedStyle = getComputedStyle(root); const stylesToExport = {}; const defaultVars = getDefaultCssVars(); for (const key of Object.keys(defaultVars)) { stylesToExport[key] = computedStyle.getPropertyValue(key).trim(); } const blob = new Blob([JSON.stringify(stylesToExport, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `Amily2-Theme-${new Date().toISOString().slice(0, 10)}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); toastr.success('主题文件已开始下载。', '导出成功'); } function importStyles() { const input = document.createElement('input'); input.type = 'file'; input.accept = '.json'; input.style.display = 'none'; const cleanup = () => { if (document.body.contains(input)) { document.body.removeChild(input); } }; input.onchange = e => { const file = e.target.files[0]; if (!file) { cleanup(); return; } const reader = new FileReader(); reader.onload = event => { try { const importedStyles = JSON.parse(event.target.result); if (typeof importedStyles !== 'object' || Array.isArray(importedStyles)) { throw new Error('无效的JSON格式。'); } applyStyles(importedStyles); saveStyles(importedStyles); toastr.success('主题已成功导入并应用!'); } catch (error) { toastr.error(`导入失败:${error.message}`, '错误'); } finally { cleanup(); } }; reader.readAsText(file); }; document.body.appendChild(input); input.click(); } function compareVersions(v1, v2) { const parts1 = v1.split('.').map(Number); const parts2 = v2.split('.').map(Number); const len = Math.max(parts1.length, parts2.length); for (let i = 0; i < len; i++) { const p1 = parts1[i] || 0; const p2 = parts2[i] || 0; if (p1 > p2) return true; if (p1 < p2) return false; } return false; } async function handleUpdateCheck() { console.log("【Amily2号】帝国已就绪,现派遣外交官,为陛下探查外界新情报..."); const updateInfo = await checkForUpdates(); if (updateInfo && updateInfo.version) { const isNew = compareVersions(updateInfo.version, pluginVersion); if(isNew) { console.log(`【Amily2号-情报部】捷报!发现新版本: ${updateInfo.version}。情报已转交内务府。`); } else { console.log(`【Amily2号-情报部】一切安好,帝国已是最新版本。情报已转交内务府备案。`); } setUpdateInfo(isNew, updateInfo); applyUpdateIndicator(); } } function sanitizeHTML(html) { if (window.DOMPurify) { return window.DOMPurify.sanitize(html, { ALLOWED_TAGS: ['b', 'i', 'u', 'em', 'strong', 'a', 'p', 'br', 'span', 'div', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'font', 'blockquote', 'code', 'pre', 'hr', 'img', 'table', 'thead', 'tbody', 'tr', 'th', 'td'], ALLOWED_ATTR: ['href', 'target', 'style', 'class', 'color', 'size', 'src', 'alt', 'title', 'width', 'height', 'align'], FORBID_TAGS: ['script', 'style', 'iframe', 'frame', 'object', 'embed', 'form', 'input', 'textarea', 'button', 'select', 'option'], FORBID_ATTR: ['onerror', 'onload', 'onclick', 'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup', 'ondblclick', 'onkeydown', 'onkeypress', 'onkeyup', 'onfocus', 'onblur', 'onchange', 'onsubmit', 'onreset', 'onselect', 'oncontextmenu'], ADD_ATTR: ['target'], }); } const tempDiv = document.createElement('div'); tempDiv.innerHTML = html; const allowedTags = ['b', 'i', 'u', 'em', 'strong', 'a', 'p', 'br', 'span', 'div', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'font']; const allowedAttrs = ['href', 'target', 'style', 'class', 'color', 'size']; const elements = tempDiv.querySelectorAll('*'); const allElements = tempDiv.getElementsByTagName('*'); for (let i = allElements.length - 1; i >= 0; i--) { const el = allElements[i]; const tagName = el.tagName.toLowerCase(); if (!allowedTags.includes(tagName)) { el.parentNode.removeChild(el); continue; } const attrs = Array.from(el.attributes); for (const attr of attrs) { const attrName = attr.name.toLowerCase(); if (!allowedAttrs.includes(attrName)) { el.removeAttribute(attr.name); } else if (attrName === 'href') { if (attr.value.toLowerCase().trim().startsWith('javascript:')) { el.removeAttribute('href'); } } else if (attrName.startsWith('on')) { el.removeAttribute(attr.name); } } } return tempDiv.innerHTML; } async function handleMessageBoard() { const updateMessage = async () => { try { const messageData = await fetchMessageBoardContent(); if (messageData && messageData.message) { const messageBoard = $('#amily2_message_board'); const messageContent = $('#amily2_message_content'); const safeContent = sanitizeHTML(messageData.message); messageContent.html(safeContent); messageBoard.show(); console.log("【Amily2号-内务府】已成功获取并展示来自陛下的最新圣谕。"); } } catch (error) { console.error("【Amily2号-内务府】获取留言板失败:", error); } }; await updateMessage(); setInterval(updateMessage, 300000); } function loadPluginStyles() { const loadStyleFile = (fileName) => { const styleId = `amily2-style-${fileName.split('.')[0]}`; if (document.getElementById(styleId)) return; const extensionPath = `scripts/extensions/third-party/${extensionName}/assets/${fileName}?v=${Date.now()}`; const link = document.createElement("link"); link.id = styleId; link.rel = "stylesheet"; link.type = "text/css"; link.href = extensionPath; document.head.appendChild(link); console.log(`[Amily2号-皇家制衣局] 已为帝国披上华服: ${fileName}`); }; // 颁布三道制衣圣谕 loadStyleFile("style.css"); // 【第一道圣谕】为帝国主体宫殿披上通用华服 loadStyleFile("historiography.css"); // 【第二道圣谕】为敕史局披上其专属华服 loadStyleFile("hanlinyuan.css"); // 【第三道圣谕】为翰林院披上其专属华服 loadStyleFile("amily2-glossary.css"); // 【新圣谕】为术语表披上其专属华服 loadStyleFile("table.css"); // 【第四道圣谕】为内存储司披上其专属华服 loadStyleFile("optimization.css"); // 【第五道圣谕】为剧情优化披上其专属华服 loadStyleFile("renderer.css"); // 【新圣谕】为渲染器披上其专属华服 loadStyleFile("iframe-renderer.css"); // 【新圣谕】为iframe渲染内容披上其专属华服 loadStyleFile("super-memory.css"); // 【新圣谕】为超级记忆披上其专属华服 // 【第六道圣谕】为角色世界书披上其专属华服 const cwbStyleId = 'cwb-feature-style'; if (!document.getElementById(cwbStyleId)) { const cwbLink = document.createElement("link"); cwbLink.id = cwbStyleId; cwbLink.rel = "stylesheet"; cwbLink.type = "text/css"; cwbLink.href = `scripts/extensions/third-party/${extensionName}/CharacterWorldBook/cwb_style.css?v=${Date.now()}`; document.head.appendChild(cwbLink); console.log(`[Amily2号-皇家制衣局] 已为角色世界书披上华服: cwb_style.css`); } // 【第七道圣谕】为世界编辑器披上其专属华服 const worldEditorStyleId = 'world-editor-style'; if (!document.getElementById(worldEditorStyleId)) { const worldEditorLink = document.createElement("link"); worldEditorLink.id = worldEditorStyleId; worldEditorLink.rel = "stylesheet"; worldEditorLink.type = "text/css"; worldEditorLink.href = `scripts/extensions/third-party/${extensionName}/WorldEditor/WorldEditor.css?v=${Date.now()}`; document.head.appendChild(worldEditorLink); console.log(`[Amily2号-皇家制衣局] 已为世界编辑器披上华服: WorldEditor.css`); } } window.addEventListener('message', function (event) { if (event.data && event.data.type === 'getAvatars') { if (window.isXiaobaixEnabled) { return; } const userAvatar = `/characters/${getContext().userCharacter?.avatar ?? ''}`; const charAvatar = `/characters/${getContext().characters[this_chid]?.avatar ?? ''}`; event.source.postMessage({ source: 'amily2-host', type: 'avatars', urls: { user: userAvatar, char: charAvatar } }, '*'); return; } if (event.data && event.data.source === 'amily2-iframe') { const { action, detail } = event.data; console.log(`[Amily2-主窗口] 收到来自iframe的动作: ${action}`, detail); switch (action) { case 'sendMessage': if (detail && detail.message) { $('#send_textarea').val(detail.message).trigger('input'); $('#send_but').trigger('click'); console.log(`[Amily2-主窗口] 已发送消息: ${detail.message}`); } break; case 'showToast': if (detail && detail.message && window.toastr) { const toastType = detail.type || 'info'; if (typeof window.toastr[toastType] === 'function') { window.toastr[toastType](detail.message, detail.title || '通知'); } } break; case 'buttonClick': console.log(`[Amily2-主窗口] 按钮被点击:`, detail); if (window.toastr) { window.toastr.info(`按钮 "${detail.buttonId || '未知'}" 被点击`, 'iframe交互'); } break; default: console.warn(`[Amily2-主窗口] 未知的动作类型: ${action}`); } } }); window.addEventListener("error", (event) => { const stackTrace = event.error?.stack || ""; if (stackTrace.includes("ST-Amily2-Chat-Optimisation")) { console.error("[Amily2-全局卫队] 捕获到严重错误:", event.error); toastr.error(`Amily2插件错误: ${event.error?.message || "未知错误"}`, "严重错误", { timeOut: 10000 }); } }); jQuery(async () => { console.log("[Amily2号-帝国枢密院] 开始执行开国大典..."); loadExternalScript(DOMPURIFY_CDN, 'DOMPurify').catch(e => console.warn("[Amily2] DOMPurify 加载失败,将使用内置净化器:", e)); try { console.log("[Amily2号-开国大典] 步骤0:优先注册上下文优化器..."); registerContextOptimizerMacros(); } catch (e) { console.error("[Amily2号-开国大典] 上下文优化器注册失败:", 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]; } } console.log("[Amily2号-帝国枢密院] 帝国基本法已确认,档案室已与国库对接完毕。"); let attempts = 0; const maxAttempts = 100; const checkInterval = 100; const targetSelector = "#sys-settings-button"; const deploymentInterval = setInterval(async () => { if ($(targetSelector).length > 0) { clearInterval(deploymentInterval); console.log("[Amily2号-帝国枢密院] SillyTavern宫殿主体已确认,开国大典正式开始!"); try { console.log("[Amily2号-开国大典] 步骤一:为宫殿披上华服..."); loadPluginStyles(); console.log("[Amily2号-开国大典] 步骤二:皇家仪仗队就位..."); await registerSlashCommands(); 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(); console.log("[Amily2号-开国大典] 步骤3.8:注册表格占位符宏..."); try { eventSource.on(event_types.GENERATION_STARTED, () => { resetContextBuffer(); }); 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(); console.log("[Amily2-剧情优化] Generation after commands triggered", { type, params, dryRun, isProcessing: isProcessingPlotOptimization }); if (type === 'regenerate' || isProcessingPlotOptimization || dryRun) { console.log("[Amily2-剧情优化] Skipping due to conditions:", { type, isProcessing: isProcessingPlotOptimization, dryRun }); return; } const globalSettings = extension_settings[extensionName]; if (globalSettings?.plotOpt_enabled === false) { return; } const isJqyhEnabled = globalSettings?.jqyhEnabled === true; const isMainApiConfigured = !!globalSettings?.apiUrl || !!globalSettings?.tavernProfile; if (!isJqyhEnabled && !isMainApiConfigured) { console.log("[Amily2-剧情优化] 优化已启用,但Jqyh API已禁用且主页API未配置。"); return; } isProcessingPlotOptimization = true; let plotOptimizationToast = null; const cancellationState = { isCancelled: false }; try { let userMessage = $('#send_textarea').val(); let isFromTextarea = true; let targetMessageId = null; 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; targetMessageId = context.chat.length - 1; console.log("[Amily2-剧情优化] 检测到输入框为空,但最后一条消息为用户发送,将对其进行优化。"); } } } if (!userMessage) { isProcessingPlotOptimization = false; return false; } const toastMessage = `