本文描述 Codex Theme Studio的运行边界、模块模型和兼容策略。产品总览见 README,好友通讯见 好友与宠物串门。
- Codex 安装包保持不变,不解包、不重签、不替换资源。
- 浏览器只提交受约束的数据,不拥有任意文件、shell、CSS 或 JavaScript 能力。
- 所有主题输出由 Helper 确定性生成,输入相同则输出相同。
- DOM 增强找不到目标时必须降级,不能阻止背景和基础颜色工作。
- 应用、暂停和 renderer 重建都必须可重复执行。
Browser Studio
│ token-protected localhost JSON API
▼
Local HTTP server
├── ThemeSchema 字段白名单与范围验证
├── ThemeStore JSON / 图片原子存储
├── RuntimeState 当前主题与 CDP 会话
└── ThemeSupervisor
├── CodexAppController
├── CDP discovery / evaluate
├── deterministic ThemeRenderer
└── NativePetBridge
│
├── Codex main renderer
└── Codex avatar-overlay renderer
Codex pet runtime
│ authenticated REST + cursor-based long polling
▼
Companion Service (separate project)
└── users / friendships / visits / events
- 在 Studio 中央区域按 Codex 视口比例等比适配;默认基线为 1512×944,应用后同步实际 renderer 视口与 sidebar/main/composer 矩形。
- 本地解码图片和提取调色板。
- 维护未保存 draft、当前选择模块与交互状态。
- 只调用主题 CRUD、状态、应用和暂停等固定 API。
- 只监听
127.0.0.1。 - 静态页面不需要令牌,但 API 必须同时通过 token、Host、Origin 和 Fetch Metadata 校验。
- 没有文件浏览、通用命令或通用 CDP evaluate 路由。
- 读取内置主题和用户主题,内置主题只读。
- 检查 ID、真实路径、图片签名、大小和后缀。
- 创建主题时先写临时目录,再原子 rename;更新用户主题时通过临时目录和备份目录原位替换,ID 保持不变。
- 无效或半写入主题在列表阶段被忽略。
- 串行化应用与暂停操作,避免并发重启/注入。
- 复用已有受管理 CDP 会话;必要时请求一次正常重启。
- 监控 renderer target,发现新 target 时检查主题标记并补注入。
- 保存当前主题 ID、CDP 端口、实测布局和受限原生 UI 快照,以便 Helper 自身重启后继续恢复。
- 是独立项目、独立进程和独立数据目录,不由本地 Helper 代理。
- Codex pet runtime 直接使用服务地址和设备 Bearer 令牌通讯。
- 主题只保存服务开关和根地址,令牌按地址隔离保存在 Codex 本地存储。
- 提供设备身份、好友关系、在线心跳、串门状态机与有界事件长轮询。
- 服务不可用时只降级好友能力,不影响主题应用和本地访客状态机。
pet-care.mjs是独立、纯函数可测试的本机成长状态机,不依赖主题文件或好友服务。pet-profile.json只保存成长开关、状态徽标开关和每日目标;实际等级、经验、四项状态、冷却与连续陪伴保存在 Codex renderer 的pet-care:v1本地记录。- 每两小时结算一次温和状态变化,最多补算 24 小时;没有后台计时器,也没有省电模式。
- 原生
avatar-overlay只监听官方宠物点击并发送“摸摸”命令,不阻止 Codex 原事件,也不修改 React 私有状态。 - 好友来访互动只在本机追加陪伴经验;服务端仍只负责好友与串门事实。
主题 Schema 版本当前为 1。核心结构:
{
"schemaVersion": 1,
"id": "quiet-orbit-ab12cd34",
"name": "Quiet Orbit",
"palette": {
"accent": "#73DACA",
"secondary": "#8AA7FF",
"surface": "#12151A",
"text": "#EEF2F5"
},
"material": {
"glassOpacity": 0.82,
"blur": 20,
"radius": 16,
"backdropDim": 0.28
},
"composition": {
"positionX": 50,
"positionY": 50,
"scale": "cover"
},
"copy": {
"kicker": "CODING WITH CONTEXT",
"headline": "今天想构建什么?",
"subtitle": "主题定义保持简单,体验保持完整。"
},
"welcome": {
"placement": "native",
"positionX": 50,
"positionY": 22,
"alignment": "center",
"visibility": {
"icon": true,
"copy": true,
"cards": [true, true, true, true]
},
"modules": {
"icon": { "offsetX": 0, "offsetY": 0 },
"copy": { "offsetX": 0, "offsetY": 0 },
"cards": { "offsetX": 0, "offsetY": 0 }
},
"cardLabels": null,
"cardIcons": ["native", "native", "native", "native"]
},
"sidebar": {
"brand": "Codex",
"visibility": {
"newTask": true,
"scheduled": true,
"plugins": true,
"sites": true,
"pullRequests": true,
"chat": true
},
"actions": {
"newTask": { "label": null, "icon": "native" },
"plugins": { "label": "我的扩展", "icon": "sparkles" }
}
},
"composer": {
"style": "glass",
"glowIntensity": 0.7,
"imageOpacity": 0.28
},
"shell": {
"preset": "classic-blue",
"title": "Codex 2007",
"topbar": { "visible": true },
"petDock": { "visible": true, "width": 20, "title": "Codex 宠物", "placeholder": "将 Codex 宠物拖到这里" },
"statusbar": { "visible": false, "text": "Codex 已就绪" }
},
"surfaces": {
"chrome": "#2E82E6", "chromeHighlight": "#EAF6FF", "panel": "#EDF7FF", "panelAlt": "#DCEEFF",
"border": "#72A8D8", "code": "#F4FAFF", "inlineCode": "#DDEEFF"
},
"typography": { "ui": "classic", "code": "classic-mono", "scale": 1 },
"hero": "hero.webp"
}welcome.modules 使用相对 Codex 主内容区的百分比偏移,因此同一主题能随窗口尺寸缩放。Studio 0.3 不再编辑旧版整组欢迎区位移;加载到编辑器时会回到 native,再应用独立模块偏移。Renderer 仍接受旧字段,保证磁盘上的旧主题可被读取和暂停。
cardLabels 为 null 时保留 Codex 原生卡片文本;存在时可包含 0 到 4 项,并与 visibility.cards、cardIcons 等长。Renderer 优先读取卡片按钮的 aria-labelledby,避免依赖不稳定的嵌套层级。图标 key、侧边栏入口配置和输入框样式均经过 Schema 白名单校验。
Studio 中央画布使用 CSS Grid 提供可用区域,预览则按 Codex 视口宽高比取 contain 尺寸。默认采用实测的 1512 × 944,应用主题后从注入结果读取当前 renderer 的 window.innerWidth / innerHeight,并在监控探针中持续采集 sidebar、main 和 composer 的 getBoundingClientRect()。暂停主题后的探针会额外采集原生快捷卡片矩形、默认标签,以及首页、四张卡片和六个侧边栏入口的 SVG viewBox 与白名单绘制属性;快照写入 RuntimeState,Studio 用安全 DOM API 重建 SVG。Studio 使用这些矩形设置横纵容器比例,装饰性窗口栏不再占用 main 高度。拖拽和 renderer 注入都使用真实主内容区宽高百分比,因此改变 Studio 面板、浏览器尺寸或 Codex 窗口后不会产生横向或纵向漂移。指针按下只建立拖拽候选,超过 6px(触摸为 9px)才捕获移动;偏移会吸附原点,并限制在主内容区和输入框上方的安全范围内。
编辑态只在 Studio DOM 上增加选择框、模块标签、拖拽光标和 contenteditable。主题保存后只保留白名单数据;干净预览和 Codex renderer 均不会收到这些编辑器装饰。
| Studio 模块 | Renderer 目标 | 能力 | 降级行为 |
|---|---|---|---|
| Sidebar | .app-shell-left-panel |
品牌字符、六个已知入口的名称、图标和显隐 | 未找到入口时跳过该入口 |
| Icon | [data-testid="home-icon"] |
显隐、相对偏移 | 保留原生图标位置 |
| Copy | 原生 heading + 注入 kicker/subtitle | 文案、对齐、显隐、相对偏移 | 保留背景和基础颜色 |
| Cards | home suggestions wrapper | 四卡显隐、文本、相对偏移 | 未命中的卡片保留原生状态 |
| Composer | .composer-surface-chrome |
玻璃、荧光边缘、图片覆盖、材质与颜色 | 图片缺失时退化为玻璃输入框 |
| Native shell | .app-header-tint + #ctm-native-commandbar + 原生左/右侧边栏 + 输出面板 |
换肤原生顶栏、右上角控件和面板;快捷栏按钮代理到原生侧边栏入口 | 未命中时保留 Codex 原生状态与功能 |
| Native pet bridge | 独立 avatar-overlay renderer + 主 renderer 的宠物中心入口 |
保留官方宠物,监听官方点击作为摸摸,在旁边显示成长状态与访客 sidecar | 找不到原生 overlay 时成长面板仍可用,不伪造主宠物 |
只有非零模块偏移才写入覆盖 transform,避免破坏 Codex 图标自身可能存在的旋转或缩放。
主题模式:
- 创建或更新唯一的
#codex-theme-studio-style。 - 写入根主题标记,用于 probe 和幂等判断。
- 标记并换肤当前可见的原生顶栏控件、右侧边栏和输出面板;不改变原生点击与状态管理。
- 根据独立宠物档案挂载宠物中心,把成长摘要与访客状态同步给原生宠物 overlay;不创建自定义主宠物。
- 应用侧边栏品牌,以及入口名称、图标和显隐。
- 定位首页 heading,创建或复用 kicker/subtitle。
- 应用文案、对齐、模块显隐和卡片文本。
- 根据主内容区尺寸计算模块像素偏移,并通过有界周期刷新与 resize 监听在 DOM 重建后重新匹配。
pet-only 模式会先执行完整主题清理,再仅注入 #codex-pet-only-style、成长/好友运行时和原生宠物桥接,因此系统白色/黑色主题及 Codex 默认布局保持不变。暂停时只移除本项目创建的 style、属性和节点,并恢复记录过的原始文字;成长数据保留,重新启用后继续使用。
idle
→ loading-theme
→ locating-codex
→ stopping-codex (仅第一次受管理会话)
→ launching-codex (仅第一次受管理会话)
→ waiting-for-renderer
→ injecting
→ active
任意应用阶段 → error → retry
active → paused
active → injecting (主题切换或 renderer 重建)
注入分为三级:
- 根层:页面背景、颜色变量和 color-scheme,最少依赖 DOM。
- 稳定表面:侧栏、main、composer、dialog 等已有语义类名或 role。
- 实验增强:首页文案、卡片、消息气泡与操作栏,允许局部降级。
每次注入返回 selector 命中统计。后续 0.5 会把统计扩展成可视化兼容性报告,并按 Codex 版本维护基线。
主题文件是可导入资产。允许任意 CSS 会带来数据读取、界面覆盖、钓鱼和升级后不可控选择器等风险;允许任意 JavaScript 则直接突破 renderer 边界。因此当前 Schema 只暴露产品明确支持的 token 和布尔开关,由内部 renderer 映射到固定规则。