Skip to content

feat(desktop): 接入工作台背景图,侧边按钮条弹窗与设置外观共用同一设置 - #1878

Merged
xiaoyumuxi merged 4 commits into
previewfrom
feat/desktop-workbench-background
Nov 14, 2026
Merged

xiaoyumuxi merged 4 commits into
previewfrom
feat/desktop-workbench-background

Conversation

@xiaoyumuxi

Copy link
Copy Markdown
Collaborator

关联 Issue / Linked issue

PR 类型 / Type

  • feat:新功能或用户可见的改进
  • fix:缺陷修复
  • docs:文档、Agent Note、Skill 等规则文件
  • release:稳定版发布准备

改动说明 / What changes

背景 / Why

旧客户端可以在工作台后面放一张自己的图片,Viva 桌面端此前只有右下侧边按钮条上的“背景”按钮,点开是空的工具窗口,设置里也没有入口。本 PR 把这项功能真正接上:侧边按钮条弹窗和 设置 → 外观 两个入口共用同一项设置,图片真实画在工作台后面;按要求不内置任何默认图片,没选图片时两处都显示“未选择背景图片”和“选择图片…”。实现参照 motty 的 internal/workbench/background.go(选图 → 复制到数据目录 → 解码一次 → 整窗画在最底层、上面叠遮罩),文件读写按本仓库规则全部移出窗口线程。

改动 / Changes

工作台背景图(desktop/internal/workbench/workbench_background.go,新增)

  • 用户选择 PNG 或 JPEG(系统文件选择器,筛选文案走 i18n),先在后台读取、检查文件头与上限(文件不超过 30 MB、像素不超过 6400 万),再解码并一次缩到最长边 2560 像素以内的 BGRA 位图。
  • 图片复制为数据目录中的 workbench-background 副本(store.WriteFile 原子写入),设置只保存原文件名和档位(settings.json 的 workbenchBackground,omitzero);原文件移动或删除不影响背景。
  • 副本写好后才替换当前背景;写入失败时提示“无法保存”,原背景与原设置保持不变(原子 rename 保证旧副本仍在)。
  • 启动时(只在窗口的第一个标签)在后台读取并解码副本恢复背景;副本缺失按未选择处理(不记日志、不提示),副本损坏时提示原因、关闭背景并在后台删除副本。
  • 移除背景时清空状态、保存设置,并在后台删除副本;档位保留给下一张图片。
  • 选择、移除、恢复共用一个序号:过期的解码、写入、删除结果一律丢弃;副本的写入和删除由同一把锁串行执行,“选图后立刻移除”不会留下副本或把背景重新打开。
  • 淡、中、浓三档对应遮罩不透明度 0.92、0.80、0.62;没有图片时不显示图片可见度和移除。
  • 一个窗口的多个项目标签共用同一张背景(App.backdrop 由第一个标签创建并传给后续标签),修改后统一重绘所有工作台和设置窗口。
  • 没有数据目录(store 为 nil)时不再空指针崩溃,所选图片只显示到退出。

绘制与透明(工作台)

  • root.go:有背景时窗口底色改为主题底色乘档位不透明度的遮罩,图片作为 0 层整窗按比例裁切居中绘制(DrawBackdrop)。
  • theme.go:调色板新增 Ground(始终不透明的编辑区底色);显示背景时 Panel、Editor 变为透明,Viva 的弹窗、菜单、卡片、输入框仍用主题实底。
  • project_gradient.go:显示背景时外框(标题栏、侧边按钮条、状态栏)不画实底。
  • 浮在工作台上方、必须保持实底的地方从 Editor/Panel 改用 Ground:标签拖动和工具窗口拖动的浮层(tabdrag.go、drag.go)、Git 日志与 worktree 的引用标签(gitgraph.go、gitworktree_view.go)、差异块复选框(difftab_view.go)、Markdown 预览目录按钮(markdown_preview.go)、记住的窗口底色(root.go)。
  • 只在打开项目时显示背景,欢迎页不显示。

侧边按钮条弹窗(catalog.go、chrome_view.go)

  • “背景”按钮不再是工具窗口:点开弹窗(图片图标、标题、关闭 X、状态、选择图片…、有图片时的图片可见度与移除),交互与标题栏齿轮一致,按下关闭弹窗的那次点击不会重新打开。
  • 旧布局里保存为“已打开”的背景按钮不再占工具窗口槽位(chromeState.apply 跳过非工具窗口项)。

