From 11facf0392a5ad1e4403a8e1bc68fc9089507018 Mon Sep 17 00:00:00 2001 From: mesalogo Date: Sun, 27 Sep 2026 15:34:01 +0800 Subject: [PATCH] fix: restore destructive dialog buttons and document recent updates Destructive confirmation buttons lost their styling outside the notes page, including the draft-discard Portal. Share button sizing, typography, disabled feedback and theme-aware hover colors across callers, with Electron and contrast regression coverage and design documentation. Include the update article draft covering v0.13.8 through v0.13.16 and the separately identified 5116613 follow-up, its verification record and article index. The article remains unpublished with screenshots and release details pending. Validation: 37 focused tests passed, including real Electron capture; typecheck, lint and staged whitespace checks passed. No model calls. The previously documented unrelated HeartbeatCenter full-suite failure was not rerun. Release note: Destructive confirmation buttons, including the discard-note-draft dialog, now display consistent styling, keyboard focus and disabled feedback in light and dark themes. --- UI-DESIGN.md | 7 +- docs/features/magic-notes/progress.md | 27 ++++ .../2026-09-26-unreleased/article.md | 146 ++++++++++++++++++ .../2026-09-26-unreleased/review.md | 137 ++++++++++++++++ docs/pr-articles/README.md | 1 + docs/quality/recurring-defects.md | 17 ++ src/renderer/src/WorkspacePrimitives.test.tsx | 9 +- src/renderer/src/styles.css | 29 ++-- tests/magic-notes-capture.electron.test.ts | 1 + tests/support/magic-notes-capture-main.ts | 72 +++++++++ 10 files changed, 427 insertions(+), 19 deletions(-) create mode 100644 docs/pr-articles/2026-09-26-unreleased/article.md create mode 100644 docs/pr-articles/2026-09-26-unreleased/review.md diff --git a/UI-DESIGN.md b/UI-DESIGN.md index c55348ea..f5e53beb 100644 --- a/UI-DESIGN.md +++ b/UI-DESIGN.md @@ -68,7 +68,7 @@ | `--success`、`--success-subtle` | 成功状态 | | `--warning` | 警告状态 | | `--danger`、`--danger-strong` | 危险文本和状态 | -| `--danger-solid` | 最终确认按钮等需要反白文字的危险背景 | +| `--danger-solid`、`--danger-solid-hover` | 最终确认按钮等需要反白文字的危险背景及悬停背景 | | `--danger-border`、`--danger-subtle` | 危险入口和失败状态背景 | 浅色与深色具体值只在 `styles.css` 的主题根节点维护。状态组件必须同时显示文字或图标,不能仅靠颜色区分。 @@ -269,6 +269,11 @@ - `danger-solid`:确认对话框中的最终危险操作。 - `danger-zone`:设置页中集中展示的高影响操作区域。 +`danger-solid` 必须是全局共享类,不依赖页面祖先;通过 Portal 挂到 `body` 后仍须生效。 +尺寸、圆角、字体、间距和禁用状态与 `primary-button`、`secondary-button` 共用规则, +键盘焦点沿用全局 `button:focus-visible`。背景及可用态悬停使用危险实心色令牌,文字使用 +`--text-on-accent`;禁用时不触发悬停变色。 + 普通页面不得用实心红色按钮与主操作并列。危险入口必须使用具体动词和对象,例如“删除知识条目”,避免只写“确定”。 ### 6.7 数据表格与活动列表 diff --git a/docs/features/magic-notes/progress.md b/docs/features/magic-notes/progress.md index c718a774..ac5a5bce 100644 --- a/docs/features/magic-notes/progress.md +++ b/docs/features/magic-notes/progress.md @@ -1,5 +1,32 @@ # Magic Notes Progress +## 2026-09-27: Shared Destructive Button Styles + +The draft discard Portal now receives the global `danger-solid` styles. The +shared rule also covers note deletion, HeartbeatSettings, SupervisorWorkspace, +WorkspaceActionDialog and ToolEnvironmentSettingsSection without caller changes. +Sizing, typography and disabled feedback share the primary/secondary button +rules; theme tokens provide default and hover backgrounds. See +[shared destructive controls](../../../UI-DESIGN.md#66-危险操作). + +- `npm test -- tests/magic-notes-capture.electron.test.ts src/renderer/src/WorkspacePrimitives.test.tsx` + passed 2 files / 37 tests. The real App/preload/IPC/SQLite capture test opens the + draft guard outside the notes page ancestor. Both themes render a 36px-high, + 10px-radius button with the same font and padding as Continue editing. Native + Tab reaches the visible shared focus ring; continuing editing retains the draft. +- Hover uses Chromium `CSS.forcePseudoState`; disabled is set on the rendered + button to check opacity, cursor and suppressed hover. These are CSS state + checks, not verification of native pointer targeting or an asynchronous submit. + Theme contrast checks include both default and hover backgrounds. +- Windows Electron screenshots were captured and visually inspected for both + themes with keyboard focus. Set `GB_CAPTURE_SCREENSHOT_DIRECTORY` to an existing + directory to retain `draft-danger-light.png` and `draft-danger-dark.png`. + Model calls: 0. Other callers and other OSes were not individually rendered. +- `npm run typecheck` and `npm run lint` passed. Repository-wide + `npm test -- --bail=1` stopped at the unrelated + `HeartbeatCenter.test.tsx:213` assertion (expected one tablist, received two); + this is not an all-green full-suite result. + ## 2026-09-27: Analysis IPC Extraction The three analysis handlers now live in `magic-notes-analysis-ipc.ts`, with diff --git a/docs/pr-articles/2026-09-26-unreleased/article.md b/docs/pr-articles/2026-09-26-unreleased/article.md new file mode 100644 index 00000000..943f020d --- /dev/null +++ b/docs/pr-articles/2026-09-26-unreleased/article.md @@ -0,0 +1,146 @@ +# GoodBuddy 更新:工作回顾、可追溯图谱与远程对话恢复 + +> **编辑标注说明(对外发布前删除):** 本稿汇总桌面版 0.13.8 至 0.13.16 的变化,另在末尾标出 0.13.16 之后已提交、尚未归入发布版本的修复。“待补图”表示尚未取得素材,“待替换”表示发布信息未核验,“待核对”表示发布前需要确认的行为。本文是宣传草稿,尚未对外发布。 + +GoodBuddy 新增了监督者工作回顾:选择一个项目或一段时间,整理讨论过的事情、进展和未解决事项,再沿着图谱查看原始依据。这轮更新还加入模型连接复制、Mermaid 图表导出,并改善了待办浏览、长对话运行记录和远程会话恢复。 + +本文接续上一份画布笔记与文档处理更新稿,覆盖 0.13.8 起的实际增量。远程项目建议先升级桌面,再将 Host 环境更新至 Agent 0.13.4,以获得本文涉及的远程完成状态和旧进程清理修复。 + +> **【待替换 R01|版本与体验入口】** 对外发布前核验目标桌面安装包、发布日期、下载地址和完整更新日志链接,并在此补入。当前正式 tag 核查截至 `v0.13.16`;末尾开发中补充依据提交 `5116613`,目标版本未定。不能仅因 tag 存在就写“所有平台已可下载”;也不要推测下一版本号。桌面 0.13.14 的发布失败后由 0.13.15 承接,不将其列为成功发布的独立版本。 + +**从较早版本升级前,请先关闭应用并备份完整用户数据。** 数据库和相邻的 `notes/` 目录需要成套保留;旧客户端不能直接打开升级后的数据库。0.13.15 升至 0.13.16 本身不再提高数据库格式版本,但从前面的版本跨版本升级仍可能需要转换数据。 + +## 新增功能 + +### 监督者:从工作记录中整理进展,查看每条结论的依据 + +项目讨论持续一段时间后,可以打开“监督者”的“工作回顾”,选择全局或一个项目,以及最近 1 天、7 天或 30 天的范围。回顾会整理工作总结、本次变化和未解决事项;历史结果保留各自的范围和生成时间,方便回看。 + +监督者默认关闭,在应用中心启用后即可手动回顾,无需先建立自动计划。当前分析来源包括范围内的用户与助手消息、任务标题与当前状态,以及消息中已有的知识引用片段。它不会自动遍历全部笔记、整个知识库或附件原件。 + +> **【待补图 01|必补|工作回顾结果】** 在隔离演示项目中通过正常聊天和任务操作形成公开、虚构的工作记录,运行一次真实回顾。截图同时展示范围、时间和实际生成的总结、未解决事项;不能用预置答案冒充模型结果。计划文件:`assets/01-work-review.png`。 +> +> 建议图注:按项目回顾近期讨论与任务,查看进展和仍待处理的问题。 + +回顾中的事件、实体与关系可以在“故事线图谱”中查看。选中一个对象,就能检查它的说明、关联和保存的来源正文,再判断模型的归纳是否准确。实体支持确认、改名或撤销,关系可以确认或移除。 + +这适合检查“某个结论来自哪段讨论”“一个事项与哪些事件有关”。图谱按事件顺序提供浏览,但还不能逐时刻还原当时的全部实体状态;图上的间距也不表示实际时间间隔。 + +> **【待补图 02|必补|图谱与来源】** 使用图 01 同一份真实回顾结果,选中事件或实体,展示对应的来源正文。画面应能辨认对象与依据的关联;不使用设计原型代替生产结果。计划文件:`assets/02-story-sources.png`。 +> +> 建议图注:从回顾图谱查看关联对象,并打开保存的讨论依据。 + +### 在任务中心跟踪当前会话或任务 + +右侧任务中心的项目概览下方新增监督反馈。选中任务时查看该任务的匹配结果,否则跟随当前会话;也可以固定目标,切换聊天后继续查看同一个事项。没有匹配回顾时会明确显示空状态。 + +查看来源后,可以选择“继续讨论”,确认目标会话和上下文,再将内容加入该会话的发送队列。需要保存摘要时,也可以选择可写的本地知识库,预览后创建知识实体。外部知识库仍保持只读。 + +这两种后续操作目前适合短摘要:继续讨论的最终内容最多 12,000 字符,知识实体描述最多 2,000 字符。固定任务没有关联会话时,不能直接发送继续讨论。后续对话沿用目标会话的模型和工作模式。 + +### 复制模型连接,保留已有配置 + +在“设置 → 模型连接”中打开一条 LLM 连接,点击“复制”,即可在副本中修改模型名称、地址或其他选项,再保存为新连接。协议、请求定制和认证配置随副本带入,已保存的 API Key 无需重新输入。 + +复制后仍需保存才会生效;当前最多保留 20 条连接。保存地址有误时,界面会指出对应连接并定位到地址字段,便于处理多条连接中的配置问题。 + +### Mermaid 图表可导出完整 PNG + +聊天中的流程图或关系图可以打开大图窗口,再导出完整 PNG。导出内容不受当前缩放比例和拖动位置影响,适合插入报告、文档或演示材料。 + +图片沿用当前主题背景,最长边上限为 4096 像素。此功能用于 Mermaid 图表,故事线图谱的图片导出不在本轮范围内。 + +> **【待补图 03|建议|图表导出】** 在正常聊天中打开一张公开示例 Mermaid 图,执行 PNG 导出,并核对图片包含整张图。优先展示实际导出结果,图注注明它是导出图片。计划文件:`assets/03-mermaid-export.png`。 +> +> 建议图注:从 Mermaid 大图窗口导出完整 PNG,保留图中的边缘节点。 + +## 功能改进 + +### 自动监督与手动回顾分开管理 + +原有的每日、每周计划现在集中到“自动监督”。新建或编辑计划时,可以选择全局或多个项目,设置时间、时区、回顾窗口和历史保留期限;暂停的计划在编辑保存后仍保持暂停。 + +自动报告与监督回顾分别记录处理进度。对于对应阶段没有新增、修改或待处理内容的情况,会跳过模型调用;手动回顾则重新分析选择的时间范围。删除报告或计划不会重置已经记录的来源处理进度。 + +启用监督者不会自动创建计划。定时工作需要桌面应用保持运行,关闭监督者会停止发起新的回顾,并保留计划和历史;从导航取消常驻只改变入口显示。 + +监督分析会将入选内容交给配置的模型服务,可能产生费用。它以只读、无工具的方式分析记录;自动报告与回顾可能分别调用模型,长时间范围也可能分成多批。并发数和超时设置不等于总费用上限。 + +### 长回顾可以看进度、保留批次并继续处理 + +监督者的“活动记录”集中展示手动和自动执行情况。展开记录,可以查看当前阶段、已保存批次、剩余来源和失败原因,再按项目、会话、批次检查已保留事实与来源片段。 + +回顾会分页读取选定范围内的来源,将长内容拆成批次处理。成功批次保存后,后续阶段失败不会把这些内容一并丢弃;暂停或失败后可以继续。0.13.16 还取消了按固定累计时长自动暂停的行为,长回顾可以持续处理至完成,无需隔一段时间手动续跑。 + +需要调整处理方式时,可在“监督者 → 设置”中配置模型并发、单次模型超时、每次读取的来源条数、每批消息和文本量,以及单次响应容量。新建回顾会保存分批配置;来源已被修改或删除时,需要重新发起回顾。 + +> **【待补图 04|必补|回顾进度与来源批次】** 展开真实运行的活动记录,展示阶段、已保存批次和来源正文。若展示暂停后继续,须通过真实操作完成,不注入运行状态。图 01 的演示可复用同一份记录。计划文件:`assets/04-review-progress.png`。 +> +> 建议图注:从活动记录查看处理阶段与已保存批次,展开核对来源。 + +> **【待核对 C01|暂停与取消的版本差异】** `v0.13.16` 支持主动暂停和失败续跑,但活动页“继续”后的同卡片暂停控件仍有可用性缺口。`5116613` 修复该问题并增加取消入口,见末尾开发中补充。截图与最终文案必须对应同一版本,不能把后续控件标成 0.13.16 已有。 + +分页和批次记录便于核查处理范围,模型给出的总结、实体和关系仍需人工判断;来源处理完成不代表每个事实都被模型准确理解。 + +### 待办、任务与用量更便于浏览 + +魔法笔记的待办页可以拖动列表与详情之间的分隔条,调整后的宽度会保存。来源笔记标题支持独立折叠,收起一个分组不会清空正在查看的任务详情。分组折叠状态不跨重启保存,窄窗口下不提供横向拖宽。 + +笔记详情中的记录索引开关移到返回按钮旁,索引和 AI 评论开关都补上了文字标签。任务中心展开列表后,可以直接选择“全部、进行中、暂停、已结束”;默认显示进行中的任务,历史任务仍可通过切换状态查看。 + +> **【待补图 05|建议|待办布局】** 用虚构笔记创建不同来源的待办,拖动分隔条并折叠一个来源组,保持另一项详情可见。计划文件:`assets/05-note-todos.png`。 +> +> 建议图注:调整待办列表宽度,按来源笔记收起暂时不看的任务。 + +运行记录中的 Token 用量先显示项目或会话总计,展开后再看 Runtime 和模型明细,按模型分组的视图继续保留。这里汇总的是应用已有的用量记录,并非服务商计费账单。 + +### 回复期间减少重复读写和界面等待 + +任务统计转到后台查询,隐藏的任务面板保留操作状态并减少不必要的更新。较长任务列表也减少了屏幕外卡片的绘制工作。 + +流式活动更新时,只保存变化的记录,并减少连续保存过程中反复读取整份历史的次数。改动针对工具活动较多、运行记录较长的对话,保留原有内容与顺序;实际响应改善取决于历史规模和设备,不以测试中的数据库指标代替整机提速幅度。 + +### 设置页减少来回查找 + +长设置表单的分类标题与相关页签固定在顶部,请求 Header、Body 等低频选项收进高级设置。默认工作目录移到“平台功能 → 通用设置”,Skills 和 MCP 页面移除重复标题,保留导入与添加操作。 + +HTTP OCR 高级设置可以直接选择是否保留页眉、页脚、页码等内容,并恢复默认过滤方式。版面与生成参数的分组和间距也重新整理;具体选项效果仍取决于所连接的 OCR 服务。 + +成功和提示通知采用更紧凑的卡片,两秒后自动消失;错误提示继续保留,直至手动关闭。删除会话的确认框也去掉了重复警告,永久删除的含义不变。 + +## 问题修复 + +### 远程回答完成后,可以继续对话 + +远程请求现在等待模型交付确认后再结束。回答已经完成时,随后回收空闲 Runtime 失败不会再把结果改成“未知”。确实遇到结果未知的请求,用户显式发送新消息后会建立新的运行绑定并带入已有历史;自动恢复仍只重新连接原请求,不会擅自重复执行结果未知的任务。 + +最后一个活动任务结束后,远程空闲 Runtime 会被释放,下一轮对话再恢复历史。仍有任务或待答问题的共享进程会保留;更新后被替换的旧 Agent 会等待已有工作结束再退出,当前 Agent 保持常驻。 + +Host 更新还会清理已确认停用的旧 Agent 程序和归属明确的孤儿进程,保留会话历史、活动安装和状态不明的安装。这些修复需要配套 Agent 0.13.4,仅升级桌面不会直接替换 Host 上已经运行的旧进程。 + +### 启动、搜索与回顾结果 + +- 修复正常聊天或保存笔记后,重启反复显示历史迁移的问题。普通数据库结构升级也不再因正常空闲页额外整理整库;确需转换的旧数据仍会处理,启动页区分实际操作。 +- 修复本机直连模型文件搜索参数受限和大结果截断的问题。模型可使用 ripgrep 原生参数,保留原生结果与退出码,并分页续读长输出。Ask 保持工作区只读,Execute 使用当前账号权限;这项变更不属于远程 Agent 或其他 Runtime 的新增工具。 +- 修复早期监督回顾只取部分会话、消息和正文的问题,改为对入选来源分页、分批处理。事实与来源独立保存,只有必要阶段完成并成功保存结果后才标记完成。 +- 修复监督回顾中的旧实体引用、摘要合并校验及长摘要报错。非法格式、未知引用和超出响应容量的结果仍会报错,成功保存的批次可以保留供后续处理。 +- 修复历史回顾入口跳到其他结果、切换后旧响应覆盖当前选择的问题。侧栏、活动记录和图谱按对应结果定位,人工确认与修订的内容保留。 + +### 日常界面操作 + +- 切回已查看的会话时,立即显示缓存的累计回复时长,再后台刷新;缓存限本次应用运行期间。 +- Mermaid 大图放大后,顶部和左侧等边缘仍可通过滚动、拖动到达,窄窗口中的控件可以换行。 +- Git 分支选择器悬浮在工作区上方,不再把文件列表和提交历史向下挤动;项目选择菜单可以浮出侧栏,长名称与路径有更多显示空间。 +- 聊天与浏览器、终端工作栏之间的分隔条恢复完整拖动区域;笔记缩略图与内容边缘对齐,键盘焦点轮廓保持可见。 +- 纯文本排队消息不再显示无效的“查看附件”按钮;浏览器页签关闭失败后重试成功,会清除残留错误。 +- 监督反馈显示会话或任务名称,固定与刷新按钮在窄侧栏中排列更紧凑。工作回顾和活动页使用可用宽度,运行阶段与已保存批次更醒目。 + +### 开发中补充:回顾运行期间的控制 + +> **【待替换 R02|尚未发布的修复】** 以下仅对应 `5116613`,不属于 `v0.13.16`。正式发布前核验其归属版本和安装包,届时合并到上文对应段落并删除本提示;若只发布截至 0.13.16 的文章,则移除本小节。 + +回顾运行期间,范围、时间选择和刷新入口继续可用;修改选择不会改变已经启动的回顾。重复点击发起按钮会提示已有运行,避免同时创建重叠回顾。 + +活动记录增加取消正在运行回顾的入口,并修复继续回顾后无法再次暂停的问题。暂停保留进度供继续处理;取消会保留已成功保存的批次,但该次回顾不能再继续。取消清理结束前显示“正在取消”,未完成结果不会发布,之后可以新建回顾。 + +手动发起、继续与自动回顾共用执行限制;自动回顾在忙碌时等待,手动操作会提示当前已有运行。同一次回顾内部仍可按设置并行处理批次。 diff --git a/docs/pr-articles/2026-09-26-unreleased/review.md b/docs/pr-articles/2026-09-26-unreleased/review.md new file mode 100644 index 00000000..2ddd81e2 --- /dev/null +++ b/docs/pr-articles/2026-09-26-unreleased/review.md @@ -0,0 +1,137 @@ +# 0.13.8 起综合宣传稿核查记录 + +## 状态与范围 + +- 状态:草稿,未对外发布,截图待补。首次建档与检查日期:2026-09-26。 +- 用户要求:“0.13.8到目前的所有功能,准备一个新的草稿”。据此直接起草,未另设选题批准步骤。 +- 起点包含 0.13.8 的新增变化,因此比较基线采用其前一版 `v0.13.7`,而非排除 0.13.8 的 `v0.13.8..HEAD`。 +- 基线解引用提交:`fe093bc15f94d90c478e1e96fef0f9feb7b819e9`。 +- 本轮正式 tag 核查截止 `v0.13.16`:`0759d5dcffb3273411fe7c486bbcde904dd23e48`。Agent 配套截止 `agent-v0.13.4`。 +- 核查期间 HEAD 新增 `51166136157a646530e1ad9f6a0242c1c6390e95`。本稿固定到该提交,范围为 `v0.13.7..5116613`;其相对 0.13.16 的变化在正文单列未发布补充。最终宣传目标 tag 未定,沿用 `unreleased` 目录,不推测下一版号。 +- `0.13.14` 桌面发布失败,产品内容由 `0.13.15` 恢复版承接;Agent 发布独立。未在线验证各平台包及最新 CI,不把本地 tag 当作全部工件可下载的证明。 +- `8dce1d7` 的发布说明沿用了原 0.13.7 多项内容。已按基线实际 diff 去重,画布、PDF 批注、HTTP OCR 接入、远程 Continue、应用管理、会话置顶不重复当作新增。 +- 开始及后续检查均有其他任务的未提交修改,涉及 DSH Runtime、MCP、能力设置、构建与依赖、工作栏方案;随后出现 Obsidian 新目录。它们只登记为工作区进行中内容,未核验为已交付功能,不纳入正文。没有修改或撤销这些文件。 + +## 选题与篇幅 + +综合稿固定按“新增功能 → 功能改进 → 问题修复”排列,修复放最后。用户已要求直接起草,以下是编辑取舍记录,不冒称用户逐项确认了提纲。 + +| 主次 | 入文内容 | 理由与处理 | +| --- | --- | --- | +| 主线 | 监督者工作回顾、故事线图谱与来源、任务反馈后续操作 | 新增完整工作入口,用户可从历史记录整理进展、检查依据并继续讨论;展开介绍,同时说明采集范围与人工复核边界 | +| 主线的使用改进 | 自动监督、活动记录、分页批次与续跑 | 决定长时间范围能否持续处理、失败后如何继续,单独放在改进段,不把原有每日每周计划重新包装成新增 | +| 独立新增 | 模型连接复制、Mermaid PNG 导出 | 各自可直接完成一个新动作,短节说明入口与限制 | +| 日常改进 | 待办调宽与分组折叠、任务筛选、用量、设置、历史读写 | 按使用场景合并;待办操作是现有工作区扩展,采用“功能改进”分类,不照抄 feat 前缀 | +| 主要修复 | 远程完成确认与恢复、进程回收、启动迁移、搜索、回顾完整性 | 影响工作能否继续和数据处理可信度,保留具体条件及 Agent 配套要求 | +| 简述修复 | 菜单、工作栏拖动、时长、队列附件、通知及页面布局 | 分别归入改进或修复,避免每个控件占一节 | +| 未发布补充 | `5116613` 回顾控制、取消、单次执行限制 | 用户要求截至目前,因而纳入,但与 0.13.16 严格区分;最终版本确定后合并对应段落 | +| 不进入正文 | CI 上传、测试调度与夹具、发布元数据、社群二维码、开发规划 | 清单保留追溯,不算应用内新能力;OSS 上传加速不能写成客户端下载提速 | + +## 完整盘点 + +以下“已实现”指进入固定目标提交,不代表本轮完成所有平台实机验证。文件证据以 Git 对应提交为准,工作区文件可能继续变化。相同功能的连续修复合并计一项。 + +| 功能 | 分类/状态 | 原来与现在、入口 | 条件与边界 | 来源与验证线索 | +| --- | --- | --- | --- | --- | +| 监督者工作回顾 | 新增/目标代码具备 | 应用中心启用监督者;全局或单项目、近 1/7/30 天回顾,查看总结、变化和未解决事项 | 默认关闭;来源为消息、任务当前信息、已有知识引用等,不扫描全部笔记或附件原件 | `7d487f9`、`bf8ece0`;`SupervisorWorkspace.tsx`、`supervisor-service.ts`、`supervision-review-store.ts`;见监督进度记录 | +| 故事线图谱与来源 | 新增/目标代码具备 | 选取事件、实体、关系,查看保存的依据;实体确认、改名、撤销,关系确认、移除 | 按顺序浏览事件,不是完整状态回放;间距不代表时长;无关系类型编辑与撤销恢复入口 | `7d487f9`;`SupervisorWorkspace.tsx`、`assistant-database.ts`;有组件、存储和隔离视觉证据 | +| 任务/会话监督反馈 | 新增/目标代码具备 | 任务中心项目概览下跟随目标、固定、刷新、查看来源和图谱 | 仅匹配目标结果;Experiment 未接入 | `7d487f9`、`b16fc90`、`c71c0ad`;`RightAssistantSidebar.tsx`、`workbar-contracts.ts` | +| 继续讨论和本地实体预览 | 新增/目标代码具备 | 确认上下文后加入目标会话队列;预览后创建本地知识实体 | 继续内容上限 12000 字符,实体描述 2000;外部库只读;无关联会话的任务不能发送 | `7d487f9`;`ipc.ts`、`supervision-contracts.ts`、`RightAssistantSidebar.tsx`;IPC 定向验证 | +| 活动与批次查看 | 新增/目标代码具备 | 查看手动/自动运行、范围、状态、阶段、事实与来源树 | 活动每页 50 条,详情每页 10 批;已保存批次可跨重启保留 | `bf8ece0`、`db5e43d`;`SupervisorActivity.tsx`、`SupervisionBatchDetails.tsx`、`supervision-review-store.ts` | +| 自动监督增量 | 改进/目标代码具备 | 在既有每日/每周计划上接入监督;报告与回顾分开记录来源进度,无待处理输入时跳过模型 | 手动回顾重新分析区间;删除报告/计划不清检查点;关闭应用不执行计划 | `bf8ece0`、`db5e43d`;`heartbeat-service.ts`、`assistant-database.ts`;历史增量实模证据 | +| 自动计划与开关 | 改进/目标代码具备 | 五页签分工,弹窗创建编辑,选择多项目、时间及时区、窗口与保留期限 | 窗口 1–720 小时、保留 1–365 天;启用不自动建计划;关闭保留数据,取消常驻仅影响导航 | `bf8ece0`、`db5e43d`;`HeartbeatCenter.tsx`、`HeartbeatSettings.tsx` | +| 分页、批次、失败续跑 | 修复兼改进/目标代码具备 | 替代有限条数截断;分页读取、长文本分片,成功批次独立保存,合并及发布失败可重试 | 来源变化需新回顾;在途输出不保证保存;处理覆盖不等于模型语义无遗漏 | `db5e43d`;`supervision-production.ts`、`supervisor-service.ts`、`supervision-review-store.ts`;506 来源隔离存储回归等 | +| 监督参数 | 改进/目标代码具备 | 设置并发、超时、读取条数、批次文本及消息量、响应容量 | 默认并发 1,范围 1–4;报告/整理单次超时默认 240 秒,范围 30–600;容量默认 1024 KiB,范围 100–16384;没有总费用预算 | `db5e43d`、`e6ac073`;`SupervisionReviewSettings.tsx`、`supervision-review-contracts.ts` | +| 连续处理与结果校验 | 修复/截至 0.13.16 | 取消累计固定时长暂停;修复旧实体引用、导航输出校验、任意摘要长度限制,精确定位历史结果 | 主动暂停、单次超时仍保留;0.13.16 续跑期间同卡片暂停控件存在缺口 | `db5e43d`、`e6ac073`;生产服务与契约;定向/实模证据见下文 | +| 模型连接复制 | 新增/0.13.8 | LLM 连接详情复制地址、认证、协议、模型和请求定制,保存副本 | 最多 20 条;已保存密钥在主进程复制,不回传明文 | `8dce1d7`;`SettingsPanel.tsx`、`runtime-settings-store.ts`,组件和凭据复制测试 | +| 模型地址错误定位 | 修复/0.13.8 | 保存时显示错误连接名称、切回连接并关联 URL 字段 | 校验格式不是验证服务可用;HTTP/HTTPS、最多 2048 字符 | `8dce1d7`;`SettingsPanel.tsx`、`contracts.ts`,组件/schema 回归 | +| 设置导航与操作归位 | 改进/0.13.8 | 长表单标题页签吸顶;高级参数折叠;工作目录归入平台通用;Skills/MCP 标题精简与操作归位 | 既有能力的入口调整,不扩大 Runtime 或 MCP 支持范围 | `8dce1d7`;`SettingsPrimitives.tsx`、`PlatformFeaturesSettingsSection.tsx`、相关设置组件及测试 | +| HTTP OCR 过滤设置 | 改进/0.13.10 | 直接选保留内容,恢复默认;重排参数间距 | HTTP OCR 接入本身基线已有;取决于远端服务 | `6c4c054`;`HttpOcrSettings.tsx`、文档解析设置测试 | +| 任务筛选与长列表 | 改进/0.13.8 | 展开即可选全部/进行中/暂停/已结束,默认进行中;减少屏幕外绘制和重复查找 | 不删除历史,不新增分页;无整机性能倍数结论 | `6739e8e`;`RightAssistantSidebar.tsx`、`task-center.css` | +| 后台执行统计与隐藏面板 | 改进/0.13.10 | 统计移至只读 worker,任务面板隐藏后保留状态并减少更新 | 非所有数据库操作后台化,浏览器和终端保留所需生命周期 | `a0b35d1`;`execution-stats-reader.ts`、`execution-stats-worker.ts`、`WorkbarShell.tsx`;worker 和组件测试 | +| 用量汇总 | 改进/0.13.12 | 项目/会话先总计,展开 Runtime/模型;按模型视图保留 | 基于应用记录,不是账单;命中率按汇总分子分母算 | `cdd5737`;`ActivityPanel.tsx`、`token-usage.ts`;25 项定向记录 | +| 回复时长缓存 | 修复/0.13.12 | 切回已查看会话先恢复时长再刷新 | Renderer 生命周期内缓存,不跨重启、不推算未知执行时长 | `f754120`;`use-execution-stats.ts`;15 项定向记录 | +| 活动历史读写 | 改进/0.13.12–0.13.13 | 仅保存变化记录,复用已提交历史减少全量读取,保留顺序与重复 ID | 缓存随关闭、清空和外部提交失效;保留回滚与重试 | `984f16f`、`bf8ff7f`;`assistant-database.ts`、`activity-history-io.test.ts`;受控 WAL 和扫描测试 | +| 待办布局 | 改进/0.13.15 承接 | 魔法笔记待办拖动/键盘调宽并保存宽度,来源分组独立折叠 | 窄布局不调宽;折叠状态仅组件内;保留选中详情 | `f68584b`;`MagicNotesWorkspace.tsx`、`MagicTodoDirectory.tsx`;115 项含原生输入验证 | +| 笔记控件与索引 | 改进及修复/0.13.12–0.13.15 | 索引开关移至返回按钮旁,索引及 AI 评论显示文字;缩略图贴齐边缘、焦点可见 | 不改笔记正文、画布能力或缩略图生成 | `0265cfb`、`344008a`;笔记工作区、样式与 Electron 布局测试 | +| Mermaid 导出及查看 | 新增及修复/0.13.12 | 完整 PNG 导出;放大后边缘可达,弹层覆盖窗口,窄屏控件换行 | 当前主题,最长边 4096;不等于故事图导出 | `785b1ee`;`MermaidDiagram.tsx`、`markdown-mermaid.css`;58 项含 9 Electron 场景、10 次 PNG 下载 | +| Git/项目菜单 | 修复/0.13.12–0.13.15 | 分支菜单悬浮不挤内容,保留搜索与焦点;项目菜单通常 380px、窄屏收窄 | 受视口约束,不是新增 Git 或项目类型 | `fb4b9bd`、`0ed1be1`;`WorkspaceGitTools.tsx`、`ProjectSwitcher.tsx`;组件与 overlay 验证 | +| 工作栏与监督布局 | 修复/0.13.13–0.13.16 | 恢复分隔条 9px 命中区域;反馈名称、按钮、排序修正;回顾和活动铺满宽度,阶段、批次突出 | 布局变化不等于新增监督目标或执行权限 | `cebd513`、`b16fc90`、`c71c0ad`、`d4bc10c`、`325b702`;组件、CSS、隔离 Electron 视觉证据 | +| 队列附件与浏览器错误 | 修复/0.13.12–0.13.15 | 依据保存输入判断附件;纯文本隐藏按钮;关闭页签重试成功清除错误 | 首次关闭失败保留页签,不保证底层失败自动恢复 | `d5f6dc6`、`bf8ece0`;`ConversationInputQueue.tsx`、`RightAssistantSidebar.tsx`、数据库与组件测试 | +| 通知和删除确认 | 改进/0.13.10–0.13.12 | 紧凑通知,成功/提示 2 秒消失,错误手动关闭;删除确认去重复文案 | 永久删除语义不变,非系统通知策略变化 | `a0b35d1`、`be84534`;`App.tsx`、`styles.css`;假时钟回归 | +| 远程空闲进程及旧 Agent | 修复/Agent 0.13.3 起 | 最后任务完成后释放 Runtime,下轮恢复历史;退役 Agent 排空已有工作后退出 | 当前 Agent 常驻;保留活动任务及待答问题;旧进程不热更新 | `83a48a4`;`runtime-acp-backend.ts`、`daemon.ts`、`cli.ts`;213 项生命周期及 Linux 实机记录 | +| 远程完成确认与续聊 | 修复/Agent 0.13.4 配套 | 交付确认后完成,清理失败不改已完成结果;未知后显式新消息新绑定带历史 | 自动恢复仅 attach,不重放未知任务;不抢占仍活动请求 | `7adae97`;`model-bridge-broker.ts`、`agent-owned-acp-prompt.ts`、`acp-remote-runtime.ts`;Linux 双轮 Ask 探针及回归 | +| 旧安装清理与远程名称 | 修复及调整/0.13.12–0.13.15 | 清理确认停用 payload 与所属孤儿;移除“技术预览”标识 | 保留未知归属、活动安装、共享 Runtime 和历史;远程项目默认仍关闭 | `83a48a4`、`7adae97`;`installation-cleanup.ts`、`remote-environment-preparer.ts`;Linux 隔离清理验证 | +| 直连原生搜索 | 修复/0.13.15 承接 | workspace_rg 原生 args/cwd、输出/退出码,大输出由 output_read 续读 | 本机直连路径;Ask 工作区只读,Execute 当前账号;句柄随会话释放 | `ff746bd`;`direct-model-ripgrep.ts`、`model-tool-provider.ts`;169 通过、1 跳过及真实 Ask/Execute | +| 正常重启重复迁移 | 修复/0.13.8–0.13.9 | 不因聊天/文件化笔记写入形成的空闲页再触发迁移 | 保留真正旧数据转换与失败重试 | `8dce1d7`、`38d0923`;`assistant-storage-upgrade.ts`、`assistant-storage-startup.ts`;SQLite、启动回归 | +| 结构升级与空间回收 | 修复/0.13.16 | 结构、内容转换、空间回收分开,普通结构更新不全库 VACUUM,启动文案准确 | 必要转换仍执行;仅同次启动保留重试意图;schema 仍为 47 | `4c7193e`;数据库/升级服务、`StorageUpgrade.tsx`;47 项定向 | +| 回顾取消与避免重叠 | 修复及扩展/未发布 | 控件运行期间可用;继续后可暂停/取消;手动与自动共享单次执行限制 | 取消保留已存批次,不能继续,不发布未完成结果;清理期间仍占执行位置;自动等待、手动提示忙;范围选择不改当前运行 | `5116613`;`supervisor-service.ts`、`SupervisorActivity.tsx`、`SupervisorWorkspace.tsx`;38 UI、27 后端、1 Preload 定向记录 | + +### 内部维护及不入正文条目 + +| 条目 | 来源 | 处理 | +| --- | --- | --- | +| OSS 上传加速 | `e6a25b7`,随 0.13.11 | 发布上传改动,无独立客户端功能,不写下载提速 | +| 测试 worker 调度、OpenCode 隔离路径、异步 UI 与大桥接载荷超时 | `984f16f`、`163e245`、`bb8c04a`、`2ca19eb`、`cd27163` | 测试维护,不把用例超时调整写成生产模型超时变化 | +| 发布元数据、功能矩阵、保留分支历史与恢复版 | `598c68f`、`bf8ece0`、`7756231`、`d23af99`、`107dc97`、`e8d93b5`、`0759d5d` 等 | 用于范围和升级依据,不重复计功能 | +| 社群二维码更新 | `4366bdc`、`24bde0c` 及合并提交 | 项目资料更新,不作为应用功能 | +| 迁移夹具与架构规范 | 相关发布/迁移提交 | 只作验证与维护证据 | +| 逐事件状态回放、事件触发监督、Experiment、自动纠偏接管、完整知识正文双向编辑、费用预算、私有应用市场 | 设计文档或后续规划 | 未作为当前功能宣传;图谱能力以生产组件为准 | + +## 事实与验证边界 + +主要交叉核查资料: + +- [监督实施记录](../../features/conversation-supervision/progress.md)、[监督设计](../../features/conversation-supervision/technical-design.md)、[故事图 PRD](../../features/story-graph/prd.md)。设计目标不等于已实现事实。 +- [0.13.12 准备](../../development/release-preparation-0.13.12.md)、[0.13.14 联合准备](../../development/release-preparation-0.13.14.md)、[0.13.15 恢复版](../../development/release-preparation-0.13.15.md)、[0.13.16 准备](../../development/release-preparation-0.13.16.md)。 +- [版本更新说明](../../../resources/release-notes.json)、[Agent 0.13.4 说明](../../../resources/agent-release-notes/0.13.4.md)、[笔记备份说明](../../features/magic-notes/technical-design.md#backup-and-restore)。 + +本次为只读代码/提交核查与文档编辑,未重跑产品测试、未构建或打包、未调用真实模型。下列均为历史验证记录,不能写成本次实测: + +| 对象 | 已有证据 | 限制 | +| --- | --- | --- | +| 回顾覆盖与恢复 | 隔离 SQLite 22×23 共 506 个来源,长文本重建、跨重开、合并/发布失败与来源变化测试 | 不证明所有模型都正确提取全部事实 | +| 回顾真实模型 | 27 条消息、9677 码点,2 次提取、1 次合并,保存 53 条结构化记录;自动增量首次 2 次、重复 0 次、修改后 1 次的记录 | 更大 129 条消息样本第二次 JSON 无效,首批保留,不能写大样本全程已跑通 | +| 持续回顾 | e6ac073 的 13 项定向测试超过旧预算仍完成,主动暂停保留批次 | 非真实模型长时间压力验证 | +| 布局 | 325b702 记录 33 项组件、40 个 Electron fixture 场景通过 | 不是正式安装包完整端到端验收 | +| 远程 | Linux x64 真实 Host 生命周期、两轮 Ask 完成与历史恢复、隔离清理探针 | 不扩大到所有架构,未覆盖所有异常注入和正式更新 UI | +| 原生搜索 | 成功一轮真实 Ask/Execute 分页验证精确 6 次请求;定向 169 通过、1 跳过 | 早期失败轮无完整计数;非所有平台完整 UI | +| 历史 I/O | 1 万条历史受控回归,连续保存减少 WAL 增量和全量读取 | 不宣传整机“百倍提速”或真实磁盘吞吐收益 | +| 0.13.16 候选整体 | 5128 通过、84 跳过、1 失败;失败 Mermaid Electron 随后独立复跑通过;类型、lint、说明校验通过 | 不能改写成全量零失败,准备文档的 CI/打包待核验状态保持原义 | +| 5116613 | Renderer 38 通过;后端/IPC 筛选 27 通过、144 跳过;Preload 1 通过;定向 ESLint、diff 检查 | 无该轮 Electron 端到端、全量、类型检查或真实模型验证;记录中的“未提交”为提交前遗留措辞 | + +## 截图准备与缺口 + +已读取 [Electron UI 自动化规范](../../quality/electron-ui-automation.md) 并检查已有产物。当前平台 Windows;拟用中文、浅色主题、1280×800 内容区,实际运行尺寸尚未取得。 + +- `dist/GoodBuddy-windows-x64/` 的便携标记及 `resources/app.asar` 版本均为 0.13.13,不能作 0.13.16/5116613 的产品截图。 +- `out/` 有 Main、Preload、Renderer,也含新取消逻辑,但未找到可绑定目标 SHA 的构建身份。含某段新逻辑不证明三部分产物一致。 +- 两个 portable staging 目录缺少完整 `resources/app.asar`,不能认定为目标包。 +- 因无可确认匹配的生产产物,本次未启动应用/fixture,未做本地生产构建,未拍图;不能将视觉测试假数据或旧版图伪装成新版本实机结果。没有创建空 assets 目录或不存在的图片链接。 + +| 编号 | 优先级 | 场景与计划文件 | 当前状态 | +| --- | --- | --- | --- | +| 01 | 必补 | 真实工作回顾结果;`assets/01-work-review.png` | blocked:目标生产构建身份未确认 | +| 02 | 必补 | 同一结果图谱与来源;`assets/02-story-sources.png` | blocked:同上,需真实生成结果 | +| 03 | 建议 | Mermaid 正常 UI 导出并打开核对;`assets/03-mermaid-export.png` | blocked:同上 | +| 04 | 必补 | 同一回顾的阶段与保存批次;`assets/04-review-progress.png` | blocked:同上,区分 0.13.16 与后续取消控件 | +| 05 | 建议 | 虚构笔记待办调宽与折叠;`assets/05-note-todos.png` | blocked:同上 | +| R01 | 必填 | 最终版本、下载、日期、完整更新说明 | 尚未在线核验,不编造 | +| R02 | 必处理 | 5116613 的最终归属 | 已提交但未归入目标 tag;发布前合并或移除开发中小节 | +| C01 | 必核对 | 续跑期间暂停与取消控件版本 | 保留具体差异,待匹配最终安装包 | + +取得可核验产物后的步骤:在本轮独立临时目录建立 Node supervisor 与 Electron bootstrap,ready 前隔离 userData/sessionData,通过生产 Main/Preload/Renderer 启动;正常 UI 创建虚构项目、消息、任务和笔记。回顾演示仅发送所需最小模型请求并从实际请求边界计数。检查保存与重新打开、对象来源、暂停续跑后,逐张 capturePage 并打开审阅,确认无私人资料、裁切、错误状态;只归档实际采用图片,停止本次进程并清理演示 profile。 + +## 文案审校与交付检查 + +采用 deai-writing 1.3.0,读者为 GoodBuddy 日常用户;标题和正文以操作与结果为主,保留必要的数据、费用、版本与功能边界。不引入虚构效率数字、用户评价或发布状态。 + +交付检查结果: + +- Python 3.13.13 执行 `deai_scan.py article.md --json`:阻断项 0、复核项 13、读取错误 0。逐条审阅:12 项是编辑状态、升级兼容、发送限制、回放能力或取消语义等必要否定表述;另 1 项命中“模型准确理解”,用于说明处理覆盖不保证语义正确,保留原义。未机械删除功能边界。 +- 独立只读复核按固定提交检查正文与清单,未发现重大过度宣称、明确功能遗漏或版本混淆。11 个相对 Markdown 链接目标及备份锚点存在;图 01–05、R01、R02、C01 与核查表对应。 +- `git diff --check -- docs/pr-articles` 通过;仅提示既有 Git 行尾策略可能将 LF 转 CRLF。新增文件另作 no-index 空白检查。 +- 已通读结构与篇幅,主要能力展开,控件调整合并,未把同一能力在三类中重复当作新增;性能无虚构量化承诺。 +- 本次产品测试、构建、截图与真实模型请求均为 0。草稿所用验证数字来自已提交历史记录,不是本次执行结果。 + +本次只改文章、核查记录和索引,不提交、不打 tag、不对外发布。交付仍为带编辑提示的草稿;待补图片和发布信息见上表。 diff --git a/docs/pr-articles/README.md b/docs/pr-articles/README.md index a3f84ba6..552401f8 100644 --- a/docs/pr-articles/README.md +++ b/docs/pr-articles/README.md @@ -386,3 +386,4 @@ Agent 专题使用对应的 `agent-v…` tag。 | 日期 | 目标 tag | 标题 | 状态 | 正文 | 发布链接 | | --- | --- | --- | --- | --- | --- | | 2026-09-19 | 未确定(unreleased) | GoodBuddy 更新预览:画布笔记、文档处理与应用管理 | 草稿,截图待补 | [综合更新稿](./2026-09-19-unreleased/article.md) | 未发布 | +| 2026-09-26 | 未确定;汇总 v0.13.8–v0.13.16,另列 5116613 未发布补充 | GoodBuddy 更新:工作回顾、可追溯图谱与远程对话恢复 | 草稿,截图待补 | [综合更新稿](./2026-09-26-unreleased/article.md) | 未发布 | diff --git a/docs/quality/recurring-defects.md b/docs/quality/recurring-defects.md index 4bbc335c..c4599763 100644 --- a/docs/quality/recurring-defects.md +++ b/docs/quality/recurring-defects.md @@ -63,6 +63,23 @@ AI 列 280。测试同步原生输入与实际状态后独立复跑通过,保 **历史**:`e9679fe`、`ca4a58c` 各修了一部分被人工点到的弹窗,同一批改动又新增了两个未覆盖 的遮罩;后续复查改为共享规则加自动发现测试。 +## 共享控件样式依赖页面祖先 + +**症状**:放弃笔记草稿弹窗中的危险按钮显示为原生按钮,其他页面的危险确认也缺少样式。 + +**原因**:唯一的 `.danger-solid` 定义限定在 `.magic-notes-page` 下;Portal 挂到 `body` +后失去该祖先,心跳、监督者、工作区文件删除和工具环境设置也无法命中。 + +**规则**:共享按钮在全局定义并复用基础控件规则,不向每个调用处补页面类。 +具体状态和令牌见 [UI 设计系统](../../UI-DESIGN.md#66-危险操作)。 + +**防复发**:`magic-notes-capture.electron.test.ts` 在真实 App 中触发草稿确认,检查 +按钮不属于页面祖先时的浅深主题 computed style、尺寸、字体、禁用及原生 Tab 焦点; +悬停通过 Chromium 调试协议强制伪状态检查,不作为原生鼠标命中验证。 +共享令牌测试检查默认和悬停危险背景上的文字对比度。 + +**历史**:2026-09-27 同次检查确认草稿确认及上述四处调用均受同一作用域错误影响。 + ## 网页浮层遗漏原生浏览器视图 **症状**:知识库实例、项目设置等 Modal 的右侧被浏览器网页覆盖,通知或菜单也可能 diff --git a/src/renderer/src/WorkspacePrimitives.test.tsx b/src/renderer/src/WorkspacePrimitives.test.tsx index 0744f793..2360ef54 100644 --- a/src/renderer/src/WorkspacePrimitives.test.tsx +++ b/src/renderer/src/WorkspacePrimitives.test.tsx @@ -431,10 +431,10 @@ describe('WorkspacePrimitives', () => { /\.field small\s*\{[^}]*font-size:\s*var\(--font-caption\);/u ) expect(stylesheet).toMatch( - /\.primary-button:disabled\s*\{[^}]*cursor:\s*not-allowed;/u + /\.primary-button:disabled,\s*\.secondary-button:disabled,\s*\.danger-solid:disabled\s*\{[^}]*cursor:\s*not-allowed;[^}]*opacity:\s*0\.55;/u ) expect(stylesheet).toMatch( - /\.secondary-button:disabled\s*\{[^}]*cursor:\s*not-allowed;/u + /\.danger-solid:hover:not\(:disabled\)\s*\{[^}]*background:\s*var\(--danger-solid-hover\);/u ) expect(stylesheet).toMatch( /\.danger-button:disabled\s*\{[^}]*cursor:\s*not-allowed;/u @@ -476,7 +476,7 @@ describe('WorkspacePrimitives', () => { /button\s*>\s*svg\s*\{[^}]*display:\s*block;[^}]*flex:\s*0 0 auto;/u ) expect(stylesheet).toMatch( - /\.primary-button,\s*\.secondary-button,\s*\.danger-button\s*\{[^}]*display:\s*inline-flex;[^}]*align-items:\s*center;[^}]*justify-content:\s*center;[^}]*gap:\s*var\(--space-2\);/u + /\.primary-button,\s*\.secondary-button,\s*\.danger-solid,\s*\.danger-button\s*\{[^}]*display:\s*inline-flex;[^}]*align-items:\s*center;[^}]*justify-content:\s*center;[^}]*gap:\s*var\(--space-2\);/u ) expect(stylesheet).toMatch( /button:focus-visible,\s*input:focus-visible,\s*select:focus-visible,\s*textarea:focus-visible\s*\{[^}]*outline:\s*2px solid var\(--accent\);/u @@ -822,7 +822,8 @@ describe('WorkspacePrimitives', () => { ['success', 'success-subtle'], ['danger', 'danger-subtle'], ['text-on-accent', 'accent-solid'], - ['text-on-accent', 'danger-solid'] + ['text-on-accent', 'danger-solid'], + ['text-on-accent', 'danger-solid-hover'] ] as const for (const [foreground, background] of textPairs) { expect( diff --git a/src/renderer/src/styles.css b/src/renderer/src/styles.css index 17dc6791..3744c26e 100644 --- a/src/renderer/src/styles.css +++ b/src/renderer/src/styles.css @@ -21,6 +21,7 @@ --accent-subtle: #e8f4ff; --danger: #c53640; --danger-solid: #aa2630; + --danger-solid-hover: #8e2028; --danger-strong: #b72d37; --danger-border: #efbcc2; --danger-subtle: #fff1f2; @@ -2428,16 +2429,6 @@ margin-block: var(--space-2); } -.magic-notes-page .danger-solid { - min-height: var(--control-height); - padding: 0 13px; - border: 1px solid var(--danger-solid); - border-radius: var(--radius-control); - background: var(--danger-solid); - color: var(--text-on-accent); - cursor: pointer; -} - @container magic-notes-page (max-width: 800px) { .magic-notes-layout { grid-template-columns: minmax(0, 1fr); @@ -12314,6 +12305,7 @@ details.settings-section > :not(summary) + :not(summary) { .primary-button, .secondary-button, +.danger-solid, .danger-button { display: inline-flex; align-items: center; @@ -12336,7 +12328,9 @@ details.settings-section > :not(summary) + :not(summary) { background: var(--accent-solid-hover); } -.primary-button:disabled { +.primary-button:disabled, +.secondary-button:disabled, +.danger-solid:disabled { cursor: not-allowed; opacity: 0.55; } @@ -12347,9 +12341,15 @@ details.settings-section > :not(summary) + :not(summary) { color: var(--text-secondary); } -.secondary-button:disabled { - cursor: not-allowed; - opacity: 0.55; +.danger-solid { + border: 1px solid var(--danger-solid); + background: var(--danger-solid); + color: var(--text-on-accent); +} + +.danger-solid:hover:not(:disabled) { + border-color: var(--danger-solid-hover); + background: var(--danger-solid-hover); } .danger-ghost { @@ -16304,6 +16304,7 @@ details.settings-section > :not(summary) + :not(summary) { --accent-subtle: #123b5b; --danger: #ff9faa; --danger-solid: #ad3441; + --danger-solid-hover: #962d38; --danger-strong: #ffb0b9; --danger-border: #71404a; --danger-subtle: #421f2a; diff --git a/tests/magic-notes-capture.electron.test.ts b/tests/magic-notes-capture.electron.test.ts index c3cefde9..d59d4383 100644 --- a/tests/magic-notes-capture.electron.test.ts +++ b/tests/magic-notes-capture.electron.test.ts @@ -59,6 +59,7 @@ it('captures messages and full history from App through real Electron preload, I const result = JSON.parse(await readFile(join(directory, 'result.json'), 'utf8')) process.stdout.write(`Magic notes capture Electron evidence: ${JSON.stringify(result)}\n`) expect(result.error, output).toBeUndefined() + expect(result.dangerButtonStyles.map((style: { theme: string }) => style.theme)).toEqual(['light', 'dark']) expect(result).toMatchObject({ existingSaved: true, newSaved: true, fullHistoryCount: 502, sourceOpened: true, workspaceOpened: true, keyboard: true, narrowPanel: true, sqliteReopened: true, sourceSurvivesDeletion: true, modelAttempts: 0 }) diff --git a/tests/support/magic-notes-capture-main.ts b/tests/support/magic-notes-capture-main.ts index 49064515..db53b646 100644 --- a/tests/support/magic-notes-capture-main.ts +++ b/tests/support/magic-notes-capture-main.ts @@ -145,6 +145,78 @@ app.whenReady().then(async () => { evidence.keyboard = true evidence.narrowPanel = true evidence.panelWidth = bounds.width + // Exercise the actual draft guard Portal outside the notes page ancestry. + await run(`document.querySelector('#compact-note-append').focus()`) + await win.webContents.insertText('Unsaved style regression draft') + await key('Escape') + await wait('Boolean(document.querySelector("#note-draft-title"))', 'draft discard Portal') + const danger = 'document.querySelector(".custom-task-dialog .danger-solid")' + assert.equal(await run(`${danger}.closest('.magic-notes-page') === null`), true) + const buttonStyles = [] + win.show() + win.focus() + win.webContents.focus() + await wait('document.hasFocus()', 'native window focus for button styles') + win.webContents.debugger.attach('1.3') + await win.webContents.debugger.sendCommand('DOM.enable') + await win.webContents.debugger.sendCommand('CSS.enable') + const { root } = await win.webContents.debugger.sendCommand('DOM.getDocument') + const { nodeId } = await win.webContents.debugger.sendCommand('DOM.querySelector', { nodeId: root.nodeId, selector: '.custom-task-dialog .danger-solid' }) + for (const theme of ['light', 'dark']) { + await run(`document.documentElement.dataset.theme = '${theme}'`) + await win.webContents.debugger.sendCommand('Input.dispatchMouseEvent', { type: 'mouseMoved', x: 0, y: 0 }) + await run(`${button('Continue editing')}.focus()`) + await key('Tab') + await wait(`${danger} === document.activeElement && ${danger}.matches(':focus-visible')`, 'keyboard focus on discard') + const style = await run>(`(() => { + const b = ${danger}, s = getComputedStyle(b); + const other = getComputedStyle(b.previousElementSibling); + const token = name => s.getPropertyValue(name).trim(); + const rgb = value => { const c = document.createElement('span'); c.style.color = value; b.append(c); const v = getComputedStyle(c).color; c.remove(); return v }; + return { width: b.getBoundingClientRect().width, height: b.getBoundingClientRect().height, minHeight: s.minHeight, + controlHeight: token('--control-height'), background: s.backgroundColor, + expectedBackground: rgb(token('--danger-solid')), expectedHover: rgb(token('--danger-solid-hover')), color: s.color, + expectedColor: rgb(token('--text-on-accent')), radius: s.borderRadius, + expectedRadius: token('--radius-control'), font: s.font, siblingFont: other.font, + padding: s.padding, siblingPadding: other.padding, display: s.display, + focused: b === document.activeElement && b.matches(':focus-visible'), + outline: s.outlineWidth, pixelRatio: window.devicePixelRatio, outlineStyle: s.outlineStyle, outlineOffset: s.outlineOffset, + outlineColor: s.outlineColor, expectedOutline: rgb(token('--accent')) }; + })()`) + assert.equal(style.background, style.expectedBackground) + assert.equal(style.color, style.expectedColor) + assert.equal(style.radius, style.expectedRadius) + assert.equal(style.minHeight, style.controlHeight) + assert.ok(Number(style.height) >= parseFloat(String(style.controlHeight))) + assert.equal(style.font, style.siblingFont) + assert.equal(style.padding, style.siblingPadding) + assert.equal(style.display, 'flex') // Inline flex is blockified in the dialog's flex footer. + assert.equal(style.focused, true) + // Chromium snaps outline widths to physical pixels on fractional Windows scaling. + assert.ok(Math.abs(parseFloat(String(style.outline)) - 2) < 1 / Number(style.pixelRatio)) + assert.equal(style.outlineStyle, 'solid') + assert.ok(Math.abs(parseFloat(String(style.outlineOffset)) - 2) < 1 / Number(style.pixelRatio)) + assert.equal(style.outlineColor, style.expectedOutline) + if (process.env.GB_CAPTURE_SCREENSHOT_DIRECTORY) { + writeFileSync(join(process.env.GB_CAPTURE_SCREENSHOT_DIRECTORY, `draft-danger-${theme}.png`), (await win.webContents.capturePage()).toPNG()) + } + // Force the browser pseudo-state to test CSS independently of OS pointer ownership. + await win.webContents.debugger.sendCommand('CSS.forcePseudoState', { nodeId, forcedPseudoClasses: ['hover'] }) + const hover = await run(`getComputedStyle(${danger}).backgroundColor`) + assert.equal(hover, style.expectedHover) + assert.notEqual(hover, style.background) + await run(`${danger}.disabled = true`) + const disabled = await run(`(() => { const s=getComputedStyle(${danger}); return {background:s.backgroundColor,opacity:s.opacity,cursor:s.cursor} })()`) + assert.deepEqual(disabled, { background: style.background, opacity: '0.55', cursor: 'not-allowed' }) + await run(`${danger}.disabled = false`) + await win.webContents.debugger.sendCommand('CSS.forcePseudoState', { nodeId, forcedPseudoClasses: [] }) + buttonStyles.push({ theme, ...style, hover, disabled }) + } + evidence.dangerButtonStyles = buttonStyles + win.webContents.debugger.detach() + await click('Continue editing') + await wait('!document.querySelector("#note-draft-title")', 'continue editing retains draft') + assert.equal(await run('document.querySelector("#compact-note-append").value'), 'Unsaved style regression draft') await run('window.goodbuddy.magicNotes.get(' + JSON.stringify(newNote.id) + ')') await dispose() database.close()