Commit Graph

158 Commits

Author SHA1 Message Date
晴天
7f078a3c49 fix(ui): import term tooltip helpers in config pages
Gateway 配置页和模型配置页都会在 label 中渲染术语帮助按钮,并在页面渲染后调用 tooltip 绑定逻辑。

这两个页面之前引用了 termHelpHtml / attachTermTooltips,但没有导入对应 helper,导致 Gateway 配置页加载配置后直接抛出 ReferenceError,页面停在加载失败状态。

## 修复
- Gateway 配置页补充 term-tooltip helper import
- 模型配置页补充同一 helper import,避免进入编辑服务商弹窗时触发同类运行时错误

## 验证
- npm run build:PASS
2026-05-15 18:54:13 +08:00
晴天
8b690cb6a7 fix(openclaw): update recommended kernel target and delta protocol handling
面板原本只声明 maxProtocol=3 的握手范围,且 chat delta 处理只接受前缀扩展。
连接到 2026.5.12+ 内核时会出现握手兼容问题;agent 输出在内容回滚或重排场景也会
丢失最新文本,前端 streaming 气泡停留在旧前缀。

## Connect 握手协议范围
- 将桌面端 connect frame 的 maxProtocol 从 3 扩展到 4
- 将 Web 模式 connect frame 的 maxProtocol 从 3 扩展到 4
- minProtocol 继续保留 3,用于兼容历史内核
- 范围 [3, 4] 同时覆盖旧内核和 5.12+ 内核,不需要按版本分支

## Chat delta replace 语义
- 当 payload.replace=true 时,无条件覆盖当前 AI 文本缓存
- 保留前缀扩展场景的增量更新逻辑
- 修复内容回滚或重排时文本长度变短导致 UI 不刷新的问题

## 推荐内核版本
- official 推荐目标升到 2026.5.12
- chinese 推荐目标升到 2026.5.12-zh.1
- KERNEL_FLOOR 不变,继续兼容历史安装

## 验证
- cargo check:PASS
- npm run build:PASS
2026-05-15 18:30:04 +08:00
晴天
b9a7c043d2 fix(audit): 复查发现的 5 个 bug — 双 listener / duplicate stub / timer leak / i18n
逐项排错盘点(OpenClaw + Hermes 引擎 + 共享层全面复查)。

## Bug #1 — Hermes 引擎双 listener 数组混用
src/engines/hermes/index.js

onStateChange / onReadyChange 共用一个 `_listeners` 数组:
```js
onStateChange(fn) { _listeners.push(fn); ... }
onReadyChange(fn) { _listeners.push(fn); ... }   ← 同一数组
```

main.js 注册 sidebar 渲染回调时两个都注册:
```js
_engineStateUnsub = engine.onStateChange(() => renderSidebar(sidebar))
_engineReadyUnsub = engine.onReadyChange(() => renderSidebar(sidebar))
```

结果:每次 detectHermesStatus(15s 一次 poll)触发,sidebar 被
renderSidebar 调两遍。OpenClaw 引擎用的 lib/app-state.js 早就是分开
两个数组(_gwListeners + _listeners),Hermes 是退化实现。

修复:
- 拆成 _stateListeners / _readyListeners 两个数组
- 加 prevReady / prevRunning 做 diff,仅在状态实际变化时通知

## Bug #2 — Web 模式下 check_panel_update 永远返回 false
scripts/dev-api.js

`check_panel_update` 在 line 6785 有完整实现(fetch GitHub/Gitee
release API),但 line 8586 又 stub 了一次:
```js
check_panel_update() { return { hasUpdate: false } }
```

Object literal 后定义覆盖前定义,Web 模式下用户永远看不到「有新版」
提示,必须升级到桌面客户端才能查更新。

修复:删掉重复 stub,留注释说明真实实现位置。

esbuild 之前就在 build 里 warn `Duplicate key "check_panel_update"
in object literal`,现在 warning 也消失了。

## Bug #3 — engine-select.js setTimeout 不在 cleanup 时清
src/pages/engine-select.js

choose 动画里两个 setTimeout (600ms + 1300ms) 没保存 id,路由
cleanup 时不清。极端情况:用户点完 OpenClaw 后立刻点 secondary
"稍后再说" → 1300ms 后被强制 navigate 回原 targetRoute,把用户拉走。

修复:
- 模块级 _animTimers 数组追踪所有动画 setTimeout id
- cleanup 时 clearTimeout 全清
- stage.dataset 防御性访问(路由切走后 stage 可能已不在 DOM)

## Bug #4 — router.js 三处中文硬编码
src/router.js

之前页面 loading / 加载失败 / 重新加载按钮直接写死"加载中..."
"页面加载失败" "重新加载"。i18n 里 `common.loading` /
`common.pageLoadFailed` / `common.reloadRetry` 早已存在但没用上。

修复:import t() + 三处替换。

## Bug #5 — dashboard.js Promise 超时 Error 写死中文
src/pages/dashboard.js

`new Error(\`超时(${ms/1000}s)\`)` — 这条错误最后被 humanizeError
处理后展示给用户,本来应该走 i18n。改成英文 `Timed out after Xs`,
统一与日志聚合(其他地方都是英文)。

## 验证
- npm run build:PASS(1.85s, 无 duplicate-key warning)
- cargo fmt --check:PASS(无改动)
- 受影响场景:
  - 引擎切换 sidebar 性能(Hermes 双重渲染消失)
  - Web 模式更新提示(恢复正常)
  - engine-select 动画中途切走(不再被强拉回)
  - 加载/错误页(i18n 完整)
2026-05-14 07:17:28 +08:00
晴天
6a0d87479c feat(engine-select): Monolith 对角线全屏选择屏
替换原卡片网格为「左上 OpenClaw(石墨黑)vs 右下 Hermes(象牙白)」对角线全屏设计。
启动屏 / 引擎切换时给用户一个有冲击力的「选择时刻」。

## 核心设计
- position: fixed 跳出 #content 范围,覆盖整个 viewport(含 sidebar)
- 双三角形 clip-path: polygon (0 0, 0 100%, 100% 0) / (100% 0, 100% 100%, 0 100%)
- 内容用 absolute 定位到三角形质心(左上 11%/6.5% / 右下 11%/6.5%),永不重叠
- 微妙的 60px 网格纹 + 角落极光(紫蓝 / 橙金)+ 极细中线分割
- clamp(80px, 13vw, 200px) 巨字标题 + 序号 + Logo + tagline + 特性列表 + CTA

## 交互
- hover 联动:用 [data-hover] attribute 替代 :has(),兼容旧 WebKit
  - 鼠标悬停一侧 → 该侧亮起 + 内容平移 + CTA 反白;另一侧变暗 + 内容模糊缩小
- 点击三角形 → 三角形 clip-path 扩满(0.8s)→ 中心圆扩散(0.9s)→ 进入主页
- reveal 节点 attach 到 body,跨路由切换存活,新页面渲染后再淡出

## Both / Later 处理
- 两个次级选项保留,做成底部居中的玻璃 pill 链接(不抢戏)
- 不走对角线扩散动画,点击后直接 applyEngineSelection + navigate

## 兼容性
- prefers-reduced-motion: reduce → 关闭所有动画
- 移动端响应式:< 760px 调整字号 / 边距 / 角标
- 用 Vite define 注入的 __APP_VERSION__ 显示版本号(与 main.js / sidebar.js 一致)

## i18n
- engine.choiceTopBanner / choiceCtaEnter
- choiceOpenclaw{Tagline,Feat1,Feat2,Feat3,Category}
- choiceHermes{Tagline,Feat1,Feat2,Feat3,Category}
- choiceSecondary{Both,Later}
- 三语完整(zh-CN / en / zh-TW)

