Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 95 additions & 84 deletions emrg/gui/renderer/index.html

Large diffs are not rendered by default.

22 changes: 18 additions & 4 deletions emrg/gui/renderer/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const App = (() => {
updateConnectionDot(init.config_exists && init.api_key_configured ? "green" : "gray");
updateModelSwitcher();
updateGrowthCard();
if (window.EMRG_I18N) window.EMRG_I18N.apply(); // rant 21:19:应用当前语言静态文案

if (!init.config_exists) {
Dialogs.showWelcome(); // 首启引导
Expand Down Expand Up @@ -517,7 +518,7 @@ const App = (() => {
Object.entries(Commands.COMMANDS).forEach(([cmd, meta]) => {
const row = el("div", { class: "help-row" });
const name = el("span", { class: "help-cmd" }, cmd);
const hint = el("span", { class: "help-hint" }, meta.hint);
const hint = el("span", { class: "help-hint" }, Commands.hintText(cmd)); // rant 21:19:hint 经 i18n 解析
row.appendChild(name);
row.appendChild(hint);
list.appendChild(row);
Expand Down Expand Up @@ -670,7 +671,17 @@ const App = (() => {
// ── 模型切换器 ─────────────────────────
async function updateModelSwitcher() {
const label = $("model-switcher-label");
if (label) label.textContent = state.model || "选择模型";
if (label) label.textContent = state.model || (window.EMRG_I18N ? window.EMRG_I18N.t("composer.modelLoading") : "加载中…");
}

/** rant 21:19:locale 切换后重刷动态文案(i18n.apply 回调) */
function refreshLocale() {
updateModelSwitcher();
// 空状态示例卡片:data-i18n 已由 apply 重刷;若补全菜单打开则重建(hint 本地化)
const menu = $("cmd-menu");
if (menu && !menu.hidden) {
showCmdMenu(($("input").value || "").trim());
}
}

// ── 自进化可见化(WorkBuddy P3)──────────────────
Expand Down Expand Up @@ -1021,9 +1032,10 @@ const App = (() => {
$("stop-btn").addEventListener("click", () => window.emrg.cancel().catch(() => {}));
$("new-chat-btn").addEventListener("click", newSession);
$("settings-btn").addEventListener("click", () => {
loadEvolutionSummary(); // WorkBuddy P3:打开设置时加载最近改进
loadEvolutionSummary(); // WorkBuddy P3(#502):打开设置时加载最近改进
Dialogs.showSettings();
});
Dialogs.initLangButtons(); // rant 21:19:设置语言选择器
$("settings-cancel").addEventListener("click", () => $("settings-dialog").close());
$("settings-save").addEventListener("click", Dialogs.saveSettings);
$("pick-dir-btn").addEventListener("click", async () => {
Expand Down Expand Up @@ -1129,7 +1141,8 @@ const App = (() => {
$("empty-state").addEventListener("click", (e) => {
const card = e.target.closest(".example-card");
if (!card) return;
input.value = card.dataset.example || "";
// rant 21:19:示例提示随语言走(textContent 已本地化;data-example 为中文兜底)
input.value = (card.textContent || card.dataset.example || "").trim();
input.focus();
input.style.height = "auto";
input.style.height = Math.min(input.scrollHeight, 150) + "px";
Expand Down Expand Up @@ -1193,6 +1206,7 @@ const App = (() => {
updateGrowthCard, // WorkBuddy P3:成长卡(导出供测试)
maybeShowEvolutionToast, // WorkBuddy P3:进化 toast 检测
showVersionInfo, // WorkBuddy P3:/version 内容(toast "去看看" 共用)
refreshLocale, // rant 21:19:locale 切换后动态文案重刷
setMode, // WorkBuddy P2:Ask/Auto 模式(导出供测试与外部调用)
loadEvolutionSummary, // WorkBuddy P3:最近改进摘要
};
Expand Down
44 changes: 27 additions & 17 deletions emrg/gui/renderer/js/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,23 @@

const Commands = (() => {
// 指令注册表:cmd → { hint, phase }
// hint 为 i18n 词典键(rant 21:19):显示时经 t() 解析为当前语言文案
const COMMANDS = {
"/clear": { hint: "清空当前对话", phase: 1 },
"/compact": { hint: "压缩当前对话历史", phase: 1 },
"/version": { hint: "显示版本与实例信息", phase: 1 },
"/help": { hint: "查看全部指令说明", phase: 1 },
"/image": { hint: "发送图片(请直接粘贴)", phase: 1 },
"/delete": { hint: "删除当前对话", phase: 2 },
"/rename": { hint: "重命名当前对话", phase: 2 },
"/resume": { hint: "切换/恢复对话", phase: 2 },
"/rewind": { hint: "回退到历史消息点", phase: 2 },
"/sessions": { hint: "查看全部对话", phase: 2 },
"/model": { hint: "切换模型", phase: 3 },
"/memory": { hint: "浏览记忆", phase: 3 },
"/skills": { hint: "查看已加载技能", phase: 3 },
"/rant": { hint: "驱动 EMRG 进化", phase: 4 },
"/trigger": { hint: "触发后台任务", phase: 4 },
"/clear": { hint: "cmd.clear.hint", phase: 1 },
"/compact": { hint: "cmd.compact.hint", phase: 1 },
"/version": { hint: "cmd.version.hint", phase: 1 },
"/help": { hint: "cmd.help.hint", phase: 1 },
"/image": { hint: "cmd.image.hint", phase: 1 },
"/delete": { hint: "cmd.delete.hint", phase: 2 },
"/rename": { hint: "cmd.rename.hint", phase: 2 },
"/resume": { hint: "cmd.resume.hint", phase: 2 },
"/rewind": { hint: "cmd.rewind.hint", phase: 2 },
"/sessions": { hint: "cmd.sessions.hint", phase: 2 },
"/model": { hint: "cmd.model.hint", phase: 3 },
"/memory": { hint: "cmd.memory.hint", phase: 3 },
"/skills": { hint: "cmd.skills.hint", phase: 3 },
"/rant": { hint: "cmd.rant.hint", phase: 4 },
"/trigger": { hint: "cmd.trigger.hint", phase: 4 },
};

/**
Expand All @@ -43,15 +44,24 @@ const Commands = (() => {
return { type: "unknown", cmd: key };
}

/** 解析 i18n hint:词典键 → 当前语言文案 */
function hintText(cmd) {
const meta = COMMANDS[cmd];
if (!meta) return "";
try {
return window.EMRG_I18N ? window.EMRG_I18N.t(meta.hint) : meta.hint;
} catch { return meta.hint; }
}

/** / 前缀补全:返回匹配的指令(含 hint),供补全菜单过滤 */
function getCompletions(prefix) {
const p = String(prefix || "").toLowerCase();
return Object.entries(COMMANDS)
.filter(([cmd]) => cmd.startsWith(p))
.map(([cmd, meta]) => ({ cmd, hint: meta.hint, phase: meta.phase }));
.map(([cmd, meta]) => ({ cmd, hint: hintText(cmd), phase: meta.phase }));
}

return { COMMANDS, parseInput, getCompletions };
return { COMMANDS, parseInput, getCompletions, hintText };
})();

window.EMRG_Commands = Commands;
87 changes: 53 additions & 34 deletions emrg/gui/renderer/js/copywriting.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,62 @@
"use strict";
/**
* copywriting.js — 去黑话文案映射(非开发者友好)。
* copywriting.js — 去黑话文案映射(非开发者友好,rant 21:19 起经 i18n 词典取词)。
* 工具名 → 友好的动词短语;系统级状态 → 口语化鼓励性文案。
* refresh():locale 切换后重建 COPY(i18n.apply 会调用)。
* 注意:EMRG_Copy 需在 i18n.js 之后加载(引用 EMRG_I18N.t)。
*/

/** 工具名 → 进行中 / 完成 短语(渐进披露:默认折叠,点开展示原始输出) */
const TOOL_PHRASES = {
bash: { doing: "正在运行命令…", done: "已运行命令" },
read: { doing: "正在读取文件…", done: "已读取文件" },
write: { doing: "正在写入文件…", done: "已写入文件" },
edit: { doing: "正在修改文件…", done: "已修改文件" },
glob: { doing: "正在查找文件…", done: "已找到文件" },
grep: { doing: "正在搜索内容…", done: "已完成搜索" },
};
const EMRG_Copy = (() => {
const _t = (key, params) => {
try {
return window.EMRG_I18N ? window.EMRG_I18N.t(key, params) : key;
} catch { return key; }
};

function toolPhrases(name) {
return TOOL_PHRASES[name] || { doing: "正在处理…", done: "已完成" };
}
/** 工具名 → 进行中 / 完成 短语(渐进披露:默认折叠,点开展示原始输出) */
function toolPhrases(name) {
const base = name || "fallback";
return {
doing: _t(`tool.${base}.doing`),
done: _t(`tool.${base}.done`),
};
}

/** 工具失败(不 blame 用户,给出下一步) */
const TOOL_FAIL_TEXT = "这一步没成功,我换个方法试试";
/** 系统状态文案(locale 敏感:切换语言后 refresh 重建;含 #501 P3 成长卡/toast 键) */
function buildCopy() {
return {
disconnected: _t("copy.disconnected"),
reconnected: _t("copy.reconnected"),
sessionBusy: _t("copy.sessionBusy"),
sendFailed: _t("copy.sendFailed"),
deleteConfirmTitle: _t("copy.deleteConfirmTitle"),
deleteConfirmBody: _t("copy.deleteConfirmBody"),
noSessions: _t("copy.noSessions"),
aboutEvolution: (n) =>
n ? _t("copy.aboutEvolution", { n }) : _t("copy.aboutEvolutionEmpty"),
// WorkBuddy P3(#501):成长卡 + 进化 toast
growthCount: (n) => _t("copy.growthCount", { n }),
growthNote: _t("copy.growthNote"),
evolutionToastTitle: _t("copy.evolutionToastTitle"),
evolutionToastMsg: (n) => _t("copy.evolutionToastMsg", { n }),
evolutionToastSee: _t("copy.evolutionToastSee"),
evolutionToastDismiss: _t("copy.evolutionToastDismiss"),
};
}
let COPY = buildCopy();
let failText = _t("tool.failText"); // 工具失败文案(字符串属性,chat.js 直接赋值用)

/** 系统状态文案 */
const COPY = {
disconnected: "连接中断了,正在重新连接…",
reconnected: "回来了,我们继续 ✦",
sessionBusy: "我还在处理上一条,稍等一下哦",
sendFailed: "没发送成功,你的话我还留着,再试一次?",
deleteConfirmTitle: "删除这段对话?",
deleteConfirmBody: "删除后无法恢复。",
noSessions: "还没有对话",
aboutEvolution: (n) => (n ? `EMRG 已自我成长 ${n} 次,感谢你的每一次反馈` : "EMRG 正在成长中"),
// WorkBuddy P3(rant 21:35):自进化可见化
growthCount: (n) => `已自我进化 ${n} 次`,
growthNote: "边工作边学习,越用越懂你",
evolutionToastTitle: "EMRG 刚刚完成一次自我进化!",
evolutionToastMsg: (n) => `这是它的第 ${n} 次自我改进,现在更好用了。`,
evolutionToastSee: "去看看",
evolutionToastDismiss: "知道了",
};
function refresh() {
COPY = buildCopy();
failText = _t("tool.failText");
}

window.EMRG_Copy = { toolPhrases, TOOL_FAIL_TEXT, COPY };
return {
toolPhrases,
COPY,
refresh,
get TOOL_FAIL_TEXT() { return failText; },
};
})();

window.EMRG_Copy = EMRG_Copy;
29 changes: 29 additions & 0 deletions emrg/gui/renderer/js/dialogs.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,32 @@ const Dialogs = (() => {
});
}

// ── 语言(rant 21:19:i18n 手动覆盖,即时生效)──
function renderLangOptions() {
const wrap = $("lang-options");
if (!wrap) return;
// 原始 localStorage 值:"" = 跟随系统(高亮第一项)
let saved = "";
try {
if (typeof localStorage !== "undefined") saved = localStorage.getItem("emrg.locale") || "";
} catch { /* ignore */ }
for (const btn of wrap.querySelectorAll(".theme-option")) {
btn.classList.toggle("active", (btn.dataset.lang || "") === saved);
}
}

function initLangButtons() {
const wrap = $("lang-options");
if (!wrap) return;
wrap.addEventListener("click", (e) => {
const btn = e.target.closest(".theme-option");
if (!btn) return;
const loc = btn.dataset.lang || "";
if (window.EMRG_I18N) window.EMRG_I18N.setLocale(loc); // 立即重刷全界面
renderLangOptions();
});
}

// ── 多模型管理 ───────────────────────────
// defaultName:当前默认模型(llm.model);extraModels:[[llm.models]] 条目(含与默认同名的配置项)
let defaultName = "";
Expand Down Expand Up @@ -189,6 +215,7 @@ const Dialogs = (() => {
$("set-project-dir").value = s.projectDir || "";
if (s.theme) theme = s.theme;
renderThemeOptions();
renderLangOptions(); // rant 21:19:语言选择器高亮当前值
defaultName = s.model || "";
extraModels = (s.modelDetails || []).map((m) => ({
name: m.name || "",
Expand Down Expand Up @@ -357,6 +384,8 @@ const Dialogs = (() => {

return {
initThemeButtons,
initLangButtons,
renderLangOptions,
initModelForm,
initRenameDialog,
showRename,
Expand Down
Loading
Loading