[Feature][Both]: 可撤销的主题安全试穿 / Reversible theme preview
中文
目标平台
macOS 与 Windows
问题与使用场景
当前从菜单栏或系统托盘选择已保存主题时,主题会直接替换当前配置。用户只有在切换完成后才能判断背景、文字对比度和安全区域是否适合自己的窗口布局。如果效果不合适,需要再次找到旧主题并手动切回;如果切换进程中断,用户也很难确认当前主题是否仍然完整。
这会让试用新主题带有不必要的风险,尤其是主题库逐渐变大以后。
期望方案
为已保存主题增加可撤销的「安全试穿」:
- 在改动活动主题前,分别创建当前主题和候选主题的完整快照。
- 两份快照都通过现有 payload、图片和路径校验后,才发布待确认事务并应用候选主题。
- 用户可以明确选择「保留此主题」或「恢复原主题」,确认框默认选择恢复。
- 如果试穿进程异常退出,下次启动或菜单刷新通过 PID 与进程启动时间识别过期事务,并自动恢复试穿前主题。
- 恢复成功前不删除备份;确认保留时先移走事务标记,再清理临时文件。
用户故事
- 作为普通用户,我希望先看到主题在当前 Codex 窗口中的实际效果,再决定是否保留。
- 作为经常切换主题的用户,我希望按回车或取消对话框时默认回到原主题,避免误操作。
- 作为维护者,我希望无效主题、路径越界、符号链接或中断进程不会破坏当前可用主题。
实现决策
- 第一阶段只覆盖「已保存主题」,不改变图片导入、编辑器或预设包格式。
- Windows 使用系统托盘原生确认框;只有注入器仍在运行且皮肤未暂停时才开始实时试穿。
- macOS 使用菜单栏入口和 AppleScript 对话框,复用现有本机 CDP 热重载,必要时走现有启动流程。
- 活动主题的发布顺序为图片先写入、
theme.json最后原子替换,避免观察到不完整的配置与图片组合。 - 自动恢复同时核对 PID 和启动时间,不能只依赖 PID 存活状态。
- 事务只存放在 Dream Skin 的用户状态目录,不修改官方 Codex 应用、
app.asar、签名、API Key 或 Base URL。
验收与测试
- 开始试穿后可以选择恢复,并逐字节还原原主题图片。
- 选择保留后候选主题继续生效,事务目录被清理。
- 候选主题无效时,活动主题不变,也不留下待确认事务。
- 使用仍然存活但启动时间不匹配的 PID 模拟 PID 复用时,可以恢复过期事务。
- macOS 拒绝符号链接替换的恢复快照;Windows 拒绝状态目录中的重解析路径。
- 菜单栏安装流程会同步试穿脚本和 Node 事务核心。
- PowerShell 5.1、PowerShell 7、Node 测试和 POSIX shell 测试均应覆盖各自的事务路径。
考虑过的替代方案
- 仅在切换前弹出确认框:用户还没有看到实际效果,无法作出有效判断。
- 只记住上一个主题 ID:主题文件可能在试穿期间被修改,无法保证准确恢复。
- 依赖固定超时自动恢复:阅读和比较主题所需时间因人而异,也不能处理进程崩溃后的状态。
范围外
- 主题参数的实时滑杆编辑
- 图片导入预览
- 定时换肤、在线主题市场或主题同步
- 对官方 Codex 安装包、认证或 API 配置的修改
优先级
Would use often / 会经常用
范围确认
- 这是 Dream Skin 的换肤与交互体验,不涉及 API 中转。
- 不修改官方 Codex 二进制、
app.asar或签名。
补充
此功能与 #63 的跨平台主题契约互补,但不依赖该契约。#63 解决主题包如何跨平台表达,本 Issue 解决用户如何安全地试用一个已经保存的主题。
English
Target platform
macOS and Windows
Problem and use case
Selecting a saved theme from the menu bar or system tray currently replaces the active theme immediately. Users can judge the artwork, text contrast, and safe areas only after the switch. If the result does not fit their window, they have to find the previous theme and switch back manually. An interrupted switch also makes it difficult to tell whether the active theme is still complete.
That makes experimenting with a growing theme library riskier than it needs to be.
Proposed solution
Add a reversible preview for saved themes:
- Before changing the active theme, create complete snapshots of both the current theme and the candidate.
- Validate both snapshots with the existing payload, image, and path checks before publishing a pending transaction or applying the candidate.
- Let the user choose either "Keep this theme" or "Restore previous theme". Restore is the default action.
- If the preview process exits unexpectedly, the next launch or menu refresh checks both the owner PID and its process start time, then restores an expired transaction.
- Keep the backup until restoration succeeds. When the user keeps the theme, move the transaction marker out of the active path before cleaning temporary files.
User stories
- As a user, I want to see a saved theme in my current Codex window before deciding to keep it.
- As someone who switches themes often, I want Enter or dialog cancellation to favor restoration so an accidental confirmation does not become permanent.
- As a maintainer, I want invalid themes, escaped paths, symbolic links, and interrupted processes to leave the last usable theme intact.
Implementation decisions
- The first version covers saved themes only. Image import, theme editors, and the preset format remain unchanged.
- Windows uses a native system tray confirmation dialog and starts a live preview only while the injector is running and the skin is not paused.
- macOS uses the menu bar and an AppleScript dialog. It reuses the existing loopback CDP hot reload and falls back to the current start flow when needed.
- Active theme publication writes the image first and atomically replaces
theme.jsonlast, so the watcher never sees an incomplete config and image pair. - Automatic recovery checks PID and process start time together. A live PID alone is not sufficient.
- The transaction stays inside the Dream Skin user state directory. It does not modify the official Codex app,
app.asar, signatures, API keys, or Base URL settings.
Acceptance and testing
- Canceling a preview restores the original theme and its exact image bytes.
- Keeping a preview leaves the candidate active and removes the transaction directory.
- An invalid candidate leaves the active theme unchanged and creates no pending transaction.
- A live PID with a mismatched start time simulates PID reuse and triggers stale recovery.
- macOS rejects a recovery snapshot replaced by a symbolic link; Windows rejects reparse points in managed state paths.
- The menu bar installer copies the preview wrapper and Node transaction core.
- PowerShell 5.1, PowerShell 7, Node, and POSIX shell tests cover their respective transaction paths.
Alternatives considered
- Confirm before switching: the user has not seen the actual result yet, so the confirmation is not useful.
- Store only the previous theme ID: files can change during the preview, which makes exact restoration unreliable.
- Use a fixed auto-revert timer: users need different amounts of time to compare themes, and a timer does not handle crashed processes by itself.
Out of scope
- Live sliders for theme parameters
- Preview for imported images
- Scheduled rotation, an online gallery, or theme synchronization
- Changes to the official Codex installation, authentication, or API configuration
Priority
Would use often
Scope check
- This concerns Dream Skin theming and UX, not API relays.
- It does not require changes to official Codex binaries,
app.asar, or signatures.
Further context
This complements #63 without depending on it. Issue #63 defines how a theme pack can be represented across platforms; this proposal defines how a user can safely try an already saved theme.
Source: Fei-Away/Codex-Dream-Skin