## 抽卡 prototype
保留 docs/engine-select-mockups/ 下的 V2 4 张设计 + 索引页(v2-monolith.html
即本次接入的最终版本)。
2026-05-14 06:22:32 +08:00
晴天
c00b2dbf64 feat(openclaw): P1-6 config.schema RPC 写入校验 - 防小白改坏配置
OpenClaw 内核 config.set/patch 写入时已经会校验,但用户要等点保存才看到错误。
本 PR 把校验提前到前端,让用户改完字段立刻看到红字提示。

## 新增工具 src/lib/config-schema.js
- getFieldSchema(path) — 调内核 config.schema.lookup,带 5 分钟缓存
- validateField(path, value) — 拿 schema 后做基础约束校验
- validateFieldSync(schema, value, path) — 已有 schema 时同步校验
- clearSchemaCache(path?) — 清缓存(极少需要)

## 校验维度(不引入 ajv,保持 vanilla JS 体积)
- required — 必填空值检查
- type — string / number / integer / boolean / array / object / null
        (含「数字字符串当数字看」的容错)
- enum — 枚举白名单
- minimum / maximum — 数值范围
- pattern — 字符串正则

## 友好错误文案(i18n)
- common.error.schemaRequired / schemaType / schemaEnum / schemaMin / schemaMax / schemaPattern
- 6 个键 × 11 语言全覆盖
- 替换 schema 给的英文 path 后用户看到的是:
  「Gateway 端口不能小于 1024」「gateway.port 应该是「integer」类型」

## 集成示范:gateway.js
- saveConfig 开头先 validateField('gateway.port', port)
- 失败 → toast 红字 + focus 回 port 输入框 + early return
- 通过 → 走原流程
- 内核无该 schema 时降级放行(不阻断保存)

## 设计哲学
- 内核仍是最终守门人(config.set/patch 会再校验)
- 本模块的价值是「立即反馈」+「未来动态 UI 渲染」基础
- 容错优先:schema.lookup 失败时静默放行,不影响老内核兼容性

## 累计
- 1 新文件(config-schema.js)
- 2 修改(gateway.js / common.js)
- 6 个新 i18n 键 × 11 语言
- 后续可在 memory/dreaming/security/cron 等页面同样接入
2026-05-14 04:30:23 +08:00
晴天
e717a7a098 feat(openclaw): P1-0 push.web.* 推送通知 - ClawPanel 关掉也能弹系统通知
OpenClaw 内核已实现 4 个 push.web.* RPC(vapidPublicKey / subscribe / unsubscribe / test),
但 ClawPanel 完全没接。这次打通整条链路:浏览器 → Service Worker → 内核 → 系统通知中心。

## 收益(用户视角)
- ClawPanel 浏览器标签/桌面应用关掉后,Agent / Cron / 渠道消息仍能弹到
  Windows / macOS / iOS / Android 系统通知中心
- 锁屏可见,可离线接收(推送服务由浏览器厂商分发)
- 是下一版主推卖点

## 实施(无需新增 Tauri 命令)
- wsClient.request 直接走 WebSocket 调内核 4 个 RPC

## 前端封装 src/lib/push-web.js
- isPushSupported() / pushPermission() / requestPushPermission()
- ensureServiceWorker() 注册 /push-sw.js(幂等)
- subscribePush() 完整流程:权限 → SW → push.web.vapidPublicKey → PushManager.subscribe → push.web.subscribe 上报内核
- unsubscribePush() 本地取消 + 通知内核清理
- sendTestPush(title, body) 调 push.web.test 广播测试
- getCurrentSubscription() / isLocallySubscribed() 状态查询
- urlBase64ToUint8Array / arrayBufferToBase64Url 工具函数
  (VAPID 公钥 base64url ↔ 二进制,订阅 keys 编码)

## Service Worker public/push-sw.js
- skipWaiting + clients.claim 立即激活
- push 事件:解析 JSON payload → showNotification(含 icon / badge / tag / requireInteraction)
- notificationclick:优先聚焦已打开标签 + postMessage 跳转 url;
  没有窗口就 openWindow 新开
- 所有路径容错(payload 解析失败 fallback 到默认文案)

## UI 页面 src/pages/notifications.js
- 状态行:通知权限 + 订阅状态(彩色徽章)
- 端点摘要(订阅成功后展示截断的 endpoint,方便用户确认)
- 三个动作按钮(互斥):启用 / 取消订阅 / 发测试通知
- 测试通知会显示「已投递到 N 个订阅」提示
- 不支持环境(Tauri 1.x 桌面壳或老浏览器)显示友好的「Push not supported here」空状态
- 全程走 humanizeError 友好错误提示

## i18n src/locales/modules/notifications.js
- 26 个键 × 11 语言全覆盖
- 含权限徽章 / 操作按钮 / 流程提示 / 不支持环境说明

## 入口
- OpenClaw 引擎「配置」section 新增「推送通知」入口
- sidebar.notifications i18n(短词「推送通知 / Push」)
- 路由 /notifications 注册到 OpenClaw 引擎
- Hermes 引擎暂不注册(push.web.* 是 OpenClaw 内核的 RPC)

## CSS
- 加 .push-status-row / .push-status-item / .push-status-label / .push-status-value
- 复用现有 .lazy-deps-badge.{ok,warn,unknown} 样式

## 待跟进
- iOS Safari 16.4+ 需用户先把 ClawPanel 添加到主屏才能收 push(已知限制,文档跟进)
- 真实流量(不只是 push.web.test)需 OpenClaw 内核侧把通知事件主动 send 出来;
  本 PR 把订阅渠道彻底打通,后续内核怎么用现成订阅发送是另一题
- 累计变动:4 新文件 + 4 修改
2026-05-14 04:27:33 +08:00
晴天
7eababad4a feat(ux): toast 智能行动按钮 + 拓展 ⓘ 到 gateway/agents + sidebar 加术语表入口
延续上一轮小白 UX 改造的尾声三连:

## 1. toast 智能行动按钮(U2 收尾)
- humanizeError 输出新增 action 字段:{ label, route?, handler?, kind }
- 自动按错误 kind 给默认按钮:
  · gatewayDown → [去启动 Gateway] → /services
  · cmdMissing / permission → [打开设置] → /settings
  · auth → [检查 API Key] → /models
  · network / timeout / rateLimit / generic → 不给(重试由用户控制)
- toast 结构化分支渲染 .toast-action-btn 按钮,点击后用 navigate(route) 或调 handler,并自动关闭 toast
- common.js 加 errorAction.* 三个按钮文案 i18n(11 语言)

## 2. ⓘ 拓展到 gateway / agents
- gateway.js: token label 后加 ⓘ(apikey 术语),renderConfig 末尾 attachTermTooltips
- agents.js: addAgent 弹窗 workspace 字段 label 加 ⓘ,setTimeout 扫 document.body 绑定
- term-tooltip.js 精简表新增 4 个术语:workspace / provider / baseurl + scope(已有)

## 3. sidebar 加术语表入口
- 在两个引擎(OpenClaw + Hermes)的最后一个 section 加「术语」条目
- sidebar.js i18n 新增 glossary 键(11 语言)
- 之前只能从 dashboard quick-actions 进入,现在 sidebar 永久可达

## 4. 顺手 bug 修复
- gateway.js 文件末尾历史残留的多余 `}` (line 348) syntax 错误,删除
- 与之前 hermes/cron.js 同类问题,本次改 ⓘ 时被 node --check 暴露

