- 人工智能
- AI 应用
- 桌面应用
【免费下载链接】open-codesign
Open-source Claude Design alternative. One-click import your Claude Code / Codex API key. Prompt → prototype / slides / PDF. Multi-model (Claude, GPT, Gemini, Kimi, GLM, Ollama). BYOK, local-first, MIT.
导读
本文以 packages/core/src/prompts/sections/editmode-protocol.md 为主体,结合 Open CoDesign 仓库中shared、core、runtime、desktop各包的源码实现,完整讲解 EDITMODE 协议:Agent 如何在生成的页面源码中声明可调参数(品牌色、密度、字号、布局等),运行时如何把声明转换为--ocd-tweak-*CSS 变量,宿主应用如何据此渲染调节面板并回写源码。读完本文,你将掌握 EDITMODE 标记块的语法规范、绑定原则、校验与持久化链路,以及一套可直接复用的调参协议设计思路。
一、协议定位:给"生成物"注入可控的设计决策
EDITMODE(Edit Mode)是 Open CoDesign 中由 Agent 生成、宿主应用消费的一层源码内嵌调参协议。它解决的是这样一个问题:LLM 生成的原生 HTML/JSX/CSS 页面(artifact)在浏览器里渲染后,用户希望像操作设计工具一样拖拽调色、调密度、换布局,而不需要重新生成或手改源码。
协议的核心约定可以概括为三点:
- 暴露少量有意义的决策:当需要或确实有用时,暴露 2~5 个关键设计决策,例如品牌 token(brand token)、密度(density)、字号比例(type scale)、已实现的布局/强调方式(implemented layout/emphasis)、内容可见性(content visibility);
- 不为凑数而加控件:不得为了满足某个数量配额而推迟交付第一版可用的切片(first working slice),也不得添加无实际意义的控件;
- 允许空声明:当控件没有必要、或用户拒绝提供控件时,空对象
{}是合法且推荐的答案。
这一原则在 packages/core/src/prompts/compose-full.ts 中落地:EDITMODE_PROTOCOL是create / tweak / revise三种模式共用系统提示词的固定组成之一,而tweak模式还会追加一份更严格的TWEAKS_PROTOCOL小节。
二、TWEAK_DEFAULTS:源码顶部的扁平 JSON 声明
协议要求在源码靠近顶部的位置声明一个扁平的 JSON 对象,并用一对注释标记包裹:
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "accentColor": "#28665c", "density": 1 }/*EDITMODE-END*/;2.1 标记块(marker block)格式
标记必须成对出现,即/*EDITMODE-BEGIN*/与/*EDITMODE-END*/。在 packages/shared/src/editmode.ts 中,解析器通过扫描/*注释找到标记名(compactMarkerName会剔除标记内的空白,因此/* EDITMODE-BEGIN */与/*EDITMODE-BEGIN*/等价),并提取两个标记之间的内容作为 JSON 字面量。规则包括:
- 缺少标记 = 没有 tweak 块;标记存在但不成对或内容损坏 = 协议错误;
- 标记之间的空白在往返(round-trip)重写时会被保留;
- 嵌套结构非法——只有
{ "key": value }一层,不允许数组、嵌套对象。
2.2 值类型约束
键使用camelCase;值仅允许三种基本类型:
| 类型 | 示例 | 说明 |
|---|---|---|
string | "accentColor": "#28665c" | 颜色、字体名、布局名等 |
number | "density": 1 | 数值型参数,必须给出安全取值区间 |
boolean | "showFooter": true | 开关型参数 |
标记块内禁止出现注释、表达式、尾逗号、数组和嵌套对象。这一约束在 parseEditmodeBlock 中被强制执行:块内必须是合法 JSON,且每个 token 值必须是 string/number/boolean,否则抛出ARTIFACT_PROTOCOL_INVALID错误(error-codes.ts 中定义的协议错误码)。
2.3 默认值一致性
协议明确要求:
- 默认值必须与渲染出的源码一致,并且反映用户当前的选择;
- 参数名要有意义,数字要落在安全区间内;
- 可选的
TWEAK_SCHEMA细节(控件形态、范围、步长)属于craft-polish方法阶段,不在TWEAK_DEFAULTS中展开。
三、绑定:把声明变成真实可见的调节
声明本身不产生任何效果——关键在绑定(binding)。
3.1 CSS 变量:--ocd-tweak-<kebab-key>
运行时会把每个 camelCase 键映射为一条 CSS 自定义属性:
--ocd-tweak-<kebab-key>例如density→--ocd-tweak-density,accentColor→--ocd-tweak-accent-color。源码中普通的视觉值必须通过它们来绑定:
padding: calc(var(--ocd-tweak-density) * 1rem);实现细节位于 packages/runtime/src/tweaks-bridge.ts:桥接脚本里的toKebab()负责把 camelCase 键转成 kebab-case(处理大小写边界、下划线、非法字符、连字符折叠),applyCssVars()再把这些变量写到document.documentElement上;布尔值被映射为'1' / '0',字符串与数字原样输出。
3.2 结构性选择必须"选择真实实现"
协议特别强调:结构性的选择(structural choices)必须真的去切换已实现的代码路径,而不是堆一段毫无作用的 JSON。比如layout: "split"必须让页面实际渲染为分栏布局;如果值变了而 UI 不变,这个参数就是"惰性 JSON"(inert JSON),属于违规。同理,共享的选择(如品牌色、密度)要应用到相关屏幕(screens)上,而不能只改单一文件。
3.3tweaks()只发现、不绑定
tweaks()是 Agent 侧的只读扫描工具,它的职责是发现工作区里已声明的 EDITMODE 值,供宿主汇总展示;它不会创建控件、建立绑定,也不会去更新未绑定到 CSS 变量的文件。这一点在 packages/core/src/tools/tweaks.ts 的注释里写得很直白:
This tool is advisory. The renderer parses its active source independently; scanner results neither register controls nor bind values to the preview.
其默认扫描模式为['**/*.html', '**/*.jsx', '**/*.css', '**/*.js'](见 tweaks.ts),支持传入自定义patterns;结果按"每个文件一个 token 袋"(parseTweakBlocks)或"扁平三元组"(aggregateTweaks,即{file, key, value})两种形态返回。由于宿主面板只读取当前预览源码,扫描未使用的起始模板中的控件并不会影响实际预览。
四、校验与自检:让声明经得起宿主解析
4.1 声明必须通过的类型/范围校验
packages/shared/src/tweak-source.ts 中的inspectTweakSource()是宿主的统一校验入口,它组合了parseEditmodeBlock与parseTweakSchema,输出四种状态:
| 状态 | 含义 |
|---|---|
missing | 源码中没有 EDITMODE 标记块 |
empty | 标记块存在但内容为{}(合法) |
ready | 块与 schema 解析成功,且 token 与 schema 类型/范围一致 |
invalid | 标记不成对、JSON 非法、token 与 schema 声明的控件类型/选项/范围不符 |
校验严格到"控件不能造假"的程度:tweak-source.test.ts 专门验证——当声明值"wide"配上{kind:'number'}、数值32超出min/max、step:0、枚举值不在options中、布尔值不匹配时,一律判定为invalid,避免宿主渲染出"显示假回退值"的控件。另外,只有 CSS 变量或未加标记的 JS 默认对象(如const TWEAK_DEFAULTS = {...}但无标记)都不会被推断为控件。
4.2 Agent 侧自检清单
协议要求 Agent 在交付前完成两项核对:
- 检查一个代表性的变体(representative alternate):改动后要实际验证备选值下的渲染效果,而不能只在默认值下自证;
- 恢复当前默认值:验证完毕后把默认值还原,保证源码状态与用户当前选择一致。
4.3 预览不等于宿主面板
一个容易踩的坑:artifact 预览里自己写的调参 UI 不能证明宿主调节面板的行为。宿主的面板(TweakPanel)有自己的读取与绑定逻辑,源码里存在"变体开关"只能说明页面实现了切换能力,不代表宿主控件能正常工作。因此在交付前,应通过宿主面板或等价通道做端到端验证,而不是"看源码变体就当验证过了"。
五、持久化与版本同步:用户的每一次选择都要被记住
5.1 后续编辑中保留用户选择
协议明确要求:在之后的编辑(revision)中,保留用户在面板上做出的选择,不能一改代码就把参数打回默认值。这也与 docs/research/11-custom-sliders.md 中记录的已知风险一致——"陈旧 EDITMODE 块"(stale EDITMODE block on revision)被列为必须规避的坑:应用文本修订时要保留 EDITMODE 块里的值,而不是重置为默认。
5.2 宿主侧:postMessage 实时流 + 防抖回写
桌面端的 TweakPanel.tsx 展示了完整的宿主链路:
- 面板用
inspectTweakSource(previewSource)解析出{block, schema},维护一份liveTokens工作副本; - 每次调节通过
postMessage({type: 'codesign:tweaks:update', tokens})推送给 iframe,实现不刷新页面的实时预览; - 持久化回写到工作区源码采用防抖(
persistTweakDebounce),避免每个按键都触发文件写入;写回使用persistTweakTokensToWorkspace合并进 EDITMODE 块,并做"生成中取消保存"等并发保护。
5.3 iframe 侧:桥接脚本保持组件状态
packages/runtime/src/tweaks-bridge.ts 注入到 iframe 的桥接脚本解决了"每次调参都整页重载"的性能问题(无桥接时每次编辑都要重新挂载 React、重跑 Babel 与 Agent 脚本,产生约 300–500ms 白屏)。它把 token 映射为 CSS 变量后,在下一帧用cloneElement重渲染已有 React 元素,保留组件类型与模块作用域(也即 hook 状态)。对于useMemo/useCallback缓存了 token 值、或组件是React.memo/纯组件的情况,桥接会回退到"重跑模块"兼容模式,并向宿主发送codesign:tweaks:compatibility通知。协议建议:在非记忆化组件内读取 token,以便交互状态在实时更新时得以保留。
对应地,editmode-runtime.ts 的bindEditmodeTokensToRuntime()会在注入阶段把源码中的TWEAK_DEFAULTS声明替换为window.__codesign_tweaks__.tokens引用,让模块读取的就是"活"的 token 对象。
5.4 实质性 token 变更必须显式同步 DESIGN.md
协议最后一条红线:在实质性 token 变更(substantive token changes)时,要显式核对并同步对应的 DESIGN.md 条目,永远不要假设它会自动同步。这与 docs/v0.2-plan.md 中"用户持续调整某 EDITMODE 值 → Agent 主动 propose 'promote to DESIGN.md'"的演进方向一致:EDITMODE 是设计系统 token 的"草稿区",而 DESIGN.md 是最终沉淀。
六、TWEAK_SCHEMA:可选但强力的控件形态声明
虽然TWEAK_DEFAULTS只负责值,协议允许在craft-polish阶段附带TWEAK_SCHEMA标记块,声明每个 token 的控件形态。格式(见 editmode.ts):
const TWEAK_SCHEMA = /*TWEAK-SCHEMA-BEGIN*/{ "accentColor": { "kind": "color" }, "radius": { "kind": "number", "min": 0, "max": 32, "step": 2, "unit": "px" }, "layout": { "kind": "enum", "options": ["split", "stacked"] }, "dense": { "kind": "boolean" }, "label": { "kind": "string", "placeholder": "Button label" } }/*TWEAK-SCHEMA-END*/;支持的五种kind及参数(TokenSchemaEntry):
| kind | 附加字段 | 宿主控件 |
|---|---|---|
color | 无 | 颜色选择器 |
number | min、max、step、unit | 范围滑块 |
enum | options: string[](非空) | 分段选择器 |
boolean | 无 | 开关 |
string | placeholder | 文本输入 |
schema 是建议性的:条目可以省略,但一旦出现TWEAK-SCHEMA-BEGIN/END标记就必须是合法 JSON 且条目形状合法,否则整份声明被判为invalid。TweakPanel 正是依据 schema 来决定渲染哪种控件(TweakPanel.tsx:number 用min/max/step/unit的滑块、enum 用选项数组等)。
七、与 TWEAKS_PROTOCOL 的配合:tweak 模式下的收紧
editmode-protocol.md是"通用规则",而 tweaks-protocol.md 是tweak模式下的收紧版,二者在 loader.ts 中分别加载,由 compose-full.ts 在mode === 'tweak'时追加。TWEAKS_PROTOCOL 的要点:
- 读取活动源码,而不是起始模板的默认值;
- 改值时,键必须与现有
TWEAK_DEFAULTS键一致,只改被请求的标记 JSON,保留其他值/文件并遵守范围; - 检查渲染绑定;
- 只有在被请求时才新增控件:通过 EDITMODE + CSS 变量或 JSX 绑定已有的有用值,并保持初始视觉不变;
- 同样的铁律:
tweaks()只发现、不绑定;值编辑期间不做重新设计。
八、把协议接到 Agent 提示词:一条完整调用链
综合各包源码,一条完整的 EDITMODE 调用链是:
- compose-full.ts 组装系统提示词,
EDITMODE_PROTOCOL固定出现,tweak模式追加TWEAKS_PROTOCOL; - 每个
.md小节由 sections/loader.ts 在模块加载时读取一次,暴露为EDITMODE_PROTOCOL、TWEAKS_PROTOCOL等冻结字符串常量(PROMPT_SECTIONS还记录editmodeProtocol: 'sections/editmode-protocol.md'的源文件映射,便于追溯); - Agent 按协议在生成源码顶部写出
TWEAK_DEFAULTS(及可选TWEAK_SCHEMA)标记块; - agent.ts 注册
tweaks工具(makeTweaksTool),当用户偏好为 enabled 时提示"实现有用的 source-backed 控件后调用tweaks()";用户显式拒绝时则注入"不要调用tweaks()"(agent.ts); - 宿主侧 TweakPanel.tsx 解析预览源码、渲染控件、postMessage 实时更新并防抖回写;
- iframe 侧 tweaks-bridge.ts 消费更新、映射 CSS 变量、保留 React 状态;
- 生成测试(generate.test.ts)与提示词测试(connected-product.test.ts)都会断言协议措辞,例如"
tweaks()discovers values; it does not create bindings"必须出现在提示词中。
九、实战检查清单
把协议浓缩为交付前可逐条核对的清单:
- 只在确有需要时暴露 2~5 个控件,不凑数;不需要时写
{} TWEAK_DEFAULTS位于源码顶部,使用成对/*EDITMODE-BEGIN*/.../*EDITMODE-END*/标记- 键为 camelCase,值仅为 string/number/boolean,块内无注释/表达式/尾逗号/数组/嵌套对象
- 默认值与渲染结果、用户当前选择一致;数字在安全区间
- 视觉值通过
--ocd-tweak-<kebab-key>绑定(如calc(var(--ocd-tweak-density) * 1rem));结构性选择真的切换实现 - 共享选择已应用到相关屏幕;
tweaks()仅用于发现,不代替绑定 - 检查一个代表性变体并恢复默认值;不把源码变体当作宿主面板行为的证明
- 后续编辑保留用户选择;实质性 token 变更显式同步 DESIGN.md
- 如附带
TWEAK_SCHEMA,其标记与条目必须合法(参考 tweak-source.test.ts 的校验用例)
遵循这套协议,Agent 生成的每一个页面都能获得"可被宿主精确调参、可被持久化、可被升级进设计系统"的能力——这正是 EDITMODE 与普通硬编码参数的本质区别。
- 人工智能
- AI 应用
- 桌面应用
【免费下载链接】open-codesign
Open-source Claude Design alternative. One-click import your Claude Code / Codex API key. Prompt → prototype / slides / PDF. Multi-model (Claude, GPT, Gemini, Kimi, GLM, Ollama). BYOK, local-first, MIT.
相关推荐
open-codesign 自定义滑块与 EDITMODE 参数调节协议:从研究文档到运行时落地的完整实现解析
open codesign 自定义滑块与 EDITMODE 参数调节协议:从研究文档到运行时落地的完整实现解析 本文以仓库研究文档 docs/research/
人工智能AI 应用桌面应用Encore 实战指南:使用 `encore exec` 在完整基础设施上下文中运行脚本与数据库种子填充
Encore 实战指南:使用 encore exec 在完整基础设施上下文中运行脚本与数据库种子填充 encore exec 是 Encore CLI 提供的脚
人工智能AI 应用桌面应用如何高效使用开源WeMod增强工具:完整实战指南
如何高效使用开源WeMod增强工具:完整实战指南 WandEnhancer是一款专为WeMod游戏修改器设计的开源增强工具,通过本地客户端配置扩展和用户体验优化
人工智能AI 应用桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考