Skip to content

feat(stash): Codex 草稿暂存 — 输入框旁 push/pop + Usage 下方暂存面板#550

Merged
Cmochance merged 13 commits into
mainfrom
worktree-feat-codex-stash
Jun 23, 2026
Merged

feat(stash): Codex 草稿暂存 — 输入框旁 push/pop + Usage 下方暂存面板#550
Cmochance merged 13 commits into
mainfrom
worktree-feat-codex-stash

Conversation

@Cmochance

Copy link
Copy Markdown
Owner

Refs CAT-259

做了什么

新增 CDP 注入器 codex_stash_injector,给 Codex 加「草稿暂存区」:把当前输入框内容先暂存腾出输入框去回复上一轮,随后选择性恢复到输入框或直接发送。比 Codex 原生 steer(预存后只能按队列顺序自动发送)更灵活。

  • composer 工具栏:暂存(push)+ 快捷恢复(pop,1 条直接恢复 / 多条下拉选)
  • Usage 面板下方:Stash 列表,每行 恢复 / 发送 / 删除
  • 全局 localStorage(catStash),跨 Codex reload 存活;恢复用 swap 语义(输入框非空先暂存当前内容)不丢草稿
  • 独立 daemon,settings.codexStashEnabled 开关(默认关)

用户影响

默认关,开启后仅对「通过本应用启动的 Codex」生效。

验证

cargo check/fmt/test(3/3)/machete · 前端 vue-tsc + build · 活体 CDP 注入真机 Codex 渲染 + 死循环 guard 实测通过。完整点击链路真机 e2e 合并前打 .app 验。

实现细节 / 关键锚点 / review 修复见 CAT-259。

新增 CDP 注入器 codex_stash_injector,解决「预输入内容 vs 临时插话」冲突:
可把当前输入框草稿先暂存腾出输入框回复,随后选择性恢复到输入框或直接发送
(比原生 steer 的「按队列顺序自动发送」更灵活)。

- composer 工具栏:暂存(push)+ 快捷恢复(pop,1 条直接恢复 / 多条下拉选)按钮
- Usage 面板下方:Stash 列表,每行 恢复/发送/删除
- 全局 localStorage(catStash)存储,跨 reload 存活;恢复采用 swap 语义(输入框
  非空时先暂存当前内容)不丢草稿
- 独立 daemon,settings.codexStashEnabled 开关(默认关)
- quota injector 配合:Stash 面板在场时让位坐到其前,保证「Usage 在 Stash 上面」
bump VERSION 时漏改 ctx_breakdown_contract 测试里 pin 的版本字符串,
CI 全量 test 捕获(本地只跑了 stash filtered 测试漏掉)。
- push/pop/面板恢复 携带图片:读 imageAttachments(dataURL),恢复经合成 paste
  复用 Codex 原生摄入(fetch(dataURL) 被 CSP 挡→改 atob 解码)
- 文件不在范围(注入侧无法干净恢复,见 followup CAT-260):push 不动文件
- save 返回成功标志:图片 dataURL 大,localStorage 配额溢出时降级为只存文字、不丢内容
- 面板行显示图片计数;push 按钮有文字或图片即可用;ensurePanel 改廉价 id 签名(不再每 tick stringify 大 dataURL)
- VERSION 1→2
- 界面预览/Preview 下各功能展示(主界面/接入/主题/用量/Stash)统一收进 <details>
  折叠分组,默认折叠缩短页面长度(GitHub Markdown 无标签页,用折叠实现整体展示区)
- 新增 Codex 草稿暂存 Stash 展示:Stash 面板(带图片计数)+ 快捷恢复下拉两图
@Cmochance

Copy link
Copy Markdown
Owner Author