## 累计变动
- 10 个文件修改
- 7 个新 i18n 键(11 语言)
- Build OK
2026-05-14 03:47:25 +08:00
晴天
e710db6ffb feat(ux): 小白 UX 全面改造 - 错误友好度 + 致命操作强确认 + 空状态 + 新手引导 + 术语表
面向小白用户的产品定位重塑,从七大 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 全程通过
2026-05-14 03:38:47 +08:00
晴天
7d4a423df0 feat(hermes-install): diagnose network failures and add optional Git mirror (#273)
- Detect git/network failure patterns (failed to connect, could not resolve host,
  unable to access, etc.) in install/update output and append a clear hint
  pointing users to the proxy or mirror settings instead of leaving them with
  raw multi-line git stderr.
- Add optional 'Hermes Install Mirror' setting (panelConfig.gitMirror): when set,
  install/upgrade injects GIT_CONFIG_COUNT/KEY_0/VALUE_0 to rewrite
  https://github.com/ via the mirror prefix at process scope only — the user's
  global ~/.gitconfig is never touched.
- Surface the new mirror field in Settings (works for both engines), with
  zh-CN/en/zh-TW copy and a hint explaining how it interacts with the install
  flow.
2026-05-14 01:46:55 +08:00
晴天
d0d6950628 fix(web-mode): consolidate Tauri event subscription helper to silence transformCallback errors (#256)
- Add shared safeTauriListen helper in tauri-api.js that returns a noop
  unsubscriber when running outside Tauri, so dynamic-importing
  @tauri-apps/api/event in the browser no longer throws
  'Cannot read properties of undefined (reading transformCallback)'.
- Replace 4 bare 'await import(@tauri-apps/api/event)' call sites
  (about.js hermes upgrade button + channels.js three install/action flows)
  that previously crashed the page on web mode.
- Drop the duplicated local tauriListen helpers in hermes dashboard / chat
  store and route them through the shared helper.
2026-05-14 01:31:58 +08:00
friendfish
1584d53bf9 fix: 修复批量测试模型时误触发 Gateway 重启 (#270)
Fixes #271
2026-05-14 01:12:04 +08:00
晴天
81c42dbfe2 chore: release v0.15.1 2026-05-10 21:30:36 +08:00
晴天
328624cf03 chore: release v0.15.0
发布 0.15.0:
- 新增内核版本兼容层、特性门控、低版本阻断和升级提示
- 新增 PATH 中 OpenClaw CLI 冲突检测、隔离与恢复
- 修复 Hermes Gateway loopback 自动拉起与 /v1/runs 诊断
- 修复 standalone 一键安装包在 About/仪表盘显示未知版本
- 同步 OpenClaw 2026.5.6 推荐版本和热更新 minAppVersion
- 补齐本地 JS/Rust 测试与发布前检查说明

验证:
- npm run build
- node --test tests/*.test.js
- node --check src/scripts JS 文件
- cargo fmt --all -- --check
- cargo check
- cargo clippy --all-targets -- -D warnings
- cargo test
2026-05-08 04:39:36 +08:00
friendfish
2ad5e2d5ce refactor(models): unify primary model rotation and sync
Refactor the model management page so every primary-model entry point goes through setPrimary(). This centralizes fallback-chain rotation, removes stale/deleted models from fallbacks, deduplicates the chain, and refreshes both the default bar and provider cards after primary changes.
2026-04-26 21:03:54 +08:00
晴天
9ee99ead24 chore: release v0.14.0
集中发版:

新功能(10)
- 心甜Claw 引擎入口(第 3 个引擎模式)
- Hermes 22 个 Provider 注册表 + 安装/仪表盘动态加载
- Hermes .env 高级编辑(拒绝触碰托管 Provider 密钥)
- Hermes 会话与用量分析增强
- Hermes Dashboard 自动拉起 + Windows POSIX-only 兼容模态
- Hermes Skills 工具集面板
- 官网 Hermes Agent 黑金特色区 + 图文指南
- Boot Manifest 启动页(双语 + 错峰动画)
- 官网 Markdown 阅读器图片 lightbox
- Hermes Memory 概览卡

改进(9)
- Hermes 仪表盘/扩展页全面本地化
- 记忆编辑大尺寸模态
- 日志下载 Web/桌面分流
- 侧边栏导航补全
- 模型备选管理 UI(PR #232)
- 模型加载错误 UX 重做(错误卡 + 详情 + 重试)
- .page 布局 clamp + .page-narrow
- Memory 单列断点提早到 1100px
- Web 模式跳过前端热更新检查

修复(12)
- Gateway 启动 platforms.api_server.enabled 自修复(含 7 unit test)
- Memory 页 overview 卡穿模(旧 flex 列约束 → 自然块流)
- Skills 页 hero/toolsets 被压缩(flex-shrink:0)
- Web 模式 Skills ReferenceError(补 _readHermesDisabledSkills)
- 日志/记忆下载行为分流
- src/pages/models.js 5 处 typo
- 删除 56 行 .hm-memory-* 死代码 + line-clamp 标准属性
- Dependabot rustls-webpki / postcss / rand
2026-04-25 23:47:22 +08:00
晴天
8a314ff64e Merge PR #232 fallback model UI optimization 2026-04-25 11:49:35 +08:00
晴天
3ed59fcb2b feat(hermes): align dashboard APIs and add xintian engine 2026-04-25 10:31:32 +08:00
晴天
5235853373 fix(gateway): debounce restart with single-flight queue (#248)
Root cause for #243 / #244 / #240: model edits trigger
api.restartGateway() with only 300ms debounce. Fast consecutive
edits stack up restart calls, creating zombie Gateway processes,
failed restarts, and CPU fan spikes.

Layer A (frontend):
- New src/lib/gateway-restart-queue.js: 3s debounce + single-flight
  lock + reschedule on in-flight request
- Refactor src/pages/models.js doAutoSave: write config immediately,
  schedule restart via queue with 'Apply now' toast button
- Subscribe to queue state for unified success/failure toast
- Add i18n: models.configQueued, models.applyNow

Layer B (backend):
- src-tauri/src/commands/config.rs: wrap restart_gateway /
  reload_gateway with tokio::sync::Mutex + 2s cooldown
- Cargo.toml: add tokio 'sync' feature
- scripts/dev-api.js: same guard for Web mode (inflight promise
  reuse + 2s cooldown)

Effects:
- 10 rapid edits within 3s -> 1 restart (was 10+ with races)
- Backend serializes concurrent restart calls, no zombie spawns
- User sees single 'Apply now' toast instead of restart storm

Refs #243 #244 #240
2026-04-24 19:35:39 +08:00
friendfish
9afe6eeb24 feat(models): refine fallback UI styling
- Display fallback chain as colored chips in a dedicated row when collapsed
- Add background colors to active chain and candidate pool for visual distinction
- Remove redundant Cancel/Save buttons since autoSave is enforced
2026-04-21 01:48:38 +08:00
friendfish
f61cb65b4a refactor(models): address PR #232 review comments
- i18n: Extract hardcoded Chinese strings to translation keys
- fix(url): Restore full-width punctuation in URL regex
- chore: Remove linux-schema.json (build artifact)
- chore: Add linux-schema.json to .gitignore
- refactor: Unify save strategy to use autoSave consistently
- style: Add trailing newline to en.json

Closes review items 1-6 from PR #232
2026-04-21 01:00:19 +08:00
晴天
1ef9ca8ede fix(models): 获取模型列表 404 改为友好提示 + 助手侧走后端绕 CORS
场景:部分服务商(如某些厂商的 Anthropic 兼容接口)不提供 /models 列表接口,
之前会直接显示 HTTP 404 Not Found / Failed to fetch 等技术错误,用户体验差。

- Rust list_remote_models:识别 404/405/501 作为"不支持自动获取"场景,
  返回带 [NOT_SUPPORTED] 前缀的友好错误,而非裸 HTTP 状态码
- 模型配置页「获取列表」:识别 [NOT_SUPPORTED] 后弹出引导对话框,
  点击「模型」按钮直接进入手动添加流程
- 助手设置页「获取列表」:改为走 Rust 后端 api.listRemoteModels,
  原先直接用前端 fetch 会被 WebView CORS 拦截(provider 不返回 CORS 头),
  改走后端既绕开 CORS 又能获得一致的友好提示
- Web 模式 dev-api.js 同步 404 识别逻辑,保证桌面 / Web 行为一致
- 补齐 models.fetchNotSupported / assistant.fetchNotSupported 多语言文案
2026-04-20 16:01:24 +08:00
晴天
12cdc72d2b fix(assistant): 模型测试按钮改用流式累积 + 增强诊断信息
- 测试请求切换到 stream: true + SSE 累积,绕开部分兼容网关
  non-streaming 分支对某些模型返回 200 + 空 body 的已知 bug,
  行为与真实对话路径一致
- 后端 test_model_verbose 显式设置 Accept-Encoding: identity,
  避免压缩协商带来的解码风险
- 用 resp.bytes() + 严格 UTF-8 decode,失败时 fallback 到
  lossy 字符串 + 前 200 字节 hex dump,方便定位非 UTF-8 响应
- 展开 reqwest error source 链,响应头与字节数原样返回前端
- 前端结果面板突出显示完整模型回复、固定 prompt 标注、
  响应头与 raw bytes hex,方便用户自查上游问题
- scripts/dev-api.js 同步 Rust 后端行为,保证 Web/桌面两侧诊断一致
2026-04-20 15:36:09 +08:00
晴天
f69360744f fix(assistant): 修复测试 Response Body 为 (empty) + 优化结果展示
## 用户反馈

截图显示测试结果详情里 "Response Body: (empty)",但对话实际可用。
用户:"有一个比较严重的bug...具体返回参数看不到...我感觉我们的功能不完整"

## 根因分析

1. **Accept-Encoding 未限制**:reqwest 只启用了 gzip feature(未启用 brotli),
   但 provider 经 CDN/反代可能返回 Content-Encoding: br,导致 resp.text() 解码失败。
2. **错误被静默吞**:`resp.text().await.unwrap_or_default()` 在解码失败时返回 "",
   前端展示为 (empty) 但没有任何错误提示,用户无法诊断。
3. **展示设计:reply 被截断 + 藏在折叠面板**:成功时模型回复只显示前 80 字符的
   预览,完整内容要展开 "查看完整请求/响应参数" 才能看到(还被 JSON 混在一起)。

## 修复

### Rust test_model_verbose

三个分支(OpenAI / Anthropic / Gemini)都显式加 `Accept-Encoding: identity`
头,禁止响应压缩。测试请求的响应体很小(几百字节),不压缩的性能损失可忽略。

`resp.text().await` 失败时不再 unwrap_or_default 静默吞,而是返回带 error 的
JSON:`"读取响应体失败: {e} (可能是压缩编码未支持或非 UTF-8 响应)"`

### dev-api.js test_model_verbose(Web 模式)

三个分支的 headers 都加 `'Accept-Encoding': 'identity'`,和 Rust 行为一致。

### 前端 buildTestResult 重写

- **顶部显眼展示模型回复**(边框高亮 + 完整内容 + max-height:180px 加 scroll,
  不再截断为 80 字预览):
  ```
  ✓ 连接成功 (300ms, Chat Completions)

  ╔═════════════════════════════╗
  ║ MODEL REPLY                 ║   ← 完整回复全文
  ║ 你好!我是 QC-A04...         ║
  ╚═════════════════════════════╝
  ```
- **空 respBody + 非空 reply 时给明确诊断**:"响应主体未能读取(可能是压缩
  编码异常),但已从响应流中提取到回复内容"
- **固定 prompt 脚注**:`📌 本次测试使用预设 prompt "你好,请用一句话回复"
  + max_tokens=200` —— 让用户明白这是固定诊断请求,不是真实对话。
- **详情面板的空 body 展示优化**:不再显示 "(empty)" 字面量(可能被误解为
  服务器真的返回了空字符串),改为带颜色和 italic 的 "(响应体为空)" 提示。

### i18n 新增 5 个翻译键

- testModelReply:Model Reply
- testFixedPrompt:本次测试使用预设 prompt...
- testRespBodyEmpty:响应主体未能读取...但已从流中提取回复
- testShowDetails:查看完整请求/响应参数(原来硬编码中文)
- testRespBodyEmptyDetail:(响应体为空) [原来是硬编码 "(empty)"]

均覆盖 zh-CN / en / zh-TW / ja / ko / vi。

## 验证

- npm run build 通过(assistant chunk gzip 49.43 KB)
- cargo check 通过
- 下一步:用户实测后确认 Response Body 正常显示再发 v0.13.4

## 相关

- Roadmap v0.14.0:把测试功能升级成迷你 Playground(自定义 prompt / 多轮
  对话 / 流式显示 / max_tokens 滑块)
2026-04-20 14:12:40 +08:00
晴天
d6cc0e04d3 fix(assistant): 补齐备用模型重设计缺失的变量声明
上一个 commit (b00c457) 的 edit 只替换了 renderFallbackList 函数体,
漏掉了前置的 5 个变量/函数声明,导致打开设置弹窗时抛错:

  Uncaught ReferenceError: renderPrimaryRow is not defined
      at assistant.js:3428:59
      at Array.forEach
      at showSettings

补齐:
- fallbackPrimaryModelEl (主模型只读行里的 model 显示)
- fallbackPrimaryHostEl  (主模型只读行里的 hostname 显示)
- fallbackPresetsEl      (厂商预设按钮容器)
- hostOf(url)            (从 URL 提取 hostname)
- renderPrimaryRow()     (渲染主模型只读行)

验证:npm run build 通过,settings modal 可正常打开。
2026-04-20 13:35:35 +08:00
晴天
b00c457c2b refactor(assistant): 备用模型 UI 重设计 - 厂商预设快捷添加 + 极简列表
## 用户反馈

"晴辰助手的备用模型配置,很复杂,很麻烦" —— 旧 UI 每个备用要填
6 个字段(label / baseUrl / apiKey / model / apiType / enabled),
每张卡 3 行 6 输入框 ~200px 高,添加流程比配主模型还复杂。

## 重设计原则

备用模型本质是"主模型挂了用啥兜底",应该是**选一个**而不是**重新配一个**。
复用 PROVIDER_PRESETS 里已有的 16 个厂商预设,一键预填 baseUrl / apiType。

## 新 UI 结构

```
┌─ 备用模型 (已启用 2 个) ────────────────────── [▼] ─┐
│ 主模型失败时按顺序切换到备用(401/403 除外)         │
│                                                      │
│ 📌 主模型(当前)  gpt-4o-mini     gpt.qt.cool      │  ← 只读
│ ⋮⋮ #2 [晴辰云] claude-haiku · api.anthropic   编辑 × │  ← 紧凑一行
│ ⋮⋮ #3 qwen3-30b · localhost:8000               编辑 × │
│                                                      │
│ 选择服务商快速添加:                                 │
│ [★晴辰云][OpenAI][Anthropic][DeepSeek][Google][Ollama]│
│ [📋 从主模型复制] [+ 自定义/自建] [更多服务商…]      │
└──────────────────────────────────────────────────────┘
```

## 关键变化

| 维度              | 旧                           | 新                                    |
|-------------------|------------------------------|---------------------------------------|
| 每行高度          | ~200px 卡片                  | ~36px 紧凑,点编辑才展开              |
| 添加方式          | 空白卡 6 字段填              | 点厂商 → 只填 apiKey + 选 model       |
| label 字段        | 用户手填                     | 去掉,自动用 model 显示               |
| enabled 开关      | 显式               | 去掉(删除即停用)+ 迁移旧禁用条目    |
| 主模型可见        | 无                           | 列表顶部显示完整调用链 (📌 主模型行) |
| 排序              | 隐式按数组顺序               | 显式 HTML5 drag-drop 拖拽手柄        |
| baseUrl/apiType   | 始终暴露                     | 折叠到"高级选项"(选 preset 后不用碰)|
| 快捷"从主模型复制"| 无                           | 有(解决"备用和主只想换个模型"场景)  |

## 实现细节

- 复用 `src/lib/model-presets.js` 的 `PROVIDER_PRESETS` 16 个厂商
- 主按钮区展示 6 个最常用(qtcool / openai / anthropic / deepseek
  / google / ollama),其余点"更多服务商…"展开
- 点厂商 → push draft 对象(含临时字段 `_editing`/`_brandLabel`),
  默认展开编辑态,autofocus 到 apiKey
- 保存时 .map 只挑 5 个字段(自动 strip 临时字段 + 不再写 enabled)
- 迁移:保存时过滤 `enabled === false` 的老条目,用户下次看到的
  都是启用状态(避免 UI 不暴露 enabled 导致的"隐形禁用"困惑)
- 主模型只读行实时跟随表单 `#ast-baseurl` / `#ast-model` 变化
- 每行折叠态的 model / hostname 在编辑态输入时实时更新(不重渲染
  避免输入框失焦)
- HTML5 drag-drop 拖拽排序(dragstart/dragover/drop),无第三方库

## i18n

新增 10 个翻译 key(至少覆盖 zh-CN / en / zh-TW / ja / ko / vi):
- fallbackPrimaryRow / fallbackPickProviderHint
- fallbackAddCopyPrimary / fallbackAddCustom / fallbackMoreProviders
- fallbackEditAdvanced / fallbackHideAdvanced / fallbackShowAdvanced
- fallbackUnnamedModel / fallbackPickProviderTitle

## 验证

- npm run build 通过
- assistant chunk 156.24KB → 162.02KB(gzip +1.5KB),合理
- 向后兼容:已存的 fallbackModels 数据(含 label/enabled 字段)
  可正常读取和显示,保存时会"隐式迁移"(去掉 enabled 字段,禁用
  条目被清理)

## 相关

- #Compat-3 系列(备用模型 failover)
- 用户反馈:"很复杂,很麻烦,整体重新设计下!"
2026-04-20 13:31:16 +08:00
晴天
7c63438c0e feat(assistant): 识别本地 LLM 服务端常见错误并给出修复指引
用户反馈:切本地 vLLM(Qwen/Qwen3-30B-A3B)后在助手里调用工具报错:
  "auto" tool choice requires --enable-auto-tool-choice and
  --tool-call-parser to be set

这是 vLLM 0.6+ 的默认安全策略 —— 必须在启动参数显式开启工具调用
才允许客户端在 body 里带 tools 字段。ClawPanel 发的请求符合 OpenAI
规范,不是我们的 bug,但用户面对这个原始报错字面上看不出是 vLLM
配置问题也不知道怎么修。

## 解决

新增 src/lib/model-error-diagnosis.js,提供 enhanceModelCallError:
保留原始错误文本 + 附加中文修复指引。目前覆盖 5 类常见本地部署错误:

1. **vLLM tool choice 限制**(本次用户实际踩的)
   - 给出 --enable-auto-tool-choice + --tool-call-parser 启动命令
   - Qwen / Mistral / Llama 各系列推荐的 parser 值
   - 建议临时切到"聊天"模式规避

2. **llama.cpp / LM Studio 旧版本不支持 tools**
3. **Ollama 模型不支持 tools**
4. **模型 ID 不存在 / 404**
5. **上下文超长 / token limit**

在 assistant.js 的 5 个错误抛出点统一接入:
- callChatCompletions(OpenAI 聊天模式)
- callResponsesAPI(新 /v1/responses 接口)
- callAnthropicMessages(Claude)
- callGeminiGenerate(Gemini)
- callAIWithTools(工具模式,就是用户踩坑的那条路径)

## 验证

- npm run build 通过
- assistant chunk 从 153.98KB → 156.24KB(gzip +0.86KB),合理
- 所有增强都走 try { parseJSON } 之后,不会影响原有错误处理路径

## 相关

- #Compat-5 系列的一部分(运行时错误诊断)
- 用户场景:vLLM + Qwen3 MoE,切换到本地模型后调工具
- 用户侧实际修复命令:
  vllm serve <model> --enable-auto-tool-choice --tool-call-parser hermes
2026-04-20 13:02:05 +08:00
晴天
bf9cb52e25 fix(setup): 修复 Node.js/Git/OpenClaw 装完不识别(需重启客户端)
用户反馈 0.13.3 版本有三个共性 bug:
1. 手动装 Node.js → 装完 panel 不识别,必须重启客户端
2. 一键装 Git → 装完 panel 不识别,必须重启客户端
3. 一键装 OpenClaw → 装完 panel 不识别,必须重启客户端

## 根因

Tauri 进程的 `std::env::var("PATH")` 是启动时快照,不会随系统 PATH 更
新。`enhanced_path()` 虽然扫描了常见安装目录,但**不存在的目录不会被
加入缓存**(line 828 `std::path::Path::new(p).exists()` 过滤)。装完
新程序后,新路径不在 enhanced_path 缓存里,CLI 检测又依赖子进程的
PATH,导致「找不到刚装的二进制」。

三个 bug 各有其子因:

### Bug 1: Git 检测 `find_git_path` 不用 enhanced_path
对比 `find_node_path` 显式 `cmd.env("PATH", enhanced_path)`,
`find_git_path` 里的 `where git` / `which git` 子进程继承的是 Tauri
启动时快照的老 PATH。即使 `refresh_enhanced_path()` 刷了缓存,子进
程也看不到新路径。`check_git` 后续调 `Command::new("git")` 同理,且
拿到 `git_path` 绝对路径后也没用。

### Bug 2: `auto_install_git` 三平台都不刷缓存
winget/xcode-select/apt 安装成功后直接 return,没有
`refresh_enhanced_path()` / `invalidate_cli_detection_cache()`。前端
`runDetect` 虽然会调 `invalidatePathCache`,但 Bug 1 让刷缓存也白刷。

### Bug 3: `upgrade_openclaw` npm 首装分支不刷缓存
npm 分支里只有 `if need_uninstall_old`(切换源场景)分支末尾刷了
PATH 缓存。**首次安装** `need_uninstall_old == false`,if 块整个跳
过,函数直接返回,CLI 检测缓存(60s TTL)和 PATH 缓存都是旧的。前
端 `setTimeout(reload, 1500)` 触发 SPA 重建,但 Tauri 进程没重启,
缓存没刷 → `is_cli_installed()` 返回 false。

### Bug 4: 手动装 Node.js 没有 hook 点
用户点「下载 Node.js」跳浏览器,装完回到 panel,panel 不知道用户装
了。虽然顶部有「重新检测」按钮,但 UX 上容易错过。`runDetect` 里虽
然会 `await api.invalidatePathCache()`,但需要用户主动触发。

## 修复

### Rust 端

1. **`find_git_path`**:子进程 `where`/`which` 显式 `env("PATH",
   enhanced_path)`,对齐 `find_node_path` 的做法。
2. **`check_git`**:优先用 `find_git_path` 返回的绝对路径执行
   `--version`;fallback 到 `"git"` 时也注入 enhanced_path 到子进程
   PATH,确保刚装完的场景能识别。
3. **`auto_install_git`**:winget/xcode-select/apt 三个平台的成功分
   支都调 `super::refresh_enhanced_path()` +
   `invalidate_cli_detection_cache()`。
4. **`upgrade_openclaw`**:npm 分支末尾(if need_uninstall_old 块之
   后、`get_local_version` 之前)无条件刷缓存,覆盖首装场景。切换源
   场景虽然前面已刷过一次,这里重刷无害(几十 ms 文件系统扫描开销
   可接受)。

### 前端

5. **`setup.js::render`**:注册 `visibilitychange` + window `focus`
   监听器,用户从浏览器装完 Node.js 切回 panel 时自动 `runDetect`。
   handler 自带 guard(`page.isConnected` 检查),页面切走后监听器
   自动卸载,防止泄漏。加 3 秒节流,避免 focus + visibilitychange
   同时连发触发重复检测。

## 验证

- `cargo fmt --check` 通过
- `cargo clippy --all-targets -- -D warnings` 通过
- `npm run build` 通过(setup chunk 未变,setup.js 新增 ~22 行)
- 本地需要用户验证:
  - [ ] 手动装完 Node.js → 切回 panel 自动识别
  - [ ] 一键装 Git → 装完立刻识别(无需重启)
  - [ ] 一键装 OpenClaw(首次 npm 安装)→ 装完立刻识别
  - [ ] 一键装 OpenClaw(切换源)→ 装完立刻识别(原本就工作,不回归)

Refs: 0.13.3 用户实测反馈
2026-04-20 11:57:08 +08:00
晴天
dfb81066b4 feat(assistant): 备用模型组 failover + 测试按钮走 Rust 后端(修 status 0)
解决用户反馈的两个问题:晴辰助手设置里"测试"按钮在某些 provider
(如 gpt.qt.cool)上显示 Response Status: 0、Body 空,以及只能配
一个模型、挂了就没法用。

## 1. 测试按钮 status 0 根因 & 修复

**根因**:Tauri 桌面端以前走 webview 的 `fetch()` 直打外部 API,
受 Chromium 网络栈限制 —— 某些 provider 的 HTTP/2 分块编码、TLS
握手、CORS 预检、或特殊响应头会被静默拒绝并抛 TypeError: Failed
to fetch,前端 catch 后把 `respStatus` 写死 0、`respBody` 空。这
不是 provider 的问题,也不是 key 的问题,是 Chromium net stack 的
兼容性问题。

**修复**:新增 Rust 命令 `test_model_verbose`(基于已有的 reqwest
HTTP 客户端),返回结构化 JSON
  `{success, status, reqUrl, reqBody, respBody, reply, error,
   elapsedMs, usedApi}`。

  前端测试按钮无论 Tauri/Web 模式,一律调 `api.testModelVerbose()`:
  - Tauri → `invoke('test_model_verbose')` → 走原生 reqwest
  - Web → `fetch('/__api/test_model_verbose')` → 走 dev-api.js 服务端
    fetch

  这样绕过了 webview net stack 所有兼容性陷阱,拿到的永远是真实
  HTTP status(含 401/429/5xx)和原始 body,debug 面板展示完整信息。

  相比旧的 `test_model` 命令,`test_model_verbose` 不会因 400/422/429
  就吞错误返回 "连接正常",而是如实回传,便于用户排查。

## 2. 备用模型组 failover(参考 OpenClaw)

**新增配置**:`_config.fallbackModels: Array<{label, baseUrl, apiKey,
model, apiType, enabled}>`,存在 localStorage 里。

**callAI 改造**:
- 旧的 `callAI` 改名为 `_callAIOnce`,保持不变
- 新增 `callAIWithSlot(slot, messages, onChunk)`:临时把 slot 注入
  到 `_config`,调 `_callAIOnce`,finally 恢复(单线程安全,因为
  `_isStreaming` 防并发)
- 新的 `callAI`:`buildActiveSlots()` 收集主模型 + 启用且配置完整
  的 fallback,按序尝试
  - 成功 → return
  - `AbortError`(用户中止)→ 直抛,不 failover
  - 鉴权错误 401/403/`unauthorized`/`invalid api key` → 直抛,不
    failover(切也白切)
  - 其他可重试错误(网络/超时/5xx/429/400 请求错/模型不存在)→ 在
    聊天里插入 `⚠ 模型「X」失败,切换到备用「Y」` 引用块,继续下
    一个 slot
  - 全部 slot 都失败 → 抛最后一个错误,触发既有 retry bar + circuit
    breaker 流程

**UI**:设置面板 API 标签页,在晴辰云 promo 卡片下方新增一个默认
折叠的 `<details>` 区块"备用模型组":
- 顶部 summary 显示启用数量 + 折叠箭头
- 每张卡片:label / baseUrl / apiType / apiKey / model(紧凑 2 列
  栅格)+ enabled 开关 + 删除按钮
- 顶部 "添加备用模型" 按钮:默认继承主模型的 apiType,减少配置项
- 编辑态用 fallbackDrafts(深拷贝),保存按钮才过滤空卡片写回
  `_config.fallbackModels`
- 单个 input 变化时只同步 drafts + 更新计数,不重渲染列表(保持
  输入框焦点)

**文件改动**:
- `src-tauri/src/commands/config.rs`:+175 行 `test_model_verbose`
- `src-tauri/src/lib.rs`:注册新命令
- `src/lib/tauri-api.js`:+1 行 `testModelVerbose` 封装
- `scripts/dev-api.js`:+75 行 Web 模式 test_model_verbose handler
- `src/pages/assistant.js`:
  - `loadConfig`: 新增 `fallbackModels = []` 默认值
  - `callAI` 重构为 failover loop(+80 行)
  - 测试按钮:移除 90 行的 webview fetch 双分支,统一调 verbose
    API(净减 ~60 行)
  - `showSettings`: 新增备用模型 UI + 事件绑定(+85 行)
  - 保存按钮:收集 fallbackDrafts 写回 _config
- `src/locales/modules/assistant.js`:11 语言翻译(slotPrimary /
  failoverNotice / fallbackModelsTitle / fallbackModelsDesc /
  fallbackEnabledSuffix / fallbackEmpty / fallbackAdd /
  fallbackRemove / fallbackEnabled / placeholders)

## 验证

- `npm run build` 通过(assistant chunk 149.85 → 153.98 kB)
- `cargo fmt --check` 通过
- `cargo clippy --all-targets -- -D warnings` 通过
- 向后兼容:旧用户的 `localStorage` 无 `fallbackModels` 字段,
  loadConfig 会初始化空数组,既有行为不变

Refs: 模型兼容性优化 + 多模型 failover 需求
2026-04-20 03:43:43 +08:00
晴天
58f5525445 fix(assistant): 连续错误自动熔断,避免无限重试循环 (#226)
在路径拦截 + sanitize 的基础上再加一道保险:用户粘贴的不只是
markdown 本地图片、而是其他 API 不接受的内容时(如意外内容格式、
鉴权失败、quota 耗尽),单纯清洗无法解决,用户反复点"重试"会陷
入同样的错误循环。

熔断机制:
- 2 分钟滑动窗口内,同一错误指纹累计 ≥3 次触发熔断
- 错误指纹归一化:去掉数字(时间戳/请求 ID)、URL、多余空白,
  只对比核心语义
- 熔断状态下重试按钮禁用 + 警告色样式,hint 文案改为"请先检查
  API 配置或网络",点击后 toast 提示而非触发重试
- 自动恢复:修改配置(saveConfig)或新建会话(createSession)时
  调用 resetCircuit() 清空失败历史

UI 共用:抽出 createRetryBar(session, circuitOpen) 供 sendMessageDirect
和 retryAIResponse 两处复用,消除原本重复的 30 行错误处理代码。
新增 .ast-retry-bar-circuit CSS 变体(warning 色 + 禁用态)。

翻译键(11 语言):
- retryCircuitHint — 熔断状态下的重试栏 hint
- retryCircuitBlocked — 点击已禁用重试按钮时的 toast

Refs: #226
2026-04-20 03:25:40 +08:00
晴天
3a4566d26a fix(assistant): 拦截本地文件路径粘贴/拖拽,清洗消息中的本地路径引用 (#226)
用户将 C:\...\image.png 等本地路径粘贴到晴辰助手输入框,消息发送到
OpenAI 兼容 API 时触发 "Only base64, http or https URLs are supported"
错误;由于历史上下文包含坏消息,后续对话会继续报错形成循环。

三处修复:
1. paste 事件:如果剪贴板是纯文本且为本地路径(Windows/Unix/file://),
   阻止默认粘贴并 toast 提示用户
2. drop 事件:若拖入的是路径文本(无 files),同样拦截并提示
3. buildMessageContent:调用 sanitizeUserTextForApi,把
   ![alt](C:\...) 这类 markdown 图片替换为占位文本 "[本地文件(已忽略)]",
   让已经输入路径的老会话也能自愈,不再循环报错

路径识别支持:
- Windows 绝对路径(C:\... / D:/...)
- macOS/Linux 常见路径前缀(/Users /home /mnt /media /opt /tmp /var /root)
- file:// URL

翻译覆盖 11 种语言(zh-CN/zh-TW/en/ja/ko/vi/es/pt/ru/fr/de)。

Refs: #226
2026-04-20 03:07:24 +08:00
friendfish
540cd405ab feat: optimize model management UI with waterfall fallback editor 2026-04-18 13:35:26 +08:00
晴天
36eaa64bf4 chore(release): v0.13.3
- 修复 #212 AI 消息气泡空白
- 修复 #215 HTTPS 下 WebSocket Mixed Content
- 修复 #219 多实例版本检测错误
- 修复引擎切换后仪表盘无限加载
- 修复热更新假更新循环(macOS/Linux)
- CI release 构建前自动同步版本号
2026-04-16 13:55:26 +08:00
晴天
55e8365cab chore: release v0.13.2 2026-04-13 15:10:20 +08:00
晴天
3fc73b485b feat(about): Hermes config/upgrade/uninstall use modal dialogs with logs
配置按钮: 弹出模态框显示当前 Hermes 配置(Provider/URL/Key/Model),
  带"前往配置向导"按钮跳转 setup 页面
升级按钮: 确认模态框 → 进度条+实时日志(监听 hermes-install-log 事件)
卸载按钮: 两步确认(是否卸载 → 是否清除配置) → 进度条+日志
所有操作完成后自动刷新 Hermes 卡片数据
新增 6 个 i18n 键
2026-04-13 10:35:37 +08:00
晴天
6971bd32e8 feat(about): add Hermes Agent upgrade/uninstall/config buttons
Hermes 卡片已安装状态下新增三个操作按钮:
- 配置: 跳转到 Hermes 设置页
- 升级: 调用 updateHermes() 重新安装最新版
- 卸载: 调用 uninstallHermes(),支持选择是否清除配置
新增 12 个 i18n 键 (zh/en/繁/ja/ko)
2026-04-13 10:24:03 +08:00
晴天
2b6f80091a fix: fake update detection, Hermes web mode commands, cleanup hot update
1. 假更新检测: checkNewVersion 对比 Tauri 二进制版本与前端版本,
   若前端版本 > 二进制版本(热更新导致),提示用户下载完整安装包
2. 版本统一: sidebar 和 about 页面均使用 __APP_VERSION__
3. 更新机制: checkHotUpdate → checkNewVersion,改用 GitHub Releases API,
   移除 check_frontend_update / download / rollback / get_update_status 死代码
4. Hermes Web 模式: dev-api.js 实现全部 15 个 Hermes 命令处理器
   (check_python, check_hermes, install_hermes, configure_hermes,
    hermes_gateway_action, hermes_health_check, hermes_api_proxy,
    hermes_agent_run, hermes_read_config, hermes_fetch_models,
    hermes_update_model, hermes_detect_environments, hermes_set_gateway_url,
    update_hermes, uninstall_hermes)
5. i18n: 新增 versionMismatch, hotUpdateDeprecated, downloadFullInstaller
2026-04-13 09:50:36 +08:00
晴天
dd9d7e5b6e fix: 初始设置页全部检测通过时自动跳转仪表盘
修复全部检测项(Node.js/Git/CLI/配置文件)通过后仍停留在初始设置页的问题,
现在会自动刷新引擎状态并跳转到仪表盘。
2026-04-13 04:18:29 +08:00
晴天
5575566806 feat: Hermes Agent 多引擎架构核心代码
- 新增 src/engines/hermes/ 完整引擎(仪表盘/服务管理/模型配置/Agent管理/对话)
- 新增 src/lib/engine-manager.js 引擎管理器(切换/检测/状态)
- 新增 src-tauri/src/commands/hermes.rs 后端命令(Gateway控制/配置读写/Agent Run SSE)
- sidebar 引擎切换器 UI
- i18n 新增 engine 模块(中/英/繁体)
- 多安装清理工具(gateway-ownership.js)
- 晴辰助手文件访问开关
- Hermes 对话工具调用可视化、SSE 流式输出
- Cargo.lock / dev-api.js 同步更新
2026-04-13 04:09:09 +08:00
晴天
271dc93b08 feat: OpenClaw 4.9 全面适配 (v0.12.0)
- 推荐内核统一升级至 2026.4.9 / 2026.4.9-zh.2
- standalone 安装兼容 edition 格式 latest.json + openclaw-zh- 文件名前缀
- standalone 三级降级: R2 CDN → GitHub Releases → npm
- pre_install_cleanup 所有命令加 10s 超时保护(修复安装卡死)
- npm uninstall 加 30s 超时保护
- wmic 全部迁移到 PowerShell(兼容 Windows 11)
- standalone 下载增加文字进度显示
2026-04-11 04:42:36 +08:00
晴天
70d768be17 feat: new pages + dashboard enhancements + backend improvements
New pages:
- Plugin Hub: grid cards, search, install/toggle/enable plugins
- Route Map: SVG visualization of channels→agents bindings with legends
- Diagnose: gateway connectivity diagnosis with step-by-step checks

Dashboard enhancements:
- WebSocket status indicator (connected/handshaking/reconnecting/disconnected)
- Connected channels overview with platform icons
- Colored log level badges (ERROR/WARN/INFO/DEBUG) with timestamps
- Channels data loading in dashboard secondary fetch

Splash screen:
- Multi-stage boot detection (JS not loaded vs boot slow vs timeout)
- 15s: WebView2/resource load failure
- 20s: "initializing..." hint with elapsed counter
- 90s: true timeout error

Backend (Rust):
- diagnose.rs: gateway connectivity diagnosis command
- messaging.rs: plugin management commands
- service.rs: improvements
- lib.rs: register new commands

Frontend libs:
- feature-gates.js: feature flag system
- ws-client.js: reconnect state tracking
- tauri-api.js: new API bindings
- model-presets.js: provider fixes
- Remove gateway-guardian-policy.js (unused)

Dev API (scripts/dev-api.js):
- list_all_plugins, toggle_plugin, install_plugin handlers
- probe_gateway_port, diagnose_gateway_connection handlers

i18n: dashboard, sidebar, diagnose, extensions, routeMap locale modules
CSS: plugin-hub cards, route-map SVG styles
2026-04-11 00:44:06 +08:00
晴天
56039ce932 fix: improve install/uninstall stability + dreaming animations + scan fix flow
Dreaming page:
- Restore animated hero card (stars, moon, floating Z's, deep sky gradient)
- Glass-morphism stat cards inside hero
- runAction/toggleDreaming: friendly toast for unsupported RPCs instead of raw error

Install/Uninstall (config.rs):
- pre_install_cleanup: fix Windows process kill (wmic CommandLine match instead of useless WINDOWTITLE filter), kill standalone dir processes, wait 2s
- uninstall: wait 3s after gateway stop, wmic kill standalone processes, retry deletion with 3s delay
- upgrade source-switch: standalone cleanup with process kill + retry + wait
- gateway reinstall: wait 2s after stop before install

Scan fix flow (chat-debug.js):
- 5-step fix with progress toasts (pairing, doctor, gateway, wait, websocket)
- Gateway start polling (up to 15s) instead of fixed 5s wait
- WebSocket reconnect polling (up to 12s) instead of fixed 3s wait

Frontend (modal.js + about.js):
- setProgressLabels() for custom progress text per modal usage
- Uninstall modal shows "停止服务/卸载组件/清理文件" instead of "下载依赖"

i18n: 11 locales updated with fixStep* and uninstall progress keys
2026-04-11 00:37:14 +08:00
晴天
cfd00f043e feat: add Dreaming UI and gateway auto-fix startup flow 2026-04-10 20:28:20 +08:00
晴天
3105e56fd6 fix hosted session binding and harden panel config resolution 2026-04-10 18:59:41 +08:00
晴天
3c5a0d252b chore: release v0.11.6
feat: Skills multi-agent support — agent selector + per-agent skills directory (Rust/Node.js/frontend)
feat: Assistant tool mode streaming — typewriter effect + tool_calls chunk accumulation
improve: OpenClaw 4.5 compatibility — full agent event stream handling + 3-min ultimate timeout
improve: Replace hot-update with stable download links (website/GitHub)
fix: Gateway status flapping — dashboard throttle + TCP retry + debounce threshold
fix: Assistant empty gray bubbles — SSE 0-chunk detection + stream error capture + render filter
2026-04-07 16:17:09 +08:00
晴天
35423f428b feat(chat): OpenClaw 4.5 compatibility — fix silent no-reply + add working state indicators
Problem:
- Sending a message with no response left users stuck with an empty typing
  indicator forever (no timeout, no error, no feedback)
- Users had no visibility into what OpenClaw was doing (thinking, planning,
  executing tools, running commands, waiting for approval)
- The response watchdog only reacted to chat delta/final events, ignoring
  all agent processing events

Root cause:
- Watchdog looped every 15s polling history but had no ultimate timeout
- Agent events (lifecycle, item, plan, approval, thinking, command_output)
  were not handled — only tool stream events updated the typing hint
- When OpenClaw processed silently (no delta), the UI showed nothing useful

Fix:
1. Handle ALL OpenClaw 4.5 agent event streams in handleEvent():
   - lifecycle: "AI is processing…" on phase start
   - item: structured execution steps (tool/command/search/analysis)
   - plan: "AI is planning…"
   - approval: "Waiting for approval…"
   - thinking: "AI is thinking…"
   - command_output: "Running command…"
   - compaction/error: appropriate indicators

2. Enhanced watchdog mechanism:
   - 3-minute ultimate timeout that persists across watchdog re-polls
   - Agent events reset the polling timer (OpenClaw is alive) but not
     the ultimate timeout
   - After 30s shows "Still waiting…" with elapsed time
   - At 3min shows clear error: "Check if OpenClaw is running"

3. Live elapsed timer in typing indicator:
   - Shows seconds elapsed since message was sent (after 5s)
   - Updates every 5s while waiting
   - Properly cleaned up on response/error/page exit

4. Config compatibility check: no changes needed — our code already uses
   canonical config paths, no legacy aliases affected by 4.5 removal.

Files: chat.js, chat.css, locales/modules/chat.js
2026-04-07 16:15:17 +08:00
晴天
ad00ffef3d chore: release v0.11.5
feat: SkillHub skill store (SDK-based, no CLI dependency)
- Rust SDK (skillhub.rs): HTTP search, index fetch, zip download+extract
- Node.js SDK (skillhub-sdk.js): mirrors Rust SDK for Web/Docker mode
- Skills page: new "Store" tab with full index browse + client-side filter
- Remove 6 old CLI-dependent commands, add 3 SDK commands
- Migrate assistant.js skill tools from ClawHub CLI to SkillHub SDK
- Fix index decode error ({total,skills} wrapper vs bare array)
- Fix skill name display (API field 'name' vs 'display_name')
- Clean up 13 dead CSS rules from old skills hero/tips UI
2026-04-07 03:25:26 +08:00
晴天
9ff91f74d8 feat: IME-aware chat input, message copy button, Git path scanning
- Fix IME composition issue: Enter during Chinese/Japanese/Korean input
  method composition no longer prematurely sends messages (assistant.js)
  Uses e.isComposing + keyCode 229 guard on keydown handler
- Add one-click copy button to chat message bubbles (both chat.js and
  assistant.js), with hover-reveal animation and checkmark feedback
- Add 'copy' icon to SVG icon library (Lucide style)
- Add CSS for msg-copy-btn in chat.css and assistant.css
- Implement scan_git_paths Rust command: scans common Git installation
  locations on Windows/macOS/Linux (Program Files, Scoop, Chocolatey,
  GitHub Desktop, VS Code, MSYS2, Homebrew, Xcode CLT, etc.)
- Register scan_git_paths in lib.rs, tauri-api.js, dev-api.js
- Add scan button + results UI in settings.js Git path section
- Add i18n keys: gitScan, gitScanning, gitScanEmpty, gitScanUse
2026-04-06 00:14:18 +08:00
晴天
42aeb8b077 fix: Gateway status discrepancy — foreign detection, claim action, banner sync
- Services page: foreign Gateway now shows warning dot (yellow) instead of green
- Add claim_gateway command (Rust + dev-api + frontend API) to adopt foreign Gateway
- Services page: add "Claim Gateway" button for foreign Gateway instances
- Top banner: distinguish foreign Gateway (warning + claim) vs stopped (info + start)
- app-state: expose isGatewayForeign(), pass foreign flag to onGatewayChange listeners
- Services page actions now immediately sync global Gateway state (no 15s poll wait)
- Relax owner matching: cli_path missing on either side no longer triggers foreign
- Add i18n keys: foreignGatewayBanner, claimGateway, claimSuccess, claimFailed, processing
2026-04-05 23:12:16 +08:00
晴天
b2ab316353 fix(settings): support custom git path and robust skills bundled scanning 2026-04-05 21:33:35 +08:00
晴天
699cf1c071 fix(models): stop auto-populating fallbacks on every save (fixes #190) 2026-04-05 20:25:33 +08:00