diff --git a/i18n/en-US.js b/i18n/en-US.js index bebbc79..9ba1233 100644 --- a/i18n/en-US.js +++ b/i18n/en-US.js @@ -43,8 +43,12 @@ export default { "panel.entry.floatingTooltip": "BME Memory Graph", "panel.entry.menuLabel": "Memory Graph", + "panel.entry.openPanelAction": "Open Panel", "panel.entry.openFailed": "Memory Graph panel failed to load. Check the console for details.", "panel.title": "ST-BME Memory Graph", + "panel.header.fabToggleTitle": "Show/Hide FAB", + "panel.header.themePickerTitle": "Switch Theme", + "panel.tab.graph": "Graph", "panel.tab.actions": "Actions", "panel.tab.cognition": "Cognition", "panel.tab.config": "Settings", @@ -59,6 +63,124 @@ export default { "panel.tab.trace": "Message Trace", "panel.tab.vector": "Vector", + "panel.sidebar.configKicker": "Config Workspace", + "panel.sidebar.configTitle": "ST-BME Settings", + "panel.sidebar.configHelp": "Switch config sections on the left; view the full settings form on the right.", + "panel.sidebar.taskKicker": "Task Monitor", + "panel.sidebar.taskTitle": "ST-BME Task Flow", + "panel.sidebar.taskHelp": "Switch monitoring views on the left; view real-time task status on the right.", + + "panel.configNav.api": "API Config", + "panel.configNav.toggles": "Feature Toggles", + "panel.configNav.advanced": "Advanced Params", + "panel.configNav.prompts": "Task Presets", + "panel.configNav.planner": "ENA Planner", + "panel.configNav.appearance": "Panel Appearance", + "panel.configNav.cleanup": "Data Cleanup", + + "panel.taskNav.pipeline": "Pipeline Overview", + "panel.taskNav.timeline": "Task Timeline", + "panel.taskNav.memory": "Memory Browser", + "panel.taskNav.injection": "Injection Preview", + "panel.taskNav.trace": "Message Trace", + "panel.taskNav.persistence": "Persistence", + + "panel.graphView.realtime": "Live Graph", + "panel.graphView.cognition": "Cognition View", + "panel.graphView.summary": "Summary View", + + "panel.graphToolbar.pauseTitle": "Pause graph rendering", + "panel.graphToolbar.zoomInTitle": "Zoom In", + "panel.graphToolbar.zoomOutTitle": "Zoom Out", + "panel.graphToolbar.resetTitle": "Reset", + + "panel.configWorkspace.kicker": "Config", + "panel.configWorkspace.title": "ST-BME Config Workspace", + "panel.configWorkspace.desc": "Configure the secondary memory model, feature toggles, fine-grained parameters, task presets, and panel theme in one place.", + + "panel.configPlaceholder.title": "Config switched to the right-side workspace", + "panel.configPlaceholder.help": "The left nav switches pages; the right pane shows the full config form.", + + "panel.configSection.apiKicker": "API Config", + "panel.configSection.apiTitle": "Model Connection & Vector Access", + "panel.configSection.apiDesc": "Manage independent memory LLM and Embedding connections, preserving existing backend proxies and direct fallback logic.", + "panel.configSection.togglesKicker": "Feature Toggles", + "panel.configSection.togglesTitle": "Main Pipeline & Enhanced Capabilities", + "panel.configSection.togglesDesc": "First decide which capabilities participate in the memory pipeline, then fine-tune each module's behavior in Advanced Params.", + "panel.configSection.advancedKicker": "Advanced Params", + "panel.configSection.advancedTitle": "Fine-grained Behavior & Scoring Strategy", + "panel.configSection.advancedDesc": "Carries over advanced items from the old settings page. If a feature is disabled, parameters are grayed out with a prompt to enable it in Feature Toggles.", + "panel.configSection.promptsKicker": "Task Presets", + "panel.configSection.promptsTitle": "Task Preset Workspace", + "panel.configSection.promptsDesc": "Maintain independent presets for each task — configure prompt assembly, generation parameters, and regex rules in one place.", + "panel.configSection.plannerKicker": "ENA Planner", + "panel.configSection.plannerTitle": "Plot Planning · LLM Integration", + "panel.configSection.plannerDesc": "Intercepts before sending and calls the planning LLM, collecting context from character cards, world books, BME memory, and history plots to generate plot and note tags appended to your input.", + "panel.configSection.appearanceKicker": "Panel Appearance", + "panel.configSection.appearanceTitle": "Theme & Visual Sync", + "panel.configSection.appearanceDesc": "Theme selection here stays in sync with the top palette shortcut and immediately refreshes the graph color scheme.", + "panel.configSection.cleanupKicker": "Data Cleanup", + "panel.configSection.cleanupTitle": "Graph, Cache & Storage Cleanup", + "panel.configSection.cleanupDesc": "Perform high-risk cleanup operations here. All operations require double confirmation; some are irreversible.", + + "panel.cognition.ownerList": "Character Cognition List", + "panel.cognition.spaceConsole": "Space Console", + + "panel.nodeDetail.title": "Node Details", + "panel.nodeDetail.delete": "Delete node", + "panel.nodeDetail.save": "Save changes", + "panel.memoryPopup.title": "Node Details", + + "panel.graphOverlay.loading": "Loading current chat graph", + + "panel.fullscreenGraph.title": "Live Graph (Fullscreen)", + "panel.fsToolbar.zoomInTitle": "Zoom In", + "panel.fsToolbar.zoomOutTitle": "Zoom Out", + "panel.fsToolbar.closeTitle": "Close", + + "panel.taskWorkspace.kicker": "Task Monitor", + "panel.taskWorkspace.title": "ST-BME Task Flow Workspace", + "panel.taskWorkspace.desc": "View the real-time running status and current batch progress of all task pipelines.", + + "panel.dashboard.activeNodes": "Active Nodes", + "panel.dashboard.edgeCount": "Edge Connections", + "panel.dashboard.archived": "Archived", + "panel.dashboard.fragRate": "Fragmentation", + "panel.dashboard.runStatus": "Runtime Status", + "panel.dashboard.cognitionSpace": "Cognition / Space", + "panel.dashboard.viewFullCognition": "View full cognition in Cognition View →", + "panel.dashboard.recentExtract": "Recent Extraction", + "panel.dashboard.recentRecall": "Recent Recall", + + "panel.actions.memoryGroup": "Memory Operations", + "panel.actions.memoryGroupSub": "Call LLM to process memory nodes", + "panel.actions.vectorGroup": "Vector Operations", + "panel.actions.vectorGroupSub": "Call Embedding API to compute node vectors", + "panel.actions.graphGroup": "Graph Management", + "panel.actions.graphGroupSub": "Import, export, and dangerous operations", + "panel.actions.persistGroup": "Persistence Repair", + "panel.actions.persistGroupSub": "Unified repair area for local persistence and Luker primary sidecar", + + "panel.mobileNav.pipeline": "Pipeline", + "panel.mobileNav.timeline": "Timeline", + "panel.mobileNav.memory": "Memory", + "panel.mobileNav.injection": "Injection", + "panel.mobileNav.trace": "Trace", + "panel.mobileNav.persistence": "Persistence", + + "theme.crimson": "Crimson Synth", + "theme.cyan": "Neon Cyan", + "theme.amber": "Amber Terminal", + "theme.violet": "Violet Haze", + "theme.paperDawn": "Paper Dawn", + "theme.glacierSky": "Glacier Sky", + "theme.crimson.desc": "High contrast, emphasizes change and alertness.", + "theme.cyan.desc": "A cooler, analytical feel — great for long debugging sessions.", + "theme.amber.desc": "Retro terminal vibe with stable reading hierarchy.", + "theme.violet.desc": "Softer nighttime texture for immersive browsing.", + "theme.paperDawn.desc": "Warm light paper base with teal accents — daytime friendly.", + "theme.glacierSky.desc": "Light cool gray with saturated blue — pairs well with system light mode.", + "persistence.loadState.error": "Load failed", "persistence.loadState.loaded": "Loaded", "persistence.loadState.loading": "Loading", diff --git a/i18n/zh-CN.js b/i18n/zh-CN.js index 6acc532..93c0828 100644 --- a/i18n/zh-CN.js +++ b/i18n/zh-CN.js @@ -43,8 +43,12 @@ export default { "panel.entry.floatingTooltip": "BME 记忆图谱", "panel.entry.menuLabel": "记忆图谱", + "panel.entry.openPanelAction": "打开面板", "panel.entry.openFailed": "记忆图谱面板加载失败,请查看控制台报错", "panel.title": "ST-BME 记忆图谱", + "panel.header.fabToggleTitle": "显示/隐藏悬浮球", + "panel.header.themePickerTitle": "切换主题", + "panel.tab.graph": "图谱", "panel.tab.actions": "操作", "panel.tab.cognition": "角色认知", "panel.tab.config": "设置", @@ -59,6 +63,124 @@ export default { "panel.tab.trace": "消息追踪", "panel.tab.vector": "向量 Vector", + "panel.sidebar.configKicker": "配置工作区", + "panel.sidebar.configTitle": "ST-BME 设置", + "panel.sidebar.configHelp": "左侧切换配置页,右侧查看对应的完整设置表单。", + "panel.sidebar.taskKicker": "任务监控", + "panel.sidebar.taskTitle": "ST-BME 任务流", + "panel.sidebar.taskHelp": "左侧切换监控视图,右侧查看实时任务状态。", + + "panel.configNav.api": "API 配置", + "panel.configNav.toggles": "功能开关", + "panel.configNav.advanced": "详细参数", + "panel.configNav.prompts": "任务预设", + "panel.configNav.planner": "ENA 规划器", + "panel.configNav.appearance": "面板外观", + "panel.configNav.cleanup": "数据清理", + + "panel.taskNav.pipeline": "管线总览", + "panel.taskNav.timeline": "任务流水", + "panel.taskNav.memory": "记忆浏览", + "panel.taskNav.injection": "注入预览", + "panel.taskNav.trace": "消息追踪", + "panel.taskNav.persistence": "持久化", + + "panel.graphView.realtime": "实时图谱", + "panel.graphView.cognition": "认知视图", + "panel.graphView.summary": "总结视图", + + "panel.graphToolbar.pauseTitle": "暂停图谱渲染", + "panel.graphToolbar.zoomInTitle": "放大", + "panel.graphToolbar.zoomOutTitle": "缩小", + "panel.graphToolbar.resetTitle": "重置", + + "panel.configWorkspace.kicker": "配置", + "panel.configWorkspace.title": "ST-BME 配置工作区", + "panel.configWorkspace.desc": "在这里集中配置第二记忆模型、功能开关、细粒度参数、任务预设和面板主题。", + + "panel.configPlaceholder.title": "配置已切换到右侧工作区", + "panel.configPlaceholder.help": "左侧子导航负责切页,右侧显示完整的配置表单。", + + "panel.configSection.apiKicker": "API 配置", + "panel.configSection.apiTitle": "模型连接与向量接入", + "panel.configSection.apiDesc": "统一管理独立记忆 LLM 和 Embedding 的连接方式,保留现有后端代理与直连兜底逻辑。", + "panel.configSection.togglesKicker": "功能开关", + "panel.configSection.togglesTitle": "主链路与增强能力", + "panel.configSection.togglesDesc": "先决定哪些能力参与记忆链路,再到详细参数里微调各模块行为。", + "panel.configSection.advancedKicker": "详细参数", + "panel.configSection.advancedTitle": "细粒度行为与评分策略", + "panel.configSection.advancedDesc": "这里承接旧设置页里的高级项。若对应功能关闭,参数会置灰并提示先到「功能开关」启用。", + "panel.configSection.promptsKicker": "任务预设", + "panel.configSection.promptsTitle": "任务预设工作区", + "panel.configSection.promptsDesc": "在这里为每个任务维护独立预设,统一配置 prompt 编排、生成参数和正则规则。", + "panel.configSection.plannerKicker": "ENA 规划器", + "panel.configSection.plannerTitle": "剧情规划 · LLM 接入", + "panel.configSection.plannerDesc": "发送前自动拦截并调用规划 LLM,从角色卡、世界书、BME 记忆、历史 plot 中收集上下文,生成 plot 和 note 追加到你的输入。", + "panel.configSection.appearanceKicker": "面板外观", + "panel.configSection.appearanceTitle": "主题与视觉同步", + "panel.configSection.appearanceDesc": "这里的主题选择会和顶部调色盘快捷入口保持同步,并立即刷新图谱配色。", + "panel.configSection.cleanupKicker": "数据清理", + "panel.configSection.cleanupTitle": "图谱、缓存与存储清理", + "panel.configSection.cleanupDesc": "在这里执行高危清理操作。所有操作均需二次确认,部分操作不可撤销。", + + "panel.cognition.ownerList": "角色认知清单", + "panel.cognition.spaceConsole": "空间控制台", + + "panel.nodeDetail.title": "节点详情", + "panel.nodeDetail.delete": "删除节点", + "panel.nodeDetail.save": "保存修改", + "panel.memoryPopup.title": "节点详情", + + "panel.graphOverlay.loading": "正在加载当前聊天图谱", + + "panel.fullscreenGraph.title": "实时图谱(全屏)", + "panel.fsToolbar.zoomInTitle": "放大", + "panel.fsToolbar.zoomOutTitle": "缩小", + "panel.fsToolbar.closeTitle": "关闭", + + "panel.taskWorkspace.kicker": "任务监控", + "panel.taskWorkspace.title": "ST-BME 任务流工作区", + "panel.taskWorkspace.desc": "实时查看所有任务管线的运行状态与当前批次进度。", + + "panel.dashboard.activeNodes": "活跃节点", + "panel.dashboard.edgeCount": "边连接", + "panel.dashboard.archived": "已归档", + "panel.dashboard.fragRate": "碎片率", + "panel.dashboard.runStatus": "运行状态", + "panel.dashboard.cognitionSpace": "认知 / 空间", + "panel.dashboard.viewFullCognition": "在认知视图中查看完整认知 →", + "panel.dashboard.recentExtract": "最近提取", + "panel.dashboard.recentRecall": "最近召回", + + "panel.actions.memoryGroup": "记忆操作", + "panel.actions.memoryGroupSub": "调用 LLM 处理记忆节点", + "panel.actions.vectorGroup": "向量操作", + "panel.actions.vectorGroupSub": "调用 Embedding API 计算节点向量", + "panel.actions.graphGroup": "图谱管理", + "panel.actions.graphGroupSub": "导入导出与危险操作", + "panel.actions.persistGroup": "持久化修复", + "panel.actions.persistGroupSub": "本地持久化与 Luker 主 sidecar 的统一修复区域", + + "panel.mobileNav.pipeline": "管线", + "panel.mobileNav.timeline": "流水", + "panel.mobileNav.memory": "记忆", + "panel.mobileNav.injection": "注入", + "panel.mobileNav.trace": "追踪", + "panel.mobileNav.persistence": "持久化", + + "theme.crimson": "赤红合成", + "theme.cyan": "霓虹青蓝", + "theme.amber": "琥珀终端", + "theme.violet": "紫雾迷离", + "theme.paperDawn": "晨光纸感", + "theme.glacierSky": "冰川晴空", + "theme.crimson.desc": "高对比、强调变化和警示感。", + "theme.cyan.desc": "更冷静的检视感,适合长时间排查。", + "theme.amber.desc": "更像复古终端,阅读层次稳定。", + "theme.violet.desc": "更柔和的夜间质感,适合沉浸式浏览。", + "theme.paperDawn.desc": "亮色暖纸面,青绿主色与琥珀强调,白天阅读友好。", + "theme.glacierSky.desc": "亮色冷灰底与饱和蓝,适合与系统浅色界面同屏。", + "persistence.loadState.error": "加载失败", "persistence.loadState.loaded": "已加载", "persistence.loadState.loading": "加载中", diff --git a/tests/i18n-catalog.mjs b/tests/i18n-catalog.mjs index b50595e..068f22d 100644 --- a/tests/i18n-catalog.mjs +++ b/tests/i18n-catalog.mjs @@ -1,4 +1,5 @@ import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; import zhCN from "../i18n/zh-CN.js"; import enUS from "../i18n/en-US.js"; @@ -44,4 +45,19 @@ assert.ok( "catalog should include at least one interpolated string", ); +const panelHtml = readFileSync(new URL("../ui/panel.html", import.meta.url), "utf8"); +const htmlKeys = new Set(); +const attrRe = /data-i18n(?:-[a-z-]+)?="([^"]+)"/g; +let match; +while ((match = attrRe.exec(panelHtml))) { + htmlKeys.add(match[1]); +} +assert.ok(htmlKeys.size > 0, "panel.html should contain data-i18n references after Phase 1"); +const missingHtmlKeys = [...htmlKeys].filter((key) => !Object.prototype.hasOwnProperty.call(zhCN, key)); +assert.deepEqual( + missingHtmlKeys, + [], + `panel.html references missing i18n keys: ${missingHtmlKeys.join(", ")}`, +); + console.log("i18n catalog tests passed"); diff --git a/ui/panel-bridge.js b/ui/panel-bridge.js index 8dc73b5..9d3dfda 100644 --- a/ui/panel-bridge.js +++ b/ui/panel-bridge.js @@ -1,4 +1,5 @@ import { debugLog } from "../runtime/debug-logging.js"; +import { setLocale, t } from "../i18n/index.js"; const MENU_ENTRY_RETRY_MS = 400; const MENU_ENTRY_MAX_ATTEMPTS = 30; @@ -9,10 +10,15 @@ function resolvePanelTheme(settings) { return settings?.panelTheme || "crimson"; } +function syncBridgeLocale(runtime) { + setLocale(runtime.getSettings?.()?.uiLocale || "auto"); +} + export function createNoticePanelActionController(runtime) { + syncBridgeLocale(runtime); if (!runtime.getPanelModule()?.openPanel) return undefined; return { - label: "打开面板", + label: t("panel.entry.openPanelAction"), kind: "neutral", onClick: () => { runtime.getPanelModule()?.openPanel?.(); @@ -37,20 +43,39 @@ function bindOpenPanelClick(runtime, element) { runtime.$?.("#extensionsMenu")?.hide?.(); } catch (error) { runtime.console.error("[ST-BME] 点击菜单打开面板失败:", error); - globalThis.toastr?.error?.("记忆图谱面板加载失败,请查看控制台报错", "ST-BME"); + globalThis.toastr?.error?.(t("panel.entry.openFailed"), "ST-BME"); } }); } +function renderOptionsMenuEntry(menuItem) { + menuItem.innerHTML = + `${t("panel.entry.menuLabel")}`; +} + +function renderExtensionsMenuEntry(menuItem) { + menuItem.innerHTML = + `
${t("panel.entry.menuLabel")}`; +} + +function renderFloatingBootstrap(fab) { + fab.innerHTML = ` + + ${t("panel.entry.floatingTooltip")} + `; +} + function injectOptionsMenuEntry(runtime) { + syncBridgeLocale(runtime); const doc = runtime.document; if (!doc || doc.getElementById(OPTIONS_MENU_ENTRY_ID)) { + const existing = doc?.getElementById(OPTIONS_MENU_ENTRY_ID); + if (existing) renderOptionsMenuEntry(existing); return true; } const menuItem = doc.createElement("a"); menuItem.id = OPTIONS_MENU_ENTRY_ID; - menuItem.innerHTML = - '记忆图谱'; + renderOptionsMenuEntry(menuItem); bindOpenPanelClick(runtime, menuItem); const anchor = doc.getElementById("option_toggle_logprobs"); @@ -68,6 +93,7 @@ function injectOptionsMenuEntry(runtime) { } function injectExtensionsMenuEntry(runtime) { + syncBridgeLocale(runtime); const doc = runtime.document; if (!doc) return false; @@ -75,6 +101,7 @@ function injectExtensionsMenuEntry(runtime) { const menu = doc.getElementById("extensionsMenu"); const button = doc.getElementById("extensionsMenuButton"); if (existing) { + renderExtensionsMenuEntry(existing); if (button?.style) button.style.display = "flex"; runtime.$?.("#extensionsMenuButton")?.css?.("display", "flex"); return true; @@ -84,8 +111,7 @@ function injectExtensionsMenuEntry(runtime) { const menuItem = doc.createElement("div"); menuItem.id = EXTENSIONS_MENU_ENTRY_ID; menuItem.className = "list-group-item flex-container flexGap5"; - menuItem.innerHTML = - '记忆图谱'; + renderExtensionsMenuEntry(menuItem); bindOpenPanelClick(runtime, menuItem); menu.appendChild(menuItem); @@ -98,6 +124,7 @@ function injectExtensionsMenuEntry(runtime) { } function injectFloatingBootstrap(runtime) { + syncBridgeLocale(runtime); const doc = runtime.document; if (!doc) return false; let fab = doc.getElementById("bme-floating-ball"); @@ -106,13 +133,15 @@ function injectFloatingBootstrap(runtime) { fab.id = "bme-floating-ball"; fab.setAttribute("data-status", "idle"); fab.setAttribute("data-bme-bootstrap", "true"); - fab.innerHTML = ` - - BME 记忆图谱 - `; + renderFloatingBootstrap(fab); const mountTarget = doc.body || doc.documentElement; if (!mountTarget) return false; mountTarget.appendChild(fab); + } else if (!fab.querySelector?.(".bme-fab-icon")) { + renderFloatingBootstrap(fab); + } else { + const tip = fab.querySelector?.(".bme-fab-tooltip"); + if (tip) tip.textContent = t("panel.entry.floatingTooltip"); } if (fab.dataset.bmeBridgeBound === "true") { return true; @@ -124,7 +153,7 @@ function injectFloatingBootstrap(runtime) { openPanelController(runtime); } catch (error) { runtime.console.error("[ST-BME] 点击悬浮球打开面板失败:", error); - globalThis.toastr?.error?.("记忆图谱面板加载失败,请查看控制台报错", "ST-BME"); + globalThis.toastr?.error?.(t("panel.entry.openFailed"), "ST-BME"); } }); return true; @@ -198,6 +227,13 @@ async function ensurePanelBridgeReady(runtime) { runtime.getThemesModule()?.applyTheme?.(nextTheme); runtime.getPanelModule()?.updatePanelTheme?.(nextTheme); } + if (Object.prototype.hasOwnProperty.call(patch || {}, "uiLocale")) { + syncBridgeLocale(runtime); + injectOptionsMenuEntry(runtime); + injectExtensionsMenuEntry(runtime); + injectFloatingBootstrap(runtime); + runtime.getPanelModule()?.updatePanelLocale?.(nextSettings.uiLocale || "auto"); + } return nextSettings; }, actions: runtime.actions, diff --git a/ui/panel.html b/ui/panel.html index b31f6fa..bb1c118 100644 --- a/ui/panel.html +++ b/ui/panel.html @@ -3,7 +3,7 @@