设置 → 外观(desktop/internal/settingswindow/)

  • 外观卡片新增“工作台背景 ⓘ”行:缩略图(与工作台同一裁切 CoverRegion)、状态、选择图片…;有图片时显示移除和淡/中/浓选择;窄窗口时改为纵向排列。
  • Options 新增 WorkbenchBackground、ChooseWorkbenchBackground、RemoveWorkbenchBackground、SetWorkbenchBackgroundStrength;设置窗口在自己的线程加锁读快照,修改投递回工作台线程执行。
  • 缩略图和工作台共用同一份像素与位图 ID,不另存一份。

终端(desktop/internal/termview/)

  • Look 新增 Solid:Background 可以是透明的(透出背景图),而反色单元、光标下文字、输入法组字区的底色、回应程序的颜色查询以及系统 ANSI 颜色的对比度判断都用不透明的 Solid;Solid 为零值时取 Background,行为与之前相同。
  • 工作台的终端外观传入 Solid: Ground。

存储(desktop/internal/store/store.go)

  • 新增 WriteFile(原子替换,Save 改为调用它)、Remove(文件不存在视为成功)、Path。

日志

  • 新增操作 workbench_background(选图或恢复失败)、workbench_background_save(副本写入失败)、workbench_background_remove(副本删除失败),以及动作 workspace.background.open/choose/remove/strength;均已纳入 TestLogEventsMatchCatalog,经 Core 目录校验。

界面文案(shared/i18n/)

  • 新增 14 条 desktop.workbenchBackground.* 中英词条(说明、移除、关闭、图片可见度、淡/中/浓、载入中、状态、失败原因、超过 30 MB、超过 6400 万像素、只支持 PNG 和 JPEG、文件筛选名);标题、“选择图片…”、“未选择背景图片”复用已有词条;已重新生成 desktop/internal/i18n/shared.generated.go。

功能矩阵(shared/platform-feature-matrix/features/)

  • 新增 workbench-background-image.json:两端 implemented、验证 pending,写明整机验证步骤。
  • editor-wallpaper-overview-ruler.json:说明 Viva 已有背景图、缩略图不画实底,Viva 编辑器没有概览标尺,本项不再单独实现。
  • workbench-islands-appearance.json:删掉验证说明里旧客户端的壁纸检查(Agent 对话、数据库工具栏等已不存在),改为指向 workbench-background-image。

测试

  • workbench_background_test.go(新增):选图后弹窗与设置页显示同一状态、整窗绘制与遮罩、档位切换、重启恢复、原图删除后仍可恢复、损坏副本被关闭并删除、超大与不支持的图片报原因且原背景不变、多个标签共用、副本写不进去时保留上一张图、副本写入期间移除后保持移除且不留副本、没有数据目录时不崩溃等。
  • settingswindow/workbench_background_view_test.go(新增):外观卡片的行、缩略图裁切和按钮。
  • termview/look_test.go(新增):透明 Background 与 Solid 的分工。
  • store_test.go:WriteFile 覆盖写入、Remove 删除与重复删除。

不在本 PR 范围内 / Out of scope


类型专属:feat

多平台对齐

Lithe 的目标是 macOS 与 Windows 能力对齐。必须选下面一项:

  • 两端都具备:合入后 macOS 与 Windows 都具备该能力(本 PR 同时实现,或本 PR 补齐另一端)——Viva 桌面端两端共用 desktop/ 实现,没有平台分支。
  • 只实现了 macOS:已为 Windows 提交跟进 Issue:#
  • 只实现了 Windows:已为 macOS 提交跟进 Issue:#
  • 刻意只属于某个平台(功能矩阵中另一端记为 platform-specific),理由:

范围检查 / Scope checks

功能矩阵 / Feature matrix

  • 已更新 shared/platform-feature-matrix/features/<id>.json:新增 workbench-background-image.json,两端实现状态 implemented、验证状态 pending(未做整机验证),证据路径、owner、验证动作已填写;同步更新了 editor-wallpaper-overview-ruler.json 与 workbench-islands-appearance.json
  • 已运行 ./scripts/platform-feature-matrix/verify-platform-feature-matrix.sh 和 ./scripts/platform-feature-matrix/verify-platform-feature-matrix-change.sh origin/preview HEAD
  • 没有提交 .artifacts/ 下生成的矩阵视图
  • [N/A] 没有更新矩阵的理由:已更新矩阵

统一日志系统 / Structured logging

  • 已接入统一日志系统:沿用 Viva 桌面端现有的 operationFailedEvent / app.action.performed 写法,操作名和动作名经 Core 目录校验(TestLogEventsMatchCatalog 用 ValidateCatalog: true 编码每个新事件);没有 print、直接写 stderr 或手写事件 ID
  • 一个操作只有一条终态:选图失败、副本写入失败、副本删除失败、恢复失败各记一次;副本缺失属于正常的“未选择”,不记日志;没有高频 INFO
  • 没有记录路径或图片内容:错误统一经 documentLogError 去掉路径,只保留错误码与错误域;文件名只出现在界面状态里
  • [N/A] node scripts/logging/generate-log-events.mjs --check 和 node scripts/logging/verify-log-usage.mjs:本仓库当前没有 scripts/logging/ 目录,无法运行;事件校验由上面的 TestLogEventsMatchCatalog 覆盖,没有新增任何豁免

界面文案 / i18n

  • 文案都走共享 i18n:在 shared/i18n/ 补齐 14 条中英词条,运行 node scripts/i18n/generate-i18n.mjs 并提交生成结果,没有手改生成文件;Go 代码通过生成的 i18n.Messages 取词条
  • 已运行 node scripts/i18n/verify-app-language-localization.mjs 和 node scripts/i18n/generate-i18n.mjs --check,没有放宽脚本或扩大豁免名单

逻辑归属 / Logic placement

  • 本 PR 只有呈现与原生 I/O:选文件、读写数据目录中的副本、解码缩放图片、裁切和遮罩绘制、Viva 界面状态,没有需要跨 UI 壳子共享的业务规则(30 MB / 6400 万像素是解码前的内存保护,三档不透明度是绘制参数);设置沿用桌面端现有的 settings.json
  • [N/A] 修改平台层已有的可共享逻辑时已迁入 Core:没有修改平台层已有的可共享逻辑
  • [N/A] 工具或运行时专属的知识放在插件或成熟上游:不涉及任何工具或运行时

性能与内存 / Performance and memory

  • 只在选图或启动恢复时读取、解码一次;没有轮询或定时器;只保留一张最长边不超过 2560 像素的 BGRA 位图(16:9 约 14 MB),设置缩略图与工作台共用同一份像素和位图 ID;移除背景时释放像素
  • 主线程没有图片文件读写:选图的读取、检查、解码,副本的写入、删除与启动读取都在后台任务里,窗口线程只交换解码结果;settings.json 的保存仍在窗口线程(既有写法,见“不在本 PR 范围内”)
  • [N/A] 性能基线:没有改动启动关键路径(恢复在后台)、输入、滚动、大文件、长输出或搜索,也没有改动 Viva 编辑器模块(desktop/internal/editor/**、desktop/internal/editorview/**),不需要编辑器基线;没有新增依赖(只用 Go 标准库 image/png、image/jpeg)

设置 / Settings

  • “工作台背景”有 ⓘ,说明图片会复制到数据目录、原图移动或删除不影响;没有“自动”或继承值
  • [N/A] 非敏感的工具配置读取并回显工具的真实来源:这是 Lithe 自己的外观设置,不是外部工具配置;打开设置页不写文件
  • [N/A] 新增或移动 macOS 设置页:没有新增或移动设置页,只在已有的 外观 卡片里加行

插件契约 / Plugin contract

N/A:不涉及插件契约、能力版本、插件函数协议或宿主方法。

发行包与资源 / Bundle and resources

  • 运行时不向 app bundle 或安装目录写入:背景图副本 workbench-background 由 store 写在用户数据目录(与 settings.json 同目录),用户选图时创建、移除或损坏时删除,归工作台所有,不影响代码签名和 Sparkle delta;没有内置默认图片,也没有新增打包资源;已运行 ./scripts/desktop/verify/verify-runtime-bundle-immutability.sh
  • [N/A] 下载、解压、生成或缓存资源:没有新增或修改此类资源

无用代码与重构 / Dead code and refactoring


验证 / Verification

命令或操作 平台 结果
scripts/desktop/desktop-go.sh test ./internal/workbench/ -count=1 macOS (arm64) 通过(141.8s)
scripts/desktop/desktop-go.sh test -race -count=3 -run Background ./internal/workbench/ macOS (arm64) 通过
scripts/desktop/desktop-go.sh test -run 'Background|Scale' -v ./internal/workbench/ macOS (arm64) 12 个用例全部通过
scripts/desktop/desktop-go.sh test ./internal/settingswindow/ macOS (arm64) 通过
scripts/desktop/desktop-go.sh test ./internal/store/ ./internal/termview/ -count=1 macOS (arm64) 通过
scripts/desktop/desktop-go.sh vet ./... macOS (arm64) 无问题
gofmt -l(改动文件) macOS 无输出
node scripts/platform-feature-matrix/generate-platform-feature-matrix.mjs --check — 222 条通过
./scripts/platform-feature-matrix/verify-platform-feature-matrix.sh — 222 条通过
./scripts/platform-feature-matrix/verify-platform-feature-matrix-change.sh origin/preview HEAD — 通过(capability records changed, relevance requires review)
node scripts/i18n/generate-i18n.mjs --check — en-US、zh-CN 11085/11085
node scripts/i18n/verify-app-language-localization.mjs — 通过
./scripts/desktop/verify/verify-runtime-bundle-immutability.sh macOS 通过

未能执行的检查和原因:

  • 没有启动真实应用做整机验收,也没有附明暗主题、中英文截图;矩阵中写的整机验证步骤待执行,两端验证状态保持 pending。
  • 没有在 Windows 上编译和运行(当前只有 macOS 机器);Windows 与 macOS 共用同一份 desktop/ 代码,没有平台分支。
  • scripts/logging/ 下的两个日志脚本在本仓库不存在,未运行;以 TestLogEventsMatchCatalog 代替。

提交前确认 / Final checks

  • 分支从 preview 创建,并已合并最新 origin/preview;目标分支:preview
  • 以 Draft 提交,本地验证通过后才标记 ready
  • 没有无关改动,没有提交 .build/、target/、dist/、.artifacts/ 等产物,没有凭据、个人路径和未脱敏的日志或截图
  • 本次只运行了 Go 测试进程,没有启动 Lithe 应用;已确认没有 Lithe 进程残留

- 用户自选 PNG/JPEG 作为工作台背景,不内置默认图片;图片复制到用户数据目录,重启后恢复
- 右下侧边按钮条打开背景弹窗,设置 → 外观 增加背景行(缩略图、状态、选择/移除、图片可见度)
- 淡、中、浓三档遮罩;没有图片时不显示图片可见度
- 打开项目时整窗绘制,面板、外框、编辑区、终端、Git 日志、差异、Markdown 预览透出背景,弹窗与菜单保持实底
- store 增加 WriteFile/Remove;新增功能矩阵 workbench-background-image,更新两条相关记录
TestLogEventsMatchCatalog 增加背景图的三个 operation_failed 操作
(workbench_background、workbench_background_save、workbench_background_remove)
和四个 app.action.performed 动作(workspace.background.open、choose、remove、strength),
确认 Core 目录校验接受这些字段值。
- 选图时在后台读取、校验并解码图片,再在后台写入副本;副本写好后才替换当前背景,
  写入失败时提示并保留原背景与原设置
- 移除背景、解码失败时在后台删除副本;启动恢复时在后台读取副本,副本缺失按未选择处理
- 选择、移除、恢复共用序号,过期的解码、写入、删除结果一律丢弃;副本的写入与删除串行执行
- 没有数据目录时不再空指针崩溃,所选图片只显示到退出
- 新增测试:副本写不进去时保留上一张图;副本写入期间移除背景后保持移除且不留副本
- 功能矩阵说明同步更新
@ghfind-review ghfind-review Bot added the review: high ghfind author score; see https://ghfind.com label Nov 13, 2026
@xiaoyumuxi
xiaoyumuxi marked this pull request as ready for review November 14, 2026 03:00
@xiaoyumuxi
xiaoyumuxi merged commit a750832 into preview Nov 14, 2026
5 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review: high ghfind author score; see https://ghfind.com

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant