Skip to content

feat(desktop): 模型选择器新增「最近」视图 - #4621

Open
zyjisdog wants to merge 4 commits into
makecindy:mainfrom
zyjisdog:cindy/inspiring-kalam
Open

zyjisdog wants to merge 4 commits into
makecindy:mainfrom
zyjisdog:cindy/inspiring-kalam

Conversation

@zyjisdog

@zyjisdog zyjisdog commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

这次改了什么

摘要

对话里选过模型后,把那一刻生效的整份配置(来源 + 模型 + 引擎 + 深度 + Fast)记为一条「最近」记录,并在模型选择器侧栏新增「最近」格(收藏之上,History 图标,常驻):点开是独立视图,按时间倒序陈列最近可路由的 5 条。常规的「全部 / 各供应商」列表不再出现最近区——上下叠放会与收藏产生视觉歧义。这里澄清一点:主干此前并没有最近入口 / 最近区,这是本功能对「历史怎么呈现」的一次性方案取舍,不是在修既有回归。

视图内的已收藏副本直接标星(先按来源 + 模型圈定条目,再比解析后的引擎 / 深度 / Fast),点星 = 把这份副本存进收藏 / 从收藏里移除;已标星的行点击与收藏行同构(带上那条收藏的 uid)。该页是只读流水账,不提供配置浮层(改配置走模型行或收藏)。

记录口径:只有对话入口(新任务草稿 / 已建任务 / 任务内切换)里「选择真的应用成功」才记;它表示选择已被接受并应用,不等于模型请求已经完成(与产品规则 §7 的选择 / 回复区分同一口径,不为记历史加探测或额外请求)。定时任务 / IM / Bot / Hook / Worker / 子代理 / 设置页不记;SSH 与 device-link 远程不记(被控端目录的模型在本机不一定可路由)——这只限定「写本机历史」,不限制远程选模与远程收藏。纯本机 localStorage,不上传,不与服务端同步。

顺带把收藏的归一化 / 去重身份抽成共享模块 state/modelConfigCopy.ts(最近与收藏共用一套,避免两处规则漂移)。

变更类型

  • feat 新功能
  • fix 缺陷修复
  • refactor / perf 重构或性能优化
  • docs / test / chore 文档、测试或工程维护
  • 其他:

范围

  • 关联 Issue / 需求:无编号(来自使用中的体验反馈);维护者确认门:维护者确认:#4621 模型选择器「最近」视图(产品 / 规则 / 大 diff) #4628;产品规则已写入 docs/product-rules/model-selector-unified.md §1.2 / §1.5 / §2.3
  • 本 PR 包含(当前 head 3ea87094c 口径:4 个提交 / 24 个文件 / +1844 −193):新增 state/recentModels.ts(本机流水 + 跨窗口 op-log 调和);新增 state/modelConfigCopy.ts 并让 modelFavorites.ts / providerModelMemory.ts 改用它;unifiedModelSelection.ts 的新锚点 / 新区;面板 / rail / 行接线;i18n 五语 3 个 key;单测 5 个文件;产品规则同步;另有一笔 test 提交把冲突消解时丢掉的上游 harness 菜单回归组原样恢复
  • 明确不包含:收藏体系的行为改造(只做等价抽取)、跨设备或跨账号同步、最近记录的容量 / 保留期设置 UI
  • 用户可见变化:模型选择器侧栏新增「最近」入口及其页面;「全部 / 各供应商」列表不再混入最近区
  • 是否存在 breaking change:无

UI 变化

  • 平台:Windows(Desktop,Electron renderer)
  • 截图 / 录屏:未附——本轮在远程被控端的 dev 实例上验证(pnpm restart:desktop:remote --region=cn --isolated=dev,实例 pid 43724),截图需在宿主侧目检;如需我补图请告知
  • 引用的设计规范:docs/design-rules/DESIGN.md §2 Color Palette & Roles(新增 UI 一律语义 token,无硬编码颜色)、§4 Border Radius Scale(星标 / 图标按钮沿用既有圆角分类,不新造档位)、§5 Layout Principles 与 docs/design-rules/design-governance.md §13(点击目标沿用 rail / 行内既有尺寸,不缩小命中区)、§10 Theme System & Token Reference 的 Light / Dark 双模式交付门槛(只用既有语义 token,两种模式同样生效)

怎么验证的

自动验证

pnpm --filter desktop run typecheck
结果:PASS(rebase 到 755166499 后重跑)

vitest related(官方 exclude 列表,覆盖本次 23 个改动文件;等价于 pnpm test:unit:related 的 desktop 部分)
结果:Test Files 634 passed / 2 failed(`bot-import/host.test.ts`、`maker-ipc/botInvitation.test.ts`),两者均与本 PR 无关:前者在 pristine `upstream/main` 上同样失败(预存在),后者单跑通过、属并发时序抖动。本 PR 触及的用例 294 条全绿(面板 167 + store 14 + 收藏 / 选模陈列)。

模型选择器面板用例(含本次新增 14 条)+ recentModels store(14 条)+ 收藏 / 选模陈列用例 + 上游 harness 菜单回归组(6 条)
结果:173(面板文件)+ 14(store)+ 其他,合计 294 passed

pnpm check:i18n
结果:五语 9629 key 全部一致

pnpm check:dco
结果:DCO check passed(3 个 commit 均带 Signed-off-by,基于 upstream/main)

新增用例覆盖:store 副本身份去重 / 容量 / 时钟回拨 / owner 分区 / 写失败基底并集 / 跨窗口调和与幂等 / 写失败兜底;最近只在 rail 视图陈列;rail 顺序(最近在收藏之上);星标跨型号不误命中(P0 回归);星标行点击带锚点(同引擎 + 跨引擎两条链路);最近行配置入口全关(不宣告 ArrowLeft、右键不 preventDefault、按 ← 不开浮层);取消「当前选中那份收藏」的回落口径;成功才记 / 失败不记 / 收藏行记归一化身份 / 选择在途切号不串写。