@codex

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e95e1a7b15

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/codex_stash_injector.rs Outdated
Comment thread src-tauri/src/codex_stash_injector.rs Outdated
Comment thread src-tauri/src/codex_stash_injector.rs Outdated
Comment thread src-tauri/src/codex_stash_injector.rs
- 暂存保留换行:textContent 改 innerText 捕获(多行不再被拼成一行);存储规范化为
  每个换行一个 \n(captureText),恢复经 text/plain 合成 paste 重建段落 —— CDP 实证
  本 composer Shift+Enter = 新建 <p> 段落,paste 按 \n 切段同构还原,往返幂等且精确;
  insertLineBreak 会插裸 \n 被 PM 规整成空格(丢换行)故弃用,回退仅 insertText/InputEvent
- 图片发送前确认 paste 就位:waitImages 回调带 ok,超时不照发也不消费暂存
- 写失败不消费:restore/sendItem 仅在 setComposer 真落值后才移除目标项
- load 解析缓存:__rev 单调写计数,热路径(ensureBar/ensurePanel)不再每 tick
  重解析数 MB 图片 dataURL;判空用 textContent 不触发 reflow

经 CDP 注入真机 Codex 跑完整 push→restore 往返验证:多行草稿换行保留、恢复结构精确、
目标项消费时机正确、用户原有暂存不受影响。

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 688e31c07c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/codex_stash_injector.rs Outdated
Comment thread src-tauri/src/codex_stash_injector.rs
Comment thread src-tauri/src/codex_stash_injector.rs Outdated
Comment thread src-tauri/src/codex_stash_injector.rs Outdated
- 保留有意空行:captureText 改块级读取(顶层 <p> 各取 textContent 按单 \n join,空块=空行),
  不再折叠连续换行;CDP 实证空行往返幂等(此前 collapse 会把 a\n\nb 压成 a\nb)
- 图片就位判定先等旧图清空:settleImages 先轮询当前 data: 图清空到 0(clearImages 异步)再 paste +
  计数,避免旧附件满足计数导致带错图/空图就提交
- restore 也等图就位才消费:之前只有 sendItem 等,restore 写完文本就消费 + addImages 不校验;
  现在有图时同样等 paste 成功才移除暂存项,失败保留可重试
- clearImages 只清可序列化(data:)图:非 data:(如 blob 上传)readImages 存不了,就不删,
  避免「没存上又删掉=丢图」

经 CDP 注入真机验证:空行往返幂等、图片(文本+图)push→restore 往返成功且消费时机正确、
用户原有暂存全程不受影响。

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 26b371ce28

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/codex_stash_injector.rs Outdated
Comment thread src-tauri/src/codex_stash_injector.rs Outdated
Comment thread src-tauri/src/codex_stash_injector.rs Outdated
- 清空判定改数「全部附件」非仅 data::settleImages 用 attCount()(composerAtt 全量)判断旧图
  清空,避免一张非 data: 旧附件被当成已清空、跟着 paste+提交
- 纯文本 send/restore 也走 settleImages([]):先等旧图清空再消费/提交,否则 clearImages 异步
  未清完时,纯文本暂存项会带着上一稿的图一起发出
- captureText 块内非 trailing <br> → \n(防御富文本粘贴/未来版本块内 hard break;CDP 实证本
  composer 把 br/HTML 断行规整成独立段落,正常不触发;空块仍用 textContent 保留空行)

经 CDP 真机复验:空行往返仍幂等、图片往返仍正确;F2(块内 br)取防御实现而非忽略。

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6151faa327

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/codex_stash_injector.rs Outdated
Comment thread src-tauri/src/codex_stash_injector.rs Outdated
- 清空判定改按附件行存在性(hasAttachments):附件行 [data-composer-attachments-row] 条件渲染
  (有任意附件才在 DOM,CDP 实证),故其在场=有附件,**含文件**;文件清不掉时 send 超时安全中止,
  不会把残留文件随暂存文本一起发出(此前 attCount 只数 imageAttachments,漏文件)
- restore/sendItem 加全局串行锁 __busy:异步(settleImages)期间忽略重复点 + push,防同条重复点 /
  并发 clobber composer / 重复 submit;每条早退路径与 callback 均复位
- stashCurrent 处理中也跳过(不在 restore/send 异步窗口里动 composer)

经 CDP 真机复验 V7:空行 + 图片往返仍正确;cargo test 262 passed。

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2b0a5176d5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/codex_stash_injector.rs Outdated
Comment thread src-tauri/src/codex_stash_injector.rs Outdated
Comment thread src-tauri/src/codex_stash_injector.rs
- setComposer 写校验改 sameText(块级 captureText 比对,保留空行;只去尾部空白/尾换行,不折叠
  内部 \n):此前 nrm 折叠连续换行,把 a\n\nb 写成 a\nb 也判通过 → 消费/发送被破坏草稿;现在
  空行不一致即判失败,保留暂存可重试
- sendItem 提交成功才消费:submitComposer() 返回 false(按钮/fiber 路径变化或暂不可提交)时
  保留暂存、不静默吞掉(此前先消费再 submit,失败=静默丢失)
- stashCurrent 暂存含图后等附件真正清空再解锁(__busy + 轮询 hasAttachments):clearImages 异步,
  否则刚暂存的图还挂着、用户紧接着发送的插话会把它带出去

经 CDP 真机复验 V8:空行往返仍幂等(sameText 校验 happy-path 不误判)、图片往返仍正确;cargo test 262 passed。

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 625448e703

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/codex_stash_injector.rs
Comment thread src-tauri/src/codex_stash_injector.rs Outdated
Comment thread src-tauri/src/codex_stash_injector.rs Outdated
- sameText 不再裁剪前导/尾随换行:此前去掉前导 \n,丢了草稿的前导空行也判通过 → 消费被破坏
  草稿;现在只归一 \r + 去各行尾水平空白,任何 \n(前导/内部/尾随)均保真比对(CDP 实证前导空行
  paste 可保留、捕获/恢复一致)
- push 遇文件附件中止:hasFileAttachment 检测附件 chip(.composer-attachment-surface)中无 <img>
  的=文件;文件不可暂存/恢复(CAT-260),只存文字会把文件孤儿化、被下一条原生发送带走,故中止并
  提示用户先移除文件(restore/send 侧已由 hasAttachments 门控自动规避)

F1(暂存图片后亚帧窗口内用 Codex 原生发送的竞态)按维护者决定作为已知限制:关闭它需拦截 Codex
原生 Enter/发送(有误吞 Enter 风险),与近乎不可达的时序竞态不成比例。

经 CDP 真机复验 V9:内部空行 + 前导空行 + 图片往返均正确;cargo test 262 passed。

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d67f0558e7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/codex_stash_injector.rs Outdated
Comment thread src-tauri/src/codex_stash_injector.rs
Comment thread src-tauri/src/codex_stash_injector.rs Outdated
- 不支持附件检测 unsupportedAtt 统一覆盖文件 + 非 data: 图(blob/上传中):
  imageAttachments 总数 > data: 图数(readImages)→ 有非 data: 图;总附件数(attTotal,附件行 flex
  容器直接子节点数,1 附件 1 子)> 图片附件数 → 有文件。任一为真即中止 push/restore/send 保草稿。
  (注:.composer-attachment-surface 类有嵌套,每附件命中 2 个,故用 flex 子节点数,CDP 实证)
- restore/sendItem 在 swapInCurrent 前拦截不支持附件:避免把当前草稿拆成 text-only stash + 残留附件
- 配额溢出整体中止:不再降级为「只存文字、图片留输入框」(那会拆分草稿 + 残留图被原生发送带走),
  改为不动 composer + 提示,保草稿完整

经 CDP 真机复验 V10:data: 图片往返正常(不误拦)、上传中/文件附件正确拦截、文本空行往返正常;
cargo test 262 passed。
@Cmochance Cmochance merged commit 78f29b4 into main Jun 23, 2026
5 checks passed
@Cmochance Cmochance deleted the worktree-feat-codex-stash branch June 23, 2026 21:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant