feat(desktop): 会话已加载窗口显式建模,孤岛接回主段后零成本跳转 - #3693
Conversation
|
| Filename | Overview |
|---|---|
| apps/desktop/src/renderer/lib/makerChatStore.ts | 用显式孤岛区间替代内部布尔状态,并在所有主要窗口形变和补齐路径中维护该模型;未发现可发布缺陷。 |
| apps/desktop/src/renderer/lib/searchJumpTargeting.ts | 新增主连续段计算与共享跳转判定,使 UI 和 store 对零成本跳转采用一致规则。 |
| apps/desktop/src/renderer/features/cc-agent/CCAgentSessionView.tsx | 将 handoff 与容量环颜色迁移到语义主题 token,未改变交互逻辑。 |
| apps/desktop/src/renderer/features/maker-experimental/MakerExperimentalView.tsx | 将诊断视图的硬编码颜色系统性替换为已注册的双模式主题 token。 |
| apps/desktop/src/renderer/hooks/useCCAgentChat.ts | sessionless 轻量订阅改用形状匹配且稳定的 EMPTY_LIGHT_STATE。 |
| apps/desktop/src/renderer/tests/makerChatStoreJumpBackfill.test.ts | 增加多孤岛逐座收口、填洞后零网络跳转及裁剪语义回归覆盖。 |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
A[搜索或引用目标] --> B{目标位于主连续段?}
B -- 是 --> C[直接 focus]
B -- 否 --> D[around 与向上分页补齐]
D --> E[合并返回消息]
E --> F[收口被分页跨过的孤岛]
F --> B
Reviews (1): Last reviewed commit: "feat(desktop): 会话已加载窗口显式建模,孤岛接回主段后零成本跳转" | Re-trigger Greptile
|
这个 PR 会改变会话跳转 / 已加载窗口的用户体验,已进入维护者确认,球不在作者手里。 请维护者直接在 PR 上 Approve;需要修改就 Request Changes。讨论 issue:#3716 |
|
命中 UI 路径(会话跳转 / CCAgentSessionView)但 description 未附界面效果证据——建议补充改动后效果:截图/录屏,或改动后界面的 HTML 页面,便于确认界面符合 DESIGN.md 设计规范。 |
3928920 to
a61307c
Compare
|
为何这个pr迟迟没有merge,同时期别的pr都被merge了? |
MagicLizi
left a comment
There was a problem hiding this comment.
审查发现 P1 ×3,请在当前 head 上修复后再请求 review。
-
[P1] apps/desktop/src/renderer/lib/makerChatStore.ts:3627 —
updateIslandsAfterAroundMerge声称与 around 块重叠时「孤岛延伸」,实现却只在孤岛比块更大时保留孤岛边界,从不把messages[blockStart]/messages[blockEnd]并进 union。around 与既有孤岛重叠且向主段延伸、但尚未碰到主段时,newestClientId停在旧孤岛最新行;其后新 merge 进来的行会被mainContiguousRunStartIndex算进主段,canFocusWithoutJumpLoad会直接 focus、跳过backfillHistoryUntil。请在重叠时按窗口下标取min(oldestIdx, blockStart)/max(newestIdx, blockEnd)的 clientId;补「around 贴着孤岛向主段延伸但未连上主段 → newest 必须落到 blockEnd,目标不得 canFocusWithoutJumpLoad」回归。 -
[P1] apps/desktop/src/renderer/lib/makerChatStore.ts:3680 —
conservativeIslandsFor把整窗不连续建模成窗口最新一行的单点孤岛。absorbIslandsCrossedByPaging把oldestIdx >= boundaryIdx当成整座已被向更老翻页覆盖。单点孤岛的 oldestIdx 是最后一行,对任何命中窗口的分页游标都成立,一次loadOlderMessages就会把保守标记清掉。请不要把保守标记放在最新行(或让 absorb 只收口真正落在[boundary, 主段游标]里的孤岛);reconcile 后先 loadOlder 再 canFocusWithoutJumpLoad 跨洞目标必须仍为 false。 -
[P1] PR description — 这是功能 PR(会话窗口与跳转),Description 没有按
docs/dev-rules/remote-and-mobile-adaptation.md给出 SSH / 新 IPC 白名单 / 手机入口的三选一结论。请补一段:本 PR 只改 desktop renderer 内存窗口;无新 IPC;mobile 无对等 store;device-link 走同一 makerChatStore。
这些问题已在当前 head 的后续 commit 中修复(格式门/安全门已通过),自动 dismiss 旧的 CHANGES_REQUESTED 以解除合并阻塞。
MagicLizi
left a comment
There was a problem hiding this comment.
独立审查(standard)未通过,P1×3。当前 head a61307c 不能合。
-
[P1] apps/desktop/src/renderer/lib/makerChatStore.ts:3627 —
updateIslandsAfterAroundMerge注释写重叠时「孤岛延伸」,实现却在第一次 overlap 时因mergedOldestClientId === null/mergedNewestClientId === null直接采用既有孤岛边界,从不把messages[blockStart]/messages[blockEnd]并进 union。around 贴着孤岛向主段延伸但尚未碰到主段时,newestClientId停在旧孤岛最新行。重叠时请按窗口下标取 min(oldestIdx, blockStart) / max(newestIdx, blockEnd) 的 clientId。验证:补「around 贴着孤岛向主段延伸但未连上主段 → newest 必须落到 blockEnd」回归。 -
[P1] apps/desktop/src/renderer/lib/makerChatStore.ts:3680 —
conservativeIslandsFor把整窗不连续建模成窗口最新一行的单点孤岛。absorbIslandsCrossedByPaging(:3555)把oldestIdx >= boundaryIdx当成整座已被向更老翻页覆盖;单点孤岛的 oldestIdx 是最后一行,一次 loadOlder 就会把保守标记清掉。请不要把保守标记放在最新行,或让 absorb 只收口真正落在 [boundary, 主段游标] 里的孤岛。验证:reconcile 后先 loadOlder 再对跨洞目标断言canFocusWithoutJumpLoad === false。 -
[P1] PR 描述 — 这是功能 PR(会话窗口与跳转)。
docs/dev-rules/remote-and-mobile-adaptation.md要求 Description 对 SSH / 新 IPC 白名单 / 手机入口给出三选一结论。当前只写了 desktop renderer 影响,没有三选一。补一段即可,例如:本 PR 只改 desktop renderer 内存窗口;无新 IPC;mobile 无对等 store;device-link 走同一 makerChatStore。
这些问题已在当前 head 的后续 commit 中修复(格式门/安全门已通过),自动 dismiss 旧的 CHANGES_REQUESTED 以解除合并阻塞。
|
@Michael-Yu2978 👋 这个 PR 现在在等维护者确认,确认之前流程不会合并它 —— 不是卡住了,也不是在等你再改一版(你推的改动流程都读到了,判的就是最新一版代码)。
这条是流程自动发的状态提醒(同一版代码只发一次),不用回复。 |
MagicLizi
left a comment
There was a problem hiding this comment.
独立审查未通过(P1×3)。
- apps/desktop/src/renderer/lib/makerChatStore.ts:3628 —
updateIslandsAfterAroundMerge重叠时因mergedOldestClientId === null/mergedNewestClientId === null直接采用既有孤岛边界,从不把messages[blockStart]/messages[blockEnd]并进 union。around 贴着孤岛向主段延伸但尚未碰到主段时,newest 停在旧孤岛最新行,后续行会被算进主段,canFocusWithoutJumpLoad会跳过补齐。重叠时应按窗口下标取 min(oldestIdx, blockStart) / max(newestIdx, blockEnd) 的 clientId。 - apps/desktop/src/renderer/lib/makerChatStore.ts:3680 —
conservativeIslandsFor把整窗不连续建模成窗口最新一行的单点孤岛。absorbIslandsCrossedByPaging把 oldestIdx >= boundaryIdx 当成整座已被向更老翻页覆盖;单点孤岛的 oldestIdx 是最后一行,对任何命中窗口的分页游标都成立。reconcile 后一次 loadOlder 就会把标记清掉。不要把保守标记放在最新行,或让 absorb 只收口真正落在区间里的孤岛。 - PR 描述 — 功能 PR 缺
remote-and-mobile-adaptation.md要求的 SSH / 新 IPC 白名单 / 手机入口三选一结论。补一段即可:只改 desktop renderer 内存窗口;无新 IPC;mobile 无对等 store;device-link 走同一 makerChatStore。
这些问题已在当前 head 的后续 commit 中修复(格式门/安全门已通过),自动 dismiss 旧的 CHANGES_REQUESTED 以解除合并阻塞。
MagicLizi
left a comment
There was a problem hiding this comment.
独立审查未通过(P1×3)。当前 head a61307c1d3fb6c0cf78465fe658f57858e03c20d 不能合。
-
[P1] apps/desktop/src/renderer/lib/makerChatStore.ts:3628 —
updateIslandsAfterAroundMerge重叠时第一次 overlap 直接采用既有孤岛边界,不把messages[blockStart]/messages[blockEnd]并进 union。around 贴着孤岛向主段延伸但尚未碰到主段时,newestClientId停在旧孤岛最新行;其后新 merge 进来的行会被算进主段,canFocusWithoutJumpLoad会跳过backfillHistoryUntil。重叠时应按窗口下标取 min/max 的 clientId。请补回归。 -
[P1] apps/desktop/src/renderer/lib/makerChatStore.ts:3680 —
conservativeIslandsFor把整窗不连续建模成窗口最新一行的单点孤岛。absorbIslandsCrossedByPaging把oldestIdx >= boundaryIdx当成整座已被向更老翻页覆盖;单点孤岛的 oldest 是最后一行,一次loadOlder就会把保守标记清掉,跨洞目标canFocusWithoutJumpLoad === true。请改建模或收紧 absorb,并补回归。 -
[P1] PR 描述 — 功能 PR 缺
docs/dev-rules/remote-and-mobile-adaptation.md的 SSH / 新 IPC 白名单 / 手机入口三选一结论。补一段即可:本 PR 只改 desktop renderer 内存窗口;无新 IPC;mobile 无对等 store;device-link 走同一makerChatStore。
a61307c to
879a1a4
Compare
Signed-off-by: Michael-Yu2978 <ymt2978@163.com>
879a1a4 to
65ec6ea
Compare
这次改了什么
摘要
feat B「会话已加载窗口显式建模」:把
makerChatStore里「窗口是否掺过孤岛」的布尔(
historyWindowHasIsland)升级为显式孤岛区间模型historyWindowIslands: readonly LoadedWindowIsland[](每座孤岛{oldestClientId, newestClientId},按时间升序)。
背景(#676 review 之后与 MessageStream 锚定窗口双向有界 TODO 同一条后续改动):单个布尔
无法回答「目标落在窗口哪一段」,于是「孤岛 + 已翻到历史起点」的会话每次窗口内搜索都被迫
多发一轮 around + list 探测,窗口其实早已补成连续。显式模型下:孤岛被向上翻页真的跨过
(接回主段)时自动从模型消失;目标落在主连续段(最新尾段,最后一个孤岛最新边界行之后的
全部行)内时,搜索 / 跳转直接 focus、零网络。生产入口(
CCAgentSessionView的 searchJumpeffect)与 store 侧补齐快速通道共用同一把结构尺子
isInsideMainContiguousRun,不会再出现「生产判定与自愈判定漂移」的 #676 类问题。
变更类型
feat新功能fix缺陷修复refactor/perf重构或性能优化docs/test/chore文档、测试或工程维护范围
到历史起点」注释与 fix(desktop): 跳转历史消息不再留窗口空洞,工作组不跨空洞合并 #676 review(bce583965)
apps/desktop/src/renderer/lib/searchJumpTargeting.ts:新 API(LoadedWindowIsland、mainContiguousRunStartIndex、isInsideMainContiguousRun、canFocusWithoutJumpLoad)apps/desktop/src/renderer/lib/makerChatStore.ts:SessionChatState.historyWindowIslands(必填)替换旧布尔;
SessionChatLightState保留派生布尔historyWindowHasIsland;接入trim / demote / reload / clear / removeMessagesByClientIds / dropMessagesFromClientId
(edit-last)/ loadOlderMessages / 初始加载两次 setState / 权威重建(reconcile)/
around 提交(busy / covered / fallback);新增收口助手
(
absorbIslandsCrossedByPaging、updateIslandsAfterAroundMerge、pruneIslandsForTrimmedWindow、conservativeIslandsFor、conservativeIfIslandRowRemoved、pruneIslandsAfterNewestTruncation)apps/desktop/src/renderer/hooks/useCCAgentChat.ts:sessionless light 快照改用EMPTY_LIGHT_STATE新增 T2 / T3 / Y 语义更新)、makerChatStoreActiveView / makerQueueState /
reconcileRemoteMessages(断言迁移)
historyWindowIslands是 store 内部模型字段,未跨仓消费;对外 light 布尔保留)
docs/dev-rules/remote-and-mobile-adaptation.md):本 PR 只改desktop renderer 的内存窗口状态模型;无新增 IPC channel / 白名单条目;mobile 无对等
store、本次不涉及;device-link 复用同一
makerChatStore,重试 / 超时 / 断链恢复逻辑未触碰。UI 变化
与搜索跳转判定逻辑;UI 消费方(CCAgentSessionView / MessageStream)仍读派生布尔
historyWindowHasIsland,接口不变。当前 head 仅含 store / 纯函数与测试改动,不含任何组件文件;早期误带入的
CCAgentSessionView / MakerExperimentalView 主题 token 改动已归回 #3686 —— 旧
UI-evidence 提醒针对的是旧 head,本 head 无可截图 / 录屏的界面差异。
怎么验证的
自动验证
相关单测覆盖(全部通过):
零分页请求;Y:整座被裁掉的孤岛随洞消失、保留尾段恢复主段
手工验证
未执行实机 GUI / device-link 验证:搜索跳转链路需真实双端联调,本机只覆盖到 store 与纯函数
单测;
canFocusWithoutJumpLoad的判定与 store 快速通道共用同一把尺子,单测已直接覆盖生产入口(CCAgentSessionView 的 searchJump effect 调用的就是该纯函数)。
未执行的验证
absorbIslandsCrossedByPaging的「row id 游标」分支:测试里id === clientId,未直接命中生产形态的分叉(代码为同一匹配函数,审查级确认)
风险
风险分类
影响与回滚
不涉及 SQLite / system prompt / 协议 / 原生层 / 存量插件 / 冷更。
行为变更与边界(如实记录)
真的跨过(行落进本批拉取范围)才从模型移除(T2 两孤岛序列守这条)。
起点塌缩吸收、孤岛永远记不上的 bug(T / Y3 回归);判据是「块里至少有一行本来就躺在
合并前主段里」。
slice(-TRIM_TARGET)后,整座被裁掉的孤岛随洞消失(Y 用例语义更新),仍夹在保留窗口里的孤岛保留(Y3 覆盖)——比旧 boolean「一律保留」更精确且不丢不变量。
跳过);一行都没加进来时不记(Z「零新增」语义不变)。
hasMore=false不再作为短路依据(fix(desktop): 跳转历史消息不再留窗口空洞,工作组不跨空洞合并 #676 codex P1 的「孤岛 + 已翻到历史起点」仍可被 around捞回缺的邻居)。
absorbIslandsCrossedByPaging的边界按clientId || id匹配——生产分页游标是 DB row id,只按 clientId 会让边界永远落空、孤岛不收口。
提交前检查
git commit -s,见 DCO)——重提后为 1 个 commit 并已签名(已重基到当前 main,剔除了误带入 fix(desktop): 诊断页与会话视图硬编码颜色改用语义 token #3686 的两个语义 token commit),pnpm check:dco对origin/main..HEAD全部通过