规则 ↔ 实现对照(维护者确认门要求;行号对齐当前 head 3ea87094c):

规则(§1.2 / §1.5 / §2.3) 实现
成功才记录;失败 / 取消 / 抛错不记 useUnifiedRowActions.ts:414(rememberRecentModel:applied !== false,async 结果同样收口)
记录时的数据归属 recentModels.ts:399 getRecentModelsOwner();useUnifiedRowActions.ts:423,425 发起时捕获、落地时比对,变了整条丢弃
完整配置身份(来源+模型+引擎+深度+Fast) modelConfigCopy.ts:64,88;recentModels.ts:99(sanitize 按副本身份去重)
只在 rail「最近」格陈列 / 常规列表不混入 unifiedModelSelection.ts:600(rail.kind === 'recent' 分支;常规视图不构建最近区)
目录可路由过滤(不可路由只隐藏不删) unifiedModelSelection.ts:604(resolveStoredEntry 不命中即 continue,从更早历史补足 5 条)
星标匹配(同条目 + 解析后三元组) UnifiedModelPanel.tsx:1175-1181(先 providerId + entryMatchesModelId,再比解析后引擎 / 深度 / Fast)
已标星行点击带收藏 uid(同收藏行) UnifiedModelPanel.tsx:1194,1233(starredFavorite → selectRow)
只读流水账(无配置浮层) UnifiedModelPanel.tsx:1213(customizeEnabled={!recentRow});UnifiedModelRow.tsx 四处按 customizeEnabled && configurationEnabled 收口
存储契约:本机 20 条 / 视图 5 条 / 分区 recentModels.ts:50(KEEP=20)、unifiedModelSelection.ts:420(LIMIT=5)、recentModels.ts 的 storageKey() 分区

共享抽取的等价性:modelConfigCopy.ts 的归一化 / 身份与收藏原先的实现逐条对齐(非法来源保留位、effort 合法集、缺省字段不落盘),modelFavorites.test.ts 全部旧用例未改动即通过(含同模型多副本、跨来源同名模型、编辑 / 取消 / 远程收藏);providerModelMemory.ts 改为 re-export 保留位常量,不改行为。

手工验证

  • dev 实例:pnpm restart:desktop:remote --region=cn --isolated=dev(沙箱 Cindy-dev2-dev,与正式版数据隔离),verdict=ready,renderer 走 HMR。截图基于 head 37c51721b;本轮 rebase 未触及面板 / 行 / rail / store 的渲染与交互语义(冲突全在上游新增的收藏接线、测试块与规则文档同段),故实机证据继续适用。
  • Dark 模式已实机目检(head 37c51721b):5 条上限与配置副本分行、星标态与点星、← 键 A/B(普通行开浮层 / 最近行不开)、来源失效只隐藏、空态 —— 截图与运行期探针(aria-keyshortcuts、keydown 投递日志、浮层节点存在性)见 PR 评论「实机目视证据(补 维护者确认:#4621 模型选择器「最近」视图(产品 / 规则 / 大 diff) #4628 里缺的 Dark)」,其中记录与星标是在真实 renderer 上用真实鼠标 / 键盘事件操作出来的;空态与幽灵来源两条为刻意构造(清键 / 注入不可路由记录,均已还原,评论里写明)。
  • Light 已实机对照:同一条状态切到浅色主题(设置 → 外观 → 浅色)截图,与 Dark 同口径。
  • 未做的目检:真机键盘 Tab 走查(焦点顺序)与移动端 / 触屏;超 5 条以外边界(如 20 条容量)未实机看。
  • 远程场景:本 PR 只保证「远程不写本机历史」(ChatInput.tsx 的 recordRecentUsage={!remoteHostId && deviceLinkDeviceId === null}),远程选模与远程收藏沿用既有链路、未改动;没有专门跑 device-link 端到端用例。

未执行的验证

  • pnpm test:unit:related 的 test:runner(非 desktop 部分)在本机 Windows shell 里有一个预存在失败:scripts/__tests__/pi-update-layout.test.mjs(tar: Cannot connect to C: resolve failed)。已在**未含本改动的 pristine upstream/main**上复现同一失败,判定与本 PR 无关;Linux CI 上的结果以流水线为准。
  • 上一轮 Windows unit tests (1/2) 曾挂在 main 进程的 pi-package-store-security.test.ts(20s 超时 + 超时后 exitCode 断言),重跑后全绿;该文件与本 PR 无关(本机 Windows 195 用例全过)。

与上游的合并处置(rebase 到 755166499,共两轮)

第一轮(→ 6ab1787c3,跨 957 个上游提交):4 处冲突,按「保留双方语义、以上游新形态为底」处置:

  1. UnifiedModelPanel.tsx(收藏接线):上游新增的远程收藏 useRemoteModelFavorites 与本 PR 的 useRecentModels 并存,两者共用 selectionPolicy === 'official' 的策略门。
  2. useUnifiedRowActions.ts(收藏 store 抽象):上游新增的 FavoriteStore 间接层与本 PR 的 rememberRecentModel 记录点并存。
  3. unifiedModelPanelRendering.test.tsx:两侧均为顶层 describe,并列保留(恢复后共 28 describe / 148 it)。首轮消解曾把上游的 harness configuration keeps the model menu open 组误替换成「最近」用例组(@greptileai P2),已按原样恢复于 09ab9ff6e —— 该组覆盖 ModelSelector 外层的菜单开关逻辑(success / cancel / error 三态下菜单与浮层保持打开、切回原引擎清待切换意图),最近视图用例只覆盖 ModelSelectorContent 的选择行为,两者不可互替。CI 不会报红(没测试断言某个 describe 必须存在),是对账发现的。
  4. model-selector-unified.md:上游同期改了同一段的分组排序细则(订阅账号 sortOrder 优先、账号周限额 38px 图标格等),与本 PR 的「最近视图」段并存——上游的账号排序细则与余量条目、本 PR 的最近视图段 / 星标例外 / 记录口径同时在位。

第二轮(→ 755166499,跨 302 个上游提交):本轮代码零冲突(面板 / 行动作 / 测试 / i18n 全部自动合并),唯一冲突是规则文档 §1.5 —— 上游同期把 Desktop 那行改写并新增两条 Mobile 收藏条款(用户确认交互原型,2026-10-02)。解法:以上游新版 Desktop 行为底,把「最近行例外」并回该行,Mobile 两条原样保留。

按上一轮教训,这轮 rebase 后跑了三道对账(缺一不可):

  1. 全仓扫残留冲突标记(上一轮 rerere 复用坏解法留下的 <<<<<<< 就是这样溜进来的)—— 干净。
  2. typecheck —— PASS;首次报错是上游新加依赖 @noble/curves 未装,pnpm install --frozen-lockfile 后消失,与本 PR 无关。
  3. 逐 describe / it 对账上游测试(脚本比对 upstream/main 与 HEAD 的 describe / it() 清单):unifiedModelPanelRendering 27→28 describe、129→145 it,上游用例 0 缺失;unifiedModelSelection 与 authContextSessionBoundary 各有 1 条标题差异,核对为我把标题扩展到最近语义(最近 / ★ 常驻…、favorites, recents and engine overrides…),用例本体与断言均在,总数不变。

related 门禁 675 个测试文件全绿(0 失败)。

另有一处 rerere 复用解法在新基线上不完整(浮层配置块残留旧冲突标记),已改为「取上游新版 optimistic 写入 + 补本 PR 的 ?? target.recent」并校正 rerere 记录;typecheck 与相关用例在修正后全绿。

风险

风险分类

  • 无已知风险
  • SQLite / migration
  • system prompt
  • 协议兼容
  • 权限 / 安全 / 用户数据:新增本机 localStorage 键 xdt:recentModels:v1:<dataOwnerId>(模型使用记录),按数据归属分区、不上传、不跨设备同步;异步落地前校验归属代次,避免跨账号串写
  • 存量插件兼容(批准状态 / 指纹 / manifest 校验 / 安装布局 / 包格式)
  • 原生层 / fingerprint / OTA
  • 跨平台差异
  • 其他:

影响与回滚

  • 影响范围:模型选择器 UI(侧栏多一格 + 独立视图)与本机偏好存储多一个键;不影响会话数据、协议、插件、更新链。记录上限 20 条、视图展示 5 条;数据按 dataOwnerId 分区,本地模式与云端账号互不可见;键损坏 / 越界条目在读取时丢弃,写盘失败静默吞(内存态仍生效,且下次写入以「磁盘 ∪ 内存」为基底,不丢未落盘记录)。
  • 与上游的兼容:本功能只新增 store、rail 项与锚点类型,不改动上游既有分支的行为;浮层配置的 optimistic 写入、FavoriteStore 远程收藏路径等上游新形态均原样保留(见「与上游的合并处置」)。
  • 回滚 / 降级方式:纯新增,旧版本读不到该键会直接忽略;revert 本 PR 即可,无需迁移或清数据(残留的 localStorage 键可手动删)。
  • 存量插件影响:无。

提交前检查

  • 已 review 完整 diff
  • 每个 commit 都带 DCO 签名(git commit -s,见 DCO)
  • UI 改动已在「UI 变化」注明引用的设计规范章节
  • 未提交凭证、令牌或授权文件
  • 已核对受影响的文档,行为变化涉及的旧结论已同步修订(docs/product-rules/model-selector-unified.md §1.2 / §1.5 / §2.3)
  • 已确认测试结果或说明未执行原因

@zyjisdog
zyjisdog requested a review from a team as a code owner September 17, 2026 10:03
@greptile-apps

greptile-apps Bot commented Sep 17, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium risk] Adds a recent models view to the model selector.

就本次 PR 的变更及既有评审意见而言,未发现阻碍合并的问题。

Summary

本 PR 为 Desktop 模型选择器新增独立的「最近」视图,在本机按数据归属保存对话中成功应用的完整模型配置,并复用收藏的配置副本规则。

  • 最近记录不混入常规模型列表;可路由记录最多展示 5 条,并支持按配置副本标星。
  • 已核对六条既有 Greptile 线程:对应修复仍在,丢失的菜单回归测试已恢复;这些线程均已解决。
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[对话中选择模型] --> B{选择成功且归属未变?}
  B -- 是 --> C[按归属保存配置副本]
  B -- 否 --> D[不记录]
  C --> E[最近视图筛选可路由记录]
  E --> F[选择副本或切换星标]
Loading

Reviews (7) · Last reviewed commit: "test(desktop): 恢复冲突消解时丢掉的上游 harness 菜单回归..."

Comment thread apps/desktop/src/renderer/components/new-chat/UnifiedModelRow.tsx Outdated
Comment thread apps/desktop/src/renderer/state/recentModels.ts
Comment thread apps/desktop/src/renderer/__tests__/recentModels.test.ts
Comment thread docs/product-rules/model-selector-unified.md Outdated
zyjisdog added a commit to zyjisdog/cindy that referenced this pull request Sep 17, 2026
对应上游 PR makecindy#4621 的 Greptile 评审(P1×1 + P2×4):

- P1 跨账号串写:选择是异步的(跨引擎事务 / 远程写),回调落地时用户可能已登出 / 切本地
  模式 —— 归属代次改在**发起选择时**捕获,落地时不一致就整条丢弃,绝不写进新分区
  (recentModels 导出 getRecentModelsOwner,useUnifiedRowActions 两处比对)
- P2 无响应的自定义入口:customizeEnabled 不能替换 configurationEnabled —— 设置类入口
  (configurationEnabled=false)继续不出自定义按钮 / 不宣告 ArrowLeft / 不抢右键,行内四个
  入口一并按两个开关收口
- P2 写失败后的写基底:commitOp 基底改为「磁盘真相 ∪ 本窗内存态」—— 上一次写失败(配额 /
  私密窗口)只活在内存里的记录,不会被下次 freshState() 读到的磁盘旧快照抹掉
- P2 调和路径的测试覆盖:新增 navigator.locks 下的迟到覆盖用例(锁内重放 op-log 把本窗记录
  重新断言、重复调和幂等不活锁)与切 owner 后旧分区仍按原 key 调和的用例
- P2 文档与注释:§1.2 分组顺序改回「收藏 → 推荐 → 供应商分组」(最近只在侧栏「最近」格单开
  一页,不参与常规列表),unifiedModelSelection 最近分支的注释同步为「配置副本」语义

验证:pnpm --filter desktop run typecheck;related 524 个测试文件全绿;新增用例中「写失败后再写
成功」「选择在途切换归属」两条已用 git stash 验证在前代码上必失败(真回归保护)

Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
@zyjisdog

Copy link
Copy Markdown
Contributor Author

@greptileai review

@zyjisdog

Copy link
Copy Markdown
Contributor Author

@codex review

@zyjisdog zyjisdog closed this Sep 17, 2026
@zyjisdog zyjisdog reopened this Sep 17, 2026
@zyjisdog

Copy link
Copy Markdown
Contributor Author

CI 说明:上一轮 Windows unit tests (1/2) 挂在 main 进程的 src/main/maker-host/__tests__/pi-package-store-security.test.ts 两个用例(20s 超时 + 超时后的 exitCode 期望 1 实得 null)。判断为运行器抖动而非回归:① 同一 job 在 ae22ec1 那轮是绿的;② 该文件在本机 Windows 上 195 个用例全过;③ 本 PR 只改 renderer 与文档,没有触碰该文件及其实现。因无 base 仓 rerun 权限,用 close/reopen 重触发了一轮,现已全绿。

@MagicLizi

Copy link
Copy Markdown
Contributor

本 PR 触发维护者确认门(当前拦截类别:产品 / UI;同时还有规则文档与大 diff)。

改动:模型选择器新增「最近」独立视图与本机最近配置流水,并更新产品规则。作者不是维护者,当前 head 尚无维护者 Approve。

请维护者直接在本 PR 上 Approve 放行讨论门;需要作者修改则 Request Changes。普通评论或摘标签不构成通过。

讨论 issue:#4628

@MagicLizi MagicLizi added awaiting-discussion 等待维护者讨论(review-pr) touches:core 改动碰到架构核心路径(review-pr 自动维护,仅展示) touches:large-diff 改动量较大(review-pr 自动维护,仅展示) touches:product-ui 改动碰到产品 / UI 面(review-pr 自动维护,仅展示) touches:rules 改动碰到规则 / 规范文档(review-pr 自动维护,仅展示) labels Sep 17, 2026
zyjisdog added a commit to zyjisdog/cindy that referenced this pull request Sep 17, 2026
对应上游 PR makecindy#4621 的 Greptile 评审(P1×1 + P2×4):

- P1 跨账号串写:选择是异步的(跨引擎事务 / 远程写),回调落地时用户可能已登出 / 切本地
  模式 —— 归属代次改在**发起选择时**捕获,落地时不一致就整条丢弃,绝不写进新分区
  (recentModels 导出 getRecentModelsOwner,useUnifiedRowActions 两处比对)
- P2 无响应的自定义入口:customizeEnabled 不能替换 configurationEnabled —— 设置类入口
  (configurationEnabled=false)继续不出自定义按钮 / 不宣告 ArrowLeft / 不抢右键,行内四个
  入口一并按两个开关收口
- P2 写失败后的写基底:commitOp 基底改为「磁盘真相 ∪ 本窗内存态」—— 上一次写失败(配额 /
  私密窗口)只活在内存里的记录,不会被下次 freshState() 读到的磁盘旧快照抹掉
- P2 调和路径的测试覆盖:新增 navigator.locks 下的迟到覆盖用例(锁内重放 op-log 把本窗记录
  重新断言、重复调和幂等不活锁)与切 owner 后旧分区仍按原 key 调和的用例
- P2 文档与注释:§1.2 分组顺序改回「收藏 → 推荐 → 供应商分组」(最近只在侧栏「最近」格单开
  一页,不参与常规列表),unifiedModelSelection 最近分支的注释同步为「配置副本」语义

验证:pnpm --filter desktop run typecheck;related 524 个测试文件全绿;新增用例中「写失败后再写
成功」「选择在途切换归属」两条已用 git stash 验证在前代码上必失败(真回归保护)

Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
@zyjisdog
zyjisdog force-pushed the cindy/inspiring-kalam branch from 9b5475c to 37c5172 Compare September 17, 2026 13:12
@zyjisdog

Copy link
Copy Markdown
Contributor Author

按 #4628 的清单做了一轮收口,head 现在到 37c51721b:

合并冲突已解(rebase 到 a15a240)

  • UnifiedModelPanel.tsx:上游新增的远程收藏(useRemoteModelFavorites)与本次的 useRecentModels 并存。
  • useUnifiedRowActions.ts:上游新增的 FavoriteStore 抽象与本 PR 的 rememberRecentModel 记录点并存。
  • 语义冲突一处:上游新加代码引用了本地 helper identityOf,而本 PR 已把它抽成共享的 modelConfigCopyIdentity —— 已改到共享实现(否则 typecheck 不过)。
  • rebase 后重跑:typecheck PASS;related desktop 530 个测试文件全绿。

证据补齐(正文已更新为最终口径:3 个提交 / 24 个文件 / +1841 −190)

  • 规则 ↔ 实现对照表进了正文「自动验证」段(成功才记、归属代次、完整配置身份、rail 专属陈列、目录可路由过滤、星标匹配、锚点传递、只读无浮层、存储契约)。
  • 共享抽取等价性:modelConfigCopy.ts 与收藏原实现逐条对齐;modelFavorites.test.ts 旧用例零改动通过(含同模型多副本、跨来源同名模型、编辑 / 取消 / 远程收藏)。
  • 文档按 维护者确认:#4621 模型选择器「最近」视图(产品 / 规则 / 大 diff) #4628 第 4 点与「记录口径」建议收口(提交 37c51721b):§1.5 把最近行的星标写成显式例外(普通模型行与收藏行的单向「添加副本」契约不变);§1.2 写明最近 = 选择已被接受并应用,不等于模型请求已完成,不为记历史加探测或额外请求;远程不写本机历史只限定历史写入,不限制远程选模与远程收藏。

仍未完成的验证(如实标注)

@zyjisdog

Copy link
Copy Markdown
Contributor Author

实机目视证据(补 #4628 里缺的 Dark)

环境:dev 实例(pnpm restart:desktop:remote --region=cn --isolated=dev,沙箱 Cindy-dev2-dev,CN 区域,renderer 走 HMR,head = 37c51721b)。窗口 1440×920 CSS px。截图为 Page.captureScreenshot 的原样输出,只裁了窗口区域、没有拼图或后期绘制。

怎么驱动的:全部是真实 renderer 上的真实输入事件(CDP Input.dispatchMouseEvent / Input.dispatchKeyEvent)——选模型、点星、按 ← 都是真点真按;不是照着设计稿摆的假图。

Dark(这次补的缺失项)

1. 有记录:5 条上限 / 星标态 / 同模型不同配置各占一行 / provider 来源

dark recent 5 rows

  • 记录是真选出来的(先点选 3 个,再逐个选 GLM / Muse / …),当时键里共 7 条 → 视图按 usedAt 倒序只陈列 5 条(UNIFIED_RECENT_MODELS_LIMIT = 5,本机保留 20 条)。
  • DeepSeek V4.1 Flash 出现两行(超高 / 中):同模型不同配置各占一行,星标各自独立 —— 这正是「最近 = 配置副本」。
  • 每行显示来源 CommandCode 与解析后的 π 超高(引擎 + 深度);第 1 行蓝框 = 当前生效的那份。

2. 点星(真点「存为收藏」→ 该行变已收藏)

前(GLM 未收藏,星为空心)→ 后(星变实心):

before
after

点击的是行内星标按钮(aria-label 由 存为收藏 → 取消收藏),点击后该副本进入收藏区,点星语义与收藏视图同一口径。

3. ← 键 A/B:普通模型行开配置浮层;最近行不开

普通模型行(model::commandcode::z-ai/glm-5.3-flash)聚焦后按 ← → 浮层打开:

normal row arrowleft opens flyout

最近行(recent::commandcode z-ai/glm-5.3-flash pi medium 0)聚焦后按 ← → 什么都不发生(蓝框仍在行上,浮层不出现):

recent row arrowleft no flyout

运行期同时核过(不是只看图):

  • 最近行 没有 aria-keyshortcuts 属性;普通模型行是 "ArrowLeft"。
  • keydown 确实投递到了最近行(capture 监听日志:[["ArrowLeft","recent::commandcode z-ai/glm-5.3-flash pi medium 0"]]),只是不做事 —— 排除了「事件根本没到」这种假阴性。
  • 该视图里可见的 [role=group] 只有面板自身与 rail,没有配置浮层节点。

4. 来源失效只隐藏不删

invalid provider hidden

存储里 6 条、其中 1 条来源不存在(这条是刻意构造的:往 dev 沙箱注入了 providerId: 'ghost-provider' 的记录,用来验「不可路由只隐藏不删」),视图仍陈列 5 条真实记录、幽灵那条不出现;存储里的 6 条还在。

5. 空态(首日为空,不回填)

dark empty

(构造方式:清掉 dev 沙箱的 xdt:recentModels:v1:local-v1 后重载;随后已把 5 条真实记录还原。)

Light 对照(同一条状态)

light recent 5 rows

经 设置 → 外观 → 浅色 的实机操作切过去,与 Dark 的第 1 张对照:同一份语义 token,两种模式都正常(星标色 / 选中底 / 分隔线都可见)。

仍未覆盖

  • 真机键盘 Tab 走查(焦点顺序)与移动端 / 触屏未做。
  • 远程(device-link)不写本机历史目前只有代码路径(ChatInput.tsx 的 recordRecentUsage={!remoteHostId && deviceLinkDeviceId === null})+ 单测,没有端到端录屏。
  • 图片放在 fork 的 assets/recent-models-evidence 分支(只作证据,不进本 PR 的 diff)。

@zyjisdog

Copy link
Copy Markdown
Contributor Author

@greptileai review

head 已更新到 37c51721b(rebase 到 a15a240:与上游的远程收藏 / FavoriteStore 冲突已解;modelFavorites.ts 里上游新代码引用的旧本地 helper identityOf 已改到共享的 modelConfigCopyIdentity)。相对你上次审的版本,代码改动集中在:冲突消解一处、文档口径两处(§1.2 记录口径、§1.5 最近行星标例外),另补了 Dark/Light 实机证据。麻烦再扫一遍。

zyjisdog added a commit to zyjisdog/cindy that referenced this pull request Sep 19, 2026
Upstream-PR: makecindy#4621
Upstream-PR-Head: 37c5172
Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
zyjisdog added a commit to zyjisdog/cindy that referenced this pull request Sep 19, 2026
Upstream-PR: makecindy#4621
Upstream-PR-Head: 37c5172
Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
zyjisdog added a commit to zyjisdog/cindy that referenced this pull request Sep 21, 2026
Upstream-PR: makecindy#4621
Upstream-PR-Head: 37c5172
Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
zyjisdog added a commit to zyjisdog/cindy that referenced this pull request Sep 21, 2026
Upstream-PR: makecindy#4621
Upstream-PR-Head: 37c5172
Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
zyjisdog added a commit to zyjisdog/cindy that referenced this pull request Sep 21, 2026
Upstream-PR: makecindy#4621
Upstream-PR-Head: 37c5172
Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
zyjisdog added a commit to zyjisdog/cindy that referenced this pull request Sep 22, 2026
Upstream-PR: makecindy#4621
Upstream-PR-Head: 37c5172
Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
zyjisdog added a commit to zyjisdog/cindy that referenced this pull request Sep 22, 2026
Upstream-PR: makecindy#4621
Upstream-PR-Head: 37c5172
Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
zyjisdog added a commit to zyjisdog/cindy that referenced this pull request Sep 23, 2026
Upstream-PR: makecindy#4621
Upstream-PR-Head: 37c5172
Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
zyjisdog added a commit to zyjisdog/cindy that referenced this pull request Sep 27, 2026
Upstream-PR: makecindy#4621
Upstream-PR-Head: 37c5172
Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
zyjisdog added a commit to zyjisdog/cindy that referenced this pull request Sep 28, 2026
Upstream-PR: makecindy#4621
Upstream-PR-Head: 37c5172
Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
zyjisdog added a commit to zyjisdog/cindy that referenced this pull request Sep 29, 2026
Upstream-PR: makecindy#4621
Upstream-PR-Head: 37c5172
Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
zyjisdog added a commit to zyjisdog/cindy that referenced this pull request Sep 30, 2026
对应上游 PR makecindy#4621 的 Greptile 评审(P1×1 + P2×4):

- P1 跨账号串写:选择是异步的(跨引擎事务 / 远程写),回调落地时用户可能已登出 / 切本地
  模式 —— 归属代次改在**发起选择时**捕获,落地时不一致就整条丢弃,绝不写进新分区
  (recentModels 导出 getRecentModelsOwner,useUnifiedRowActions 两处比对)
- P2 无响应的自定义入口:customizeEnabled 不能替换 configurationEnabled —— 设置类入口
  (configurationEnabled=false)继续不出自定义按钮 / 不宣告 ArrowLeft / 不抢右键,行内四个
  入口一并按两个开关收口
- P2 写失败后的写基底:commitOp 基底改为「磁盘真相 ∪ 本窗内存态」—— 上一次写失败(配额 /
  私密窗口)只活在内存里的记录,不会被下次 freshState() 读到的磁盘旧快照抹掉
- P2 调和路径的测试覆盖:新增 navigator.locks 下的迟到覆盖用例(锁内重放 op-log 把本窗记录
  重新断言、重复调和幂等不活锁)与切 owner 后旧分区仍按原 key 调和的用例
- P2 文档与注释:§1.2 分组顺序改回「收藏 → 推荐 → 供应商分组」(最近只在侧栏「最近」格单开
  一页,不参与常规列表),unifiedModelSelection 最近分支的注释同步为「配置副本」语义

验证:pnpm --filter desktop run typecheck;related 524 个测试文件全绿;新增用例中「写失败后再写
成功」「选择在途切换归属」两条已用 git stash 验证在前代码上必失败(真回归保护)

Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
@zyjisdog
zyjisdog force-pushed the cindy/inspiring-kalam branch from 37c5172 to e9c03ad Compare September 30, 2026 16:48
@zyjisdog

Copy link
Copy Markdown
Contributor Author

上游又前进了 957 个提交,已 rebase 到 6ab1787c3,head 现为 e9c03ad41(3 commits / 24 files / +1836 −242),冲突已全部消解,状态 MERGEABLE。

四处冲突的处置(原则:保留双方语义,以上游新形态为底)

  1. UnifiedModelPanel.tsx 收藏接线:上游的远程收藏 useRemoteModelFavorites 与本 PR 的 useRecentModels 并存,共用 selectionPolicy === 'official' 策略门。
  2. useUnifiedRowActions.ts:上游的 FavoriteStore 间接层与本 PR 的 rememberRecentModel 记录点并存。
  3. unifiedModelPanelRendering.test.tsx:上游新增 harness 用例块与本 PR「最近」用例块均为顶层 describe,整块并存(27 describe / 141 it)。
  4. model-selector-unified.md:上游同期改了同一段(订阅账号 sortOrder 优先、账号周限额 38px 图标格),与本 PR 的「最近视图」段并存——上游排序细则与余量条目、本 PR 的最近视图段 / 星标例外 / 记录口径同时在位。

一处值得说的坑:rerere 复用的旧解法在新基线上不完整(浮层配置块残留了上轮冲突标记,直接 typecheck 报语法错)。已改为「取上游新版 optimistic 写入 + 补本 PR 的 ?? target.recent」,并校正了 rerere 记录,后续同步不会再复用坏解法。

验证(重跑):typecheck PASS;本 PR 触及的 294 条用例全绿;related 门禁 634 passed / 2 failed,两个失败文件(bot-import/host.test.ts、maker-ipc/botInvitation.test.ts)与本 PR 无关——前者在 pristine upstream/main 上同样失败,后者单跑通过、属并发抖动。文档、代码与实机证据的口径未变(冲突全在上游新增的收藏接线、测试块与规则文档同段),故上一轮的 Dark/Light 实机截图继续适用,正文已同步更新数字与对照表行号。

@zyjisdog

Copy link
Copy Markdown
Contributor Author

本轮 CI 红了 2 个 check,根因只有一个,且都与本 PR 无关(证据如下):

唯一失败用例:src/renderer/__tests__/githubSetup.test.tsx > GitHub setup UI > recovers polling after a transient status failure without restarting authorization(Linux 分片 2/2;该分片 1563 文件过 / 1 挂)。

判定与本 PR 无关的三条证据:

  1. 不在改动面:githubSetup.test.tsx 不在本 PR 的 24 个文件里,本 PR 只动模型选择器 / renderer state / i18n / 规则文档。
  2. 本地通过:单独跑该文件 Test Files 1 passed。
  3. 该用例本身是计时敏感型:它靠真实计时的 findByText(..., { timeout: 2000 }) 等授权轮询从一次瞬时失败里恢复,CI 上机器一慢就抖。旁证:上游主干自己在 2026-09-30 的 client-ci(run 36659863362)也红过一次,挂在 lifecycle.test.ts > runQuitDisposers 的计时断言(expected 94 to be less than 90)—— 同一类计时抖动,出在主干、与任何 PR 无关。

verify 是连带失败,不是独立问题:它的步骤就是 test "$LINUX_UNIT_SHARDS_RESULT" = "success",Linux 分片红了它就跟着红。

因 fork 作者无 rerun 权限,按本 PR 之前的做法 close/reopen 重触发流水线,不塞空提交、不改历史。

@zyjisdog zyjisdog closed this Sep 30, 2026
@zyjisdog zyjisdog reopened this Sep 30, 2026
@zyjisdog

Copy link
Copy Markdown
Contributor Author

重触发后最新一轮 CI 全绿(11/11:DCO、design-basis、verify-checks、Linux 1/2 + 2/2、Windows 1/2 + 2/2、Windows unit tests、Desktop Git integration、verify、Greptile Review)。上一条评论里那两个失败项已在本轮通过,rollup 里仍可见的 FAILURE 是重触发前的旧 run 残留,以最新一次为准。

当前:head e9c03ad41(rebase 到 6ab1787c3)、3 commits / 24 files / +1836 −242、状态 MERGEABLE,只等维护者在 PR 上 Approve 放行 #4628 的确认门(产品取舍仍由维护者定:入口是否独立、记录范围、数据边界、最近行星标例外)。

@zyjisdog

zyjisdog commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai review

处置了你这轮的 P2(跨引擎菜单回归测试丢失,已属实并修复于 09ab9ff6e):把上游的 harness configuration keeps the model menu open 组原样恢复、与最近视图用例并列(describe 27 → 28),本文件 173 passed,typecheck PASS,线程内已逐条回复取证。head 现为 09ab9ff6e,麻烦再扫一遍。

zyjisdog added a commit to zyjisdog/cindy that referenced this pull request Oct 1, 2026
Upstream-PR: makecindy#4621
Upstream-PR-Head: 09ab9ff
Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
- 新增 recentModels store(localStorage、dataOwnerId 分区、同步写 + 会话 op-log
  跨窗口调和):记录对话里用过的**配置副本**(模型 + 引擎 + 深度 + Fast + usedAt),
  与收藏共用同一份归一化 / 身份规则(新增 modelConfigCopy;providerModelMemory 的
  保留位常量一并落位,避免纯逻辑层反向依赖 store),本机保留 20 条
- rail 在收藏之上新增「最近」格(History 图标,常驻);最近只在侧栏「最近」格打开的
  独立视图陈列 —— 常规的「全部 / 各供应商」不再混入,避免与收藏上下叠放的视觉歧义;
  视图取最近可路由的 5 条,行与收藏一致(L2 显 provider 来源)
- 已收藏的副本在该页标星:先按来源 + 模型圈定同一条目,再比解析后的引擎 / 深度 / Fast
  一致即点亮,点星把这份副本存进收藏 / 从收藏里移除;已标星的行点击与收藏行同构(带上
  命中收藏的 uid);该页是只读流水账,不提供配置浮层 —— 行内不绑右键 / ←、不宣告
  ArrowLeft 快捷键、右键不抢原生菜单(UnifiedModelRow 的 customizeEnabled)
- 记录点:对话入口(新任务草稿 / 已建任务 / 任务内切换)选择真的应用成功才记整份配置;
  SSH / device-link 与非对话入口不记;不可路由只隐藏不删记录;首日为空不回填
- i18n 五语新增 railRecent / recentGroup / recentEmpty;产品规格 §1.2 / §2.3 同步
- 单测:store 副本身份去重与容量、最近视图陈列、rail 顺序与过滤、星标匹配与取消(含
  跨型号不误命中)、选中锚点、配置入口全关、成功才记、删「当前选中收藏」的回落口径

独立对抗评审(Muse Spark 1.3 Contributor)发现的 P0×1 / P1×3 已全部收口并在本次提交内
修复:跨型号误删收藏、右键吞原生菜单与假快捷键、星标行点击丢锚点、取消收藏回落口径写明。

验证:pnpm test:unit:related(desktop PASS)、pnpm --filter desktop run typecheck、
pnpm check:i18n 与 check:i18n-glossary 通过

Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
对应上游 PR makecindy#4621 的 Greptile 评审(P1×1 + P2×4):

- P1 跨账号串写:选择是异步的(跨引擎事务 / 远程写),回调落地时用户可能已登出 / 切本地
  模式 —— 归属代次改在**发起选择时**捕获,落地时不一致就整条丢弃,绝不写进新分区
  (recentModels 导出 getRecentModelsOwner,useUnifiedRowActions 两处比对)
- P2 无响应的自定义入口:customizeEnabled 不能替换 configurationEnabled —— 设置类入口
  (configurationEnabled=false)继续不出自定义按钮 / 不宣告 ArrowLeft / 不抢右键,行内四个
  入口一并按两个开关收口
- P2 写失败后的写基底:commitOp 基底改为「磁盘真相 ∪ 本窗内存态」—— 上一次写失败(配额 /
  私密窗口)只活在内存里的记录,不会被下次 freshState() 读到的磁盘旧快照抹掉
- P2 调和路径的测试覆盖:新增 navigator.locks 下的迟到覆盖用例(锁内重放 op-log 把本窗记录
  重新断言、重复调和幂等不活锁)与切 owner 后旧分区仍按原 key 调和的用例
- P2 文档与注释:§1.2 分组顺序改回「收藏 → 推荐 → 供应商分组」(最近只在侧栏「最近」格单开
  一页,不参与常规列表),unifiedModelSelection 最近分支的注释同步为「配置副本」语义

验证:pnpm --filter desktop run typecheck;related 524 个测试文件全绿;新增用例中「写失败后再写
成功」「选择在途切换归属」两条已用 git stash 验证在前代码上必失败(真回归保护)

Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
- §1.2 记录口径:写明最近记的是「选择已被接受并应用」,不等于模型请求已完成(与 §7 同一
  区分,不为记历史加收费探测或额外请求);远程不写本机历史只限定历史写入,不限制远程选模
  与远程收藏
- §1.5 星标契约:把最近行的星标写成**显式例外**(按同一条目已匹配的收藏亮/灭、点星=删那条
  收藏,删除口径与收藏视图一致),普通模型行与收藏行的单向添加契约不变

Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
上一轮 rebase 解决 `unifiedModelPanelRendering.test.tsx` 冲突时,上游新增的
`harness configuration keeps the model menu open` 组被本 PR 的「最近」用例组
**替换**而非并存 —— 少的是 success / cancel / error 三态下「模型菜单与配置浮层
保持打开」,以及切回原引擎经同一事务清掉待切换意图的回归保护(@greptileai P2)。

按原样恢复该组,与最近视图用例并列(describe 27 -> 28)。该组覆盖 `ModelSelector`
外层的菜单开关逻辑,最近视图用例只覆盖 `ModelSelectorContent` 的选择行为,两者
不可互相替代,所以补回而不是改写。

验证:本文件 173 passed(原 167 + 恢复的 6 条);`pnpm --filter desktop run
typecheck` PASS。

Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
@zyjisdog
zyjisdog force-pushed the cindy/inspiring-kalam branch from 09ab9ff to 3ea8709 Compare October 5, 2026 00:23
@zyjisdog

zyjisdog commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

上游又前进 302 个提交,已 rebase 到 755166499,head 现为 3ea87094c(4 commits / 24 files / +1844 −193),冲突已消解,MERGEABLE。

本轮冲突只有 1 处:docs/product-rules/model-selector-unified.md §1.5 —— 上游同期把 Desktop 那行改写、并新增两条 Mobile 收藏条款(用户确认交互原型,2026-10-02)。解法:以上游新版 Desktop 行为底,把「最近行例外」并回该行,Mobile 两条原样保留。代码零冲突(面板 / 行动作 / 测试 / i18n 全部自动合并)。

按上一轮的教训,这轮 rebase 后补跑了三道对账(上一轮就是靠 rerere 复用坏解法 + 误删上游测试组才出的问题):

  1. 全仓扫残留冲突标记 —— 干净(上一轮那次是 <<<<<<< 混进文件、typecheck 才报出来)。
  2. typecheck —— PASS;首次报错是上游新加依赖 @noble/curves 未装,pnpm install --frozen-lockfile 后消失,与本 PR 无关。
  3. 逐 describe / it 对账上游测试(脚本比对 upstream/main 与 HEAD 的清单):面板测试 27→28 describe、129→145 it,上游用例 0 缺失;另外两个文件各有 1 条标题差异,核对为我把标题扩展到最近语义(最近 / ★ 常驻…、favorites, recents and engine overrides…),用例本体与断言都在,总数不变 —— 不是丢失。

验证:本 PR 触及的 6 个文件 340 passed;related 门禁 675 个测试文件全绿(0 失败)。实机证据口径未变(冲突仍在规则文档同段,未触及最近视图的渲染与交互语义),上一轮的 Dark/Light 截图继续适用。正文已同步新基线、数字与对照表行号。

@zyjisdog

zyjisdog commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

收尾:head 3ea87094c 上 CI 10/10 全绿(DCO、design-basis、verify-checks、verify、Linux 1/2 + 2/2、Windows 1/2 + 2/2、Windows unit、Desktop Git integration);6 个 review 线程全部 resolved、无未处理意见;状态 MERGEABLE / BLOCKED —— 只等维护者在 PR 上 Approve 放行 #4628 的确认门(产品取舍:入口是否独立、记录范围、数据边界、最近行星标例外)。

zyjisdog added a commit to zyjisdog/cindy that referenced this pull request Oct 5, 2026
Upstream-PR: makecindy#4621
Upstream-PR-Head: 3ea8709
Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
@zyjisdog

zyjisdog commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting-discussion 等待维护者讨论(review-pr) touches:core 改动碰到架构核心路径(review-pr 自动维护,仅展示) touches:large-diff 改动量较大(review-pr 自动维护,仅展示) touches:product-ui 改动碰到产品 / UI 面(review-pr 自动维护,仅展示) touches:rules 改动碰到规则 / 规范文档(review-pr 自动维护,仅展示)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants