From e710db6ffb98ecc0e63709484f42c6314df84f57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=99=B4=E5=A4=A9?= Date: Thu, 14 May 2026 03:38:47 +0800 Subject: [PATCH] =?UTF-8?q?feat(ux):=20=E5=B0=8F=E7=99=BD=20UX=20=E5=85=A8?= =?UTF-8?q?=E9=9D=A2=E6=94=B9=E9=80=A0=20-=20=E9=94=99=E8=AF=AF=E5=8F=8B?= =?UTF-8?q?=E5=A5=BD=E5=BA=A6=20+=20=E8=87=B4=E5=91=BD=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E5=BC=BA=E7=A1=AE=E8=AE=A4=20+=20=E7=A9=BA=E7=8A=B6=E6=80=81?= =?UTF-8?q?=20+=20=E6=96=B0=E6=89=8B=E5=BC=95=E5=AF=BC=20+=20=E6=9C=AF?= =?UTF-8?q?=E8=AF=AD=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 面向小白用户的产品定位重塑,从七大 UX 痛点逐一改造: ## U1 错误友好度(59 处改造) - 新工具 src/lib/humanize-error.js:自动把后端原始错误(fetch failed、ENETUNREACH、ENOENT 等) 映射成「主行 + hint 行动建议 + 折叠技术详情」三段式结构化对象 - toast 组件升级支持 { message, hint, raw } 结构化入参,向后完全兼容 - 14 个 page 文件中所有 toast(t('xxx.failed') + ': ' + e, 'error') 替换为 toast(humanizeError(e, t(...)), 'error') - common.js 加 error.* / errorHint.* 共 13 个新 i18n 键(11 语言): 网络/Gateway 未启动/命令缺失/权限/超时/限流/未找到/鉴权/服务繁忙/通用 ## U2 致命操作强确认(14 处改造) - showConfirm 升级支持结构化对象 { message, impact[], title, confirmText, cancelText, variant } - 加 .modal-impact-list 红边样式(让小白看清楚删了会丢什么) - 14 处致命操作改造,每处显示影响列表 + 红色「删除/移除/重置」按钮 + 灰色「保留」取消: · agents.js 删除 Agent(动态显示 N 个绑定影响) · channels.js 移除平台(动态算 N 个 binding)+ 移除 Agent binding · memory.js 删除记忆文件 · services.js 卸载 Gateway(3 段影响)+ 删除备份 · models.js 批量删模型 · chat.js 删除会话 + 重置会话 · dreaming.js 重置梦境日记 + 清空 grounded 短期记忆 · agent-detail.js 解除渠道绑定 · cron.js 删除任务(OpenClaw + Hermes 两端) - skills.js 原生 confirm() 改 showConfirm - hermes-cron.js 原生 confirm() 改 showConfirm,顺手修末尾多余 `}` 的 syntax 残留 ## U3-C 空状态 emoji+CTA(5 页面) - 通用 .empty-state 组件(大 emoji + 标题 + 副本 + CTA 按钮 + 紧凑变体) - agents.js: 🤖 + 「+ 新建 Agent」CTA - memory.js: 🧠 + 「+ 新建记忆文件」CTA(紧凑版) - cron.js: ⏰ + 「+ 新建任务」CTA - skills.js: 🛠️ + 「技能商店」CTA(点击切 Tab) - channels.js: 💬 + 紧凑提示 - CTA 巧妙复用页面顶部已有按钮的 click,零重复逻辑 ## U3-B Dashboard 新手任务卡片 - 蓝紫渐变卡片,4 步任务自动检测:启动 Gateway / 添加模型 / 创建 Agent / 第一次聊天 - 已完成:✓ 徽章 + 删除线 + 60% 透明 - 未完成:编号徽章 + 蓝色 CTA 按钮跳对应页面 - 全部完成 → 庆祝条「🎉 全部搞定!」+ 关闭按钮 - localStorage 标记,用户主动关闭后永久隐藏 - 14 个新 i18n 键,文案小白化(Gateway 是「发动机」/ Agent 是「分身」/ 模型给 AI 装「大脑」) ## U3-A 术语表页(/glossary) - 25 个核心术语 × 4 大分类(核心 8 / 模型 6 / 接入 5 / 进阶 6) - 搜索框实时过滤 + Tab 切换分类 + 卡片网格布局 - 每条术语:「比喻 + 一句话」描述(避免循环引用)+ 「打开页面 →」CTA 直达配置 - 3 语言(zh-CN / en / zh-TW)完整翻译,其他 8 语言 fallback - 双引擎(OpenClaw + Hermes)共用路由 - dashboard quick-actions 加「📖 面板术语」入口 ## U3-D 术语 ⓘ tooltip - 通用 src/lib/term-tooltip.js helper:termHelpHtml(id) + attachTermTooltips(root) - 8 个高频术语精简表(OAuth / Webhook / Bot Token / API Key / Token / Context Window / Binding / Scope) - channels.js 字段 label 智能匹配关键词自动追加 ⓘ(覆盖 8 个渠道全部敏感字段) - models.js 添加/编辑 provider 的 API Key label 也加 ⓘ - 点 ⓘ → 弹小型 modal 含解释 + 「打开术语表 →」CTA - attachTermTooltips 内部去重,可安全多次调用 ## 累计交付 - 4 个新文件(humanize-error.js / term-tooltip.js / glossary.js page / glossary.js i18n) - 6 个升级文件(toast / modal / components.css / dashboard / channels / models) - 14 个 page 错误 toast 友好化(59 处) - 14 处致命操作强确认 - 5 处空状态升级 + Dashboard 新手卡片 + 术语表 + ⓘ tooltip - 109 个新 i18n 键(11 语言) - Build 全程通过 --- src/components/modal.js | 47 +++- src/components/toast.js | 60 ++++- src/engines/hermes/index.js | 1 + src/engines/hermes/pages/cron.js | 14 +- src/engines/openclaw/index.js | 1 + src/lib/humanize-error.js | 125 ++++++++++ src/lib/term-tooltip.js | 114 +++++++++ src/locales/index.js | 3 +- src/locales/modules/agentDetail.js | 5 + src/locales/modules/agents.js | 5 + src/locales/modules/channels.js | 11 + src/locales/modules/chat.js | 10 + src/locales/modules/common.js | 31 +++ src/locales/modules/cron.js | 5 + src/locales/modules/dashboard.js | 19 ++ src/locales/modules/dreaming.js | 10 + src/locales/modules/engine.js | 5 + src/locales/modules/glossary.js | 9 + src/locales/modules/memory.js | 5 + src/locales/modules/models.js | 5 + src/locales/modules/services.js | 10 + src/pages/agent-detail.js | 24 +- src/pages/agents.js | 37 ++- src/pages/assistant.js | 9 +- src/pages/channels.js | 76 ++++-- src/pages/chat.js | 22 +- src/pages/communication.js | 3 +- src/pages/cron.js | 28 ++- src/pages/dashboard.js | 125 +++++++++- src/pages/dreaming.js | 22 +- src/pages/extensions.js | 7 +- src/pages/gateway.js | 7 +- src/pages/glossary.js | 272 +++++++++++++++++++++ src/pages/logs.js | 5 +- src/pages/memory.js | 37 ++- src/pages/models.js | 21 +- src/pages/services.js | 31 ++- src/pages/skills.js | 24 +- src/style/components.css | 374 +++++++++++++++++++++++++++++ 39 files changed, 1510 insertions(+), 109 deletions(-) create mode 100644 src/lib/humanize-error.js create mode 100644 src/lib/term-tooltip.js create mode 100644 src/locales/modules/glossary.js create mode 100644 src/pages/glossary.js diff --git a/src/components/modal.js b/src/components/modal.js index b7708b6..dbfa908 100644 --- a/src/components/modal.js +++ b/src/components/modal.js @@ -17,26 +17,51 @@ function escapeAttr(str) { /** * 自定义确认弹窗,替代原生 confirm() * Tauri WebView 不支持原生 confirm/alert,必须用自定义弹窗 - * @param {string} message 确认消息 - * @returns {Promise} 用户选择确认返回 true,取消返回 false + * + * 入参 message 支持两种: + * 1) string —— 旧用法(向后兼容) + * 2) { message, impact?, ...options } —— 结构化致命操作确认: + * - message: 主问题("删除 Agent 'main'?") + * - impact: 影响列表 string[],渲染成 ul(让小白看清楚删了会丢什么) + * - title/confirmText/cancelText/variant: 同 options 同名字段 + * + * @param {string|object} message 确认消息或结构化对象 + * @param {object} [options] 旧版 options 字段(仍兼容) + * @returns {Promise} 用户选择确认返回 true,取消返回 false */ export function showConfirm(message, options = {}) { - // options: - // title: 标题文本(默认 t('common.confirmAction')) - // confirmText: 确认按钮文字(默认 t('common.confirm')) - // cancelText: 取消按钮文字(默认 t('common.cancel')) - // variant: 'danger' | 'primary'(默认 'danger',决定确认按钮颜色) + // 结构化入参:把对象字段合并到 options,message 取其 .message 字段 + let actualMessage = message + let impactList = null + if (message && typeof message === 'object') { + actualMessage = message.message ?? '' + impactList = Array.isArray(message.impact) ? message.impact.filter(Boolean) : null + // 对象字段优先于 options 同名字段 + options = { + title: message.title ?? options.title, + confirmText: message.confirmText ?? options.confirmText, + cancelText: message.cancelText ?? options.cancelText, + variant: message.variant ?? options.variant, + } + } + const title = options.title || t('common.confirmAction') const confirmText = options.confirmText || t('common.confirm') const cancelText = options.cancelText || t('common.cancel') const variant = options.variant === 'primary' ? 'btn-primary' : 'btn-danger' + + const impactHtml = impactList && impactList.length + ? `` + : '' + return new Promise((resolve) => { const overlay = document.createElement('div') overlay.className = 'modal-overlay' overlay.innerHTML = ` -