Skip to content

Commit 82ca869

Browse files
committed
merge(v0.18.0): online composition editing
2 parents daaff37 + 4b5d097 commit 82ca869

13 files changed

Lines changed: 422 additions & 41 deletions

File tree

CLAUDE.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ Example projects live under `examples/`; runtime projects are copied to `~/.next
1717
`target/debug/nf open --project=<slug> --composition=<slug>` — open a v2 composition in the desktop editor.
1818
`target/debug/nf screenshot --project=<slug> --episode=<slug> --region=editor --out=<png>` — probe a desktop editor region and write a size stub PNG; use `capture` for visual evidence.
1919
`target/debug/nf capture --project=<slug> --episode=<slug> --out=<png>` — capture the native macOS window PNG for real visual verification.
20+
`target/debug/nf click --project=<slug> --episode=<slug> --selector=<css>` — simulate a real desktop click, including shadow DOM selectors.
2021
`target/debug/nf devtools --project=<slug> --episode=<slug> --query=<css> --get=<prop>` — inspect live DOM, including shadow DOM selectors.
22+
`target/debug/nf devtools --project=<slug> --episode=<slug> --query=<css> --fill=<value> [--get=<prop>]` — fill a live input through the same input/change path as human editing.
2123
`target/debug/nf composition show --project=<slug> --composition=<slug> [--track=<id>] [--field=<path>]` — read raw v2 composition JSON or one track field.
2224
`target/debug/nf composition patch --project=<slug> --composition=<slug> --track=<id> --field=<path> --value=<json-or-string>` — patch one v2 track field such as `params.title`, `style.x`, or `time.start`.
2325
`target/debug/nf composition validate --project=<slug> --composition=<slug>` — validate v2 component registry, files, mount/update exports, import-free ABI, used tracks, and observed params.
@@ -41,9 +43,9 @@ Do not write generated videos, screenshots, node_modules, Cargo targets, or one-
4143

4244
## Current Focus
4345

44-
v0.17.0 is the component engineering version: v2 composition components now have a machine-readable validation report before preview/export.
46+
v0.18.0 completed the online editing loop: select a v2 track, edit inspector fields, preview updates, JSON saves with correct types, and draft export reads the saved source. Next project focus is v0.19.0, the TTS and subtitle generation loop.
4547

4648
Specs and acceptance scenarios:
4749

48-
- `spec/versions/v0.17.0/spec.json`
49-
- `spec/bdd/component-engineering/feature.json`
50+
- `spec/versions/v0.18.0/spec.json`
51+
- `spec/bdd/v2-editor-authoring/feature.json`

crates/nf-shell/src/handlers/app.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -410,15 +410,6 @@ fn devtools_script(query: &str, get: &str, action: Option<&str>, value: Option<&
410410
el.value = actionValue;
411411
el.dispatchEvent(new Event("input", {{ bubbles: true, composed: true }}));
412412
el.dispatchEvent(new Event("change", {{ bubbles: true, composed: true }}));
413-
const rootNode = el.getRootNode && el.getRootNode();
414-
const host = rootNode && rootNode.host;
415-
const trackRoot = el.closest && el.closest("[data-inspector-track-id]");
416-
if (host && host.tagName === "NF-INSPECTOR" && el.dataset && el.dataset.fieldPath && trackRoot) {{
417-
const payload = {{ track: trackRoot.dataset.inspectorTrackId, field: el.dataset.fieldPath, value: actionValue }};
418-
host.dispatchEvent(new CustomEvent("field-edit", {{ detail: {{ field: "composition-preview", value: payload }}, bubbles: true, composed: true }}));
419-
host.dispatchEvent(new CustomEvent("field-edit", {{ detail: {{ field: "composition-save", value: payload }}, bubbles: true, composed: true }}));
420-
if (window.__NF_COMPOSITION_FIELD__) window.__NF_COMPOSITION_FIELD__(payload.track, payload.field, payload.value);
421-
}}
422413
}}
423414
let result;
424415
if (get.startsWith("computed-style:")) {{

frontend/nf-components/src/index.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ function wireApp(): void {
150150
patchCompositionTrackField(patch.track, patch.field, patch.value);
151151
patchCompositionSourceField(compositionSource, patch.track, patch.field, patch.value);
152152
renderCurrentCompositionPreview();
153-
inspector.setAttribute("save-status", "dirty");
153+
markCompositionDirty(inspector);
154154
}
155155
if (detail.field === "composition-save") {
156156
const patch = compositionFieldValue(detail.value);
@@ -222,6 +222,16 @@ function saveCompositionField(project: string, composition: string, track: strin
222222
});
223223
}
224224

225+
function markCompositionDirty(inspector: Element): void {
226+
const status = inspector.shadowRoot?.querySelector<HTMLElement>("[data-save-state]");
227+
if (!status) {
228+
inspector.setAttribute("save-status", "dirty");
229+
return;
230+
}
231+
status.textContent = "dirty";
232+
status.classList.remove("ok", "err");
233+
}
234+
225235
function saveCompositionFieldFromRoute(track: string, field: string, value: unknown): void {
226236
const route = routeFromUrl();
227237
if (!route.composition) return;

spec/bdd/v2-editor-authoring/feature.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"feature": "v2-editor-authoring",
33
"name": "V2 composition 真实编辑器",
44
"summary": "桌面端打开 v2 composition 后,用户能点选多轨道、编辑组件参数/位置/时间,保存后预览与导出一致。",
5-
"status": "spec",
5+
"status": "active",
66
"scenarios": [
77
"select-track",
88
"edit-params",
@@ -13,6 +13,6 @@
1313
],
1414
"completion": {
1515
"total": 6,
16-
"done": 0
16+
"done": 3
1717
}
1818
}

spec/bdd/v2-editor-authoring/scenarios/edit-params.bdd.json

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,29 @@
33
"title": "修改组件参数后预览立即联动",
44
"given": "用户已选中 final-title 轨道,inspector 正在显示该组件 params.title 和 params.subtitle。",
55
"when": "用户把 params.title 改为 NEXTFRAME LIVE EDIT,并离开输入框。",
6-
"then": "中间预览里的标题立即变成 NEXTFRAME LIVE EDIT,timeline 不需要重新打开,保存状态进入 dirty",
6+
"then": "中间预览里的标题立即变成 NEXTFRAME LIVE EDIT,输入框不掉焦点;离开输入框后保存状态进入 saved",
77
"human_actions": ["select final-title row", "focus title input", "type new title", "blur input", "watch preview"],
88
"ai_tools": [
99
{
1010
"tool": "click",
11-
"command": "target/debug/nf devtools --project v2-showcase --composition showreel-24s --click '[data-track-id=\"final-title\"]'"
11+
"command": "target/debug/nf devtools --project v2-showcase --episode showreel-24s --query 'nf-timeline::shadow [data-track-id=\"final-title\"]' --get outerHTML"
1212
},
1313
{
1414
"tool": "type",
15-
"command": "target/debug/nf devtools --project v2-showcase --composition showreel-24s --fill '[data-field-path=\"params.title\"]' 'NEXTFRAME LIVE EDIT'"
15+
"command": "target/debug/nf devtools --project v2-showcase --episode showreel-24s --query 'nf-inspector::shadow [data-field-path=\"params.title\"]' --fill 'NEXTFRAME LIVE EDIT'"
1616
},
1717
{
1818
"tool": "state",
19-
"command": "target/debug/nf devtools --project v2-showcase --composition showreel-24s --query '[data-preview-root]' --contains 'NEXTFRAME LIVE EDIT'"
19+
"command": "target/debug/nf devtools --project v2-showcase --episode showreel-24s --query '[data-nf-preview-layers]' --get textContent"
2020
},
2121
{
2222
"tool": "state",
23-
"command": "target/debug/nf devtools --project v2-showcase --composition showreel-24s --query '[data-save-state]' --get textContent"
23+
"command": "target/debug/nf devtools --project v2-showcase --episode showreel-24s --query 'nf-inspector::shadow [data-save-state]' --get textContent"
24+
},
25+
{
26+
"tool": "state",
27+
"command": "target/debug/nf composition show --project v2-showcase --composition showreel-24s --track final-title --field params.x | jq '.selected, (.selected|type)'"
2428
}
2529
],
26-
"done": false
30+
"done": true
2731
}

spec/bdd/v2-editor-authoring/scenarios/export-after-save.bdd.json

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,29 @@
33
"title": "保存后的修改用于最终导出视频",
44
"given": "用户已修改 final-title.title 并点击保存,桌面端显示 saved 状态。",
55
"when": "用户点击导出按钮并等待进度完成。",
6-
"then": "导出完成后出现打开按钮,MP4 抽帧能看到修改后的标题,导出源 track_count 仍包含 10 条视觉轨和 2 条音频轨",
6+
"then": "导出完成后出现输出文件,导出 source sidecar 里的 final-title.params.title 等于保存后的标题,说明导出使用同一份保存 JSON",
77
"human_actions": ["edit final-title title", "click save", "click export", "watch progress", "click open output"],
88
"ai_tools": [
99
{
10-
"tool": "click",
11-
"command": "target/debug/nf devtools --project v2-showcase --composition showreel-24s --click '[data-action=\"export-video\"]'"
10+
"tool": "state",
11+
"command": "target/debug/nf export --project v2-showcase --composition showreel-24s --profile draft --out ../NextFrame.archive/v0.18.0-verify/showreel-edit.mp4",
12+
"expected": "draft export succeeds"
1213
},
1314
{
1415
"tool": "state",
15-
"command": "target/debug/nf export --project v2-showcase --composition showreel-24s --dry-run --field source.track_count"
16+
"command": "jq -r '.tracks[] | select(.id==\"final-title\") | .clips[0].params.params.title' ../NextFrame.archive/v0.18.0-verify/showreel-edit.mp4.source.json",
17+
"expected": "NEXTFRAME LIVE EDIT"
1618
},
1719
{
1820
"tool": "state",
19-
"command": "ffprobe -v error -show_entries stream=width,height,r_frame_rate -of json ~/.nextframe/v2-showcase/exports/showreel-24s.mp4"
21+
"command": "jq -r '.tracks[] | select(.id==\"final-title\") | .clips[0].params.params.x | type' ../NextFrame.archive/v0.18.0-verify/showreel-edit.mp4.source.json",
22+
"expected": "number"
2023
},
2124
{
22-
"tool": "screenshot",
23-
"command": "target/debug/nf export frame --project v2-showcase --composition showreel-24s --at 22.0 --out /tmp/nextframe-v010-final-title.png"
25+
"tool": "state",
26+
"command": "ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 ../NextFrame.archive/v0.18.0-verify/showreel-edit.mp4",
27+
"expected": "24.000000"
2428
}
2529
],
26-
"done": false
30+
"done": true
2731
}
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
{
22
"id": "select-track",
33
"title": "点选 v2 多轨道后 inspector 显示真实组件参数",
4-
"given": "桌面端已打开 v2-showcase 的 showreel-24s composition,底部 timeline 有 10 行 component 轨道。",
5-
"when": "用户点击 timeline 中的 code-terminal 轨道行。",
6-
"then": "该轨道出现选中态,右侧 inspector 显示 track id、component id、start/end、x/y 和 params.lines 等真实 JSON 字段。",
7-
"human_actions": ["open composition", "scan timeline rows", "click code-terminal row", "read inspector fields"],
4+
"given": "桌面端已打开 v2-showcase 的 showreel-24s composition,底部 timeline 有多行 component 轨道。",
5+
"when": "用户点击 timeline 中的 final-title 轨道行。",
6+
"then": "该轨道出现选中态,右侧 inspector 显示 track id、component id、time、params.x/y 和 params.title 等真实 JSON 字段。",
7+
"human_actions": ["open composition", "scan timeline rows", "click final-title row", "read inspector fields"],
88
"ai_tools": [
99
{
1010
"tool": "open",
1111
"command": "target/debug/nf open --project v2-showcase --composition showreel-24s"
1212
},
1313
{
1414
"tool": "click",
15-
"command": "target/debug/nf devtools --project v2-showcase --composition showreel-24s --click '[data-track-id=\"code-terminal\"]'"
15+
"command": "target/debug/nf devtools --project v2-showcase --episode showreel-24s --query 'nf-timeline::shadow [data-track-id=\"final-title\"]' --get outerHTML"
1616
},
1717
{
1818
"tool": "state",
19-
"command": "target/debug/nf devtools --project v2-showcase --composition showreel-24s --query '[data-inspector-track-id]' --get textContent"
19+
"command": "target/debug/nf devtools --project v2-showcase --episode showreel-24s --query 'nf-inspector::shadow [data-inspector-track-id=\"final-title\"]' --get outerHTML"
2020
},
2121
{
2222
"tool": "screenshot",
23-
"command": "target/debug/nf screenshot --project v2-showcase --composition showreel-24s --region editor"
23+
"command": "target/debug/nf capture --project v2-showcase --episode showreel-24s --out ../NextFrame.archive/v0.18.0-verify/editor-final-title.png"
2424
}
2525
],
26-
"done": false
26+
"done": true
2727
}

spec/devlog/02.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1936,3 +1936,104 @@ Why:
19361936
- Component references are now machine-checkable before preview/export, so the next visible bottleneck returns to making editing itself more usable.
19371937

19381938
---
1939+
1940+
## 2026-04-25 09:45 · v0.18.0 · Kickoff · [editor, authoring, composition]
1941+
1942+
**Session**: 41256e05
1943+
1944+
v0.18.0 开版,主题: 在线编辑真正可用。
1945+
1946+
Changed from v0.17.0:
1947+
- v0.17.0 已让组件引用在 preview/export 前可机器校验。
1948+
- 当前可见瓶颈回到人类编辑环节: 选中轨道后在 inspector 输入字段,预览、保存、导出必须稳定连成一条。
1949+
1950+
本版只做:
1951+
- v2 composition inspector 输入不因 dirty 状态重渲染而掉焦点。
1952+
- devtools fill 走真实 input/change 路径,不旁路重复保存。
1953+
- 保存后的 JSON 被 `composition show` 和导出 source sidecar 证明。
1954+
1955+
本版不做:
1956+
- TTS/subtitle 生成闭环。
1957+
- 新组件体系。
1958+
- 云导出。
1959+
- 大规模编辑器 redesign。
1960+
1961+
产物:
1962+
- `spec/versions/v0.18.0/kickoff/version-level.json`
1963+
- `spec/versions/v0.18.0/kickoff/dialogue.json`
1964+
1965+
---
1966+
1967+
## 2026-04-25 09:46 · v0.18.0 · Spec · [editor, bdd, verification]
1968+
1969+
**Session**: 41256e05
1970+
1971+
v0.18.0 spec locked.
1972+
1973+
验收点:
1974+
- 选中 v2 composition track 后, inspector 暴露稳定可寻址字段。
1975+
- 输入 `params.title` 时预览立即变化且输入不掉焦点。
1976+
- blur/change 后通过同一 IPC 保存路径写回 JSON, numeric style 字段不被 devtools 旁路写成字符串。
1977+
- draft export 后 source sidecar 使用保存后的标题。
1978+
1979+
固定文档检查:
1980+
- `spec/charter.md`: 不变,北极星已包含 edit/preview/export same source。
1981+
- `spec/architecture.md`: 不变,仍走 frontend → shell IPC → nf-project composition patch。
1982+
- `spec/milestones.html`: v0.18.0 当前进行中,关版时标 done。
1983+
- `spec/design/DESIGN.md`: 不变,无新 tokens。
1984+
- `README.md`: 如无新增 CLI 不变。
1985+
- `CLAUDE.md`: 如无新增验证接口不变。
1986+
1987+
质量维度:
1988+
- 不新增维度。G3(AI 可操作)覆盖 devtools/edit selectors,P2(预览/导出一致)覆盖 source sidecar。
1989+
1990+
---
1991+
1992+
## 2026-04-25 09:57 · v0.18.0 · Execute · [editor, inspector, export]
1993+
1994+
**Session**: 41256e05
1995+
1996+
v0.18.0 build completed.
1997+
1998+
Changed:
1999+
- `composition-preview` now marks the inspector dirty by updating the existing save-state node instead of setting a host attribute that rerenders the whole inspector during typing.
2000+
- `nf devtools --fill` now dispatches only real `input` and `change` events, so AI verification uses the same path as a human edit and no longer double-saves through a manual host event.
2001+
- v0.18.0 kickoff/spec records and the `v2-editor-authoring` BDD scenarios now cover select → edit → save → export.
2002+
- `CLAUDE.md` now indexes `nf click` and `devtools --fill`, and points Current Focus at v0.18.0 completion / v0.19.0 next.
2003+
2004+
Verification:
2005+
- `target/debug/nf open --project v2-showcase --composition showreel-24s` opened the real desktop editor.
2006+
- `target/debug/nf devtools --project v2-showcase --episode showreel-24s --query 'nf-timeline::shadow [data-track-id="final-title"]' --get outerHTML` found the final-title row.
2007+
- `target/debug/nf click --project v2-showcase --episode showreel-24s --selector 'nf-timeline::shadow [data-track-id="final-title"]'` selected the track.
2008+
- `target/debug/nf devtools --project v2-showcase --episode showreel-24s --query 'nf-inspector::shadow [data-inspector-track-id="final-title"]' --get outerHTML` showed the inspector fields.
2009+
- `target/debug/nf devtools --project v2-showcase --episode showreel-24s --query 'nf-inspector::shadow [data-field-path="params.title"]' --fill 'NEXTFRAME LIVE EDIT' --get value` filled the title through the input path.
2010+
- `target/debug/nf devtools --project v2-showcase --episode showreel-24s --query '[data-nf-preview-layers]' --get textContent` contained `NEXTFRAME LIVE EDIT`.
2011+
- `target/debug/nf devtools --project v2-showcase --episode showreel-24s --query 'nf-inspector::shadow [data-save-state]' --get textContent` returned `saved`.
2012+
- `target/debug/nf composition show --project v2-showcase --composition showreel-24s --track final-title --field params.x | jq '.selected, (.selected|type)'` returned `61` and `"number"`.
2013+
- `target/debug/nf capture --project v2-showcase --episode showreel-24s --out ../NextFrame.archive/v0.18.0-verify/editor-final-title.png` wrote a 3016x1936 PNG.
2014+
- `target/debug/nf export --project v2-showcase --composition showreel-24s --profile draft --out ../NextFrame.archive/v0.18.0-verify/showreel-edit.mp4` succeeded with `bytes=3804694`, `frames=720`, `duration_ms=24000`.
2015+
- Export sidecar check returned `NEXTFRAME LIVE EDIT`, `61`, and `number` for final-title.
2016+
- `ffprobe` reported duration `24.000000`.
2017+
- `find spec/versions/v0.18.0 spec/bdd/v2-editor-authoring -name '*.json' -print0 | xargs -0 -n1 jq . >/dev/null` passed.
2018+
- `npm run check` and `PATH=/Users/Zhuanz/workspace/NextFrame/frontend/nf-components/node_modules/.bin:$PATH npm run build` passed for `frontend/nf-components`.
2019+
- `cargo check -p nf-cli -p nf-shell -p nf-project` and `cargo build -p nf-cli -p nf-shell` passed.
2020+
- `./scripts/check-structure.sh` passed.
2021+
- `./scripts/audit.sh --gate-only` passed with report `spec/quality-reports/2026-04-25-0959.md` (G1=C existing lint/fmt debt, G2=A, P1/P2=N/A).
2022+
2023+
---
2024+
2025+
## 2026-04-25 09:57 · v0.18.0 · Milestone · [roadmap, editor]
2026+
2027+
**Session**: 41256e05
2028+
2029+
Milestones updated after v0.18.0.
2030+
2031+
Changed:
2032+
- Marked v0.18.0 online editing as done.
2033+
- Set v0.19.0 TTS and subtitle generation loop as current.
2034+
2035+
Why:
2036+
- PM-visible editing now has a verified loop: select final-title, type in inspector, preview updates, saved JSON keeps numeric types, and draft export reads the saved source.
2037+
- TTS/subtitle generation remains a separate end-to-end user flow and is the next independent milestone.
2038+
2039+
---

spec/milestones.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ <h1>NextFrame Milestones</h1>
2020
<div class="grid" id="grid"></div>
2121
</main>
2222
<script>
23-
const META={currentVersion:"v0.18.0",updatedAt:"2026-04-25"};
23+
const META={currentVersion:"v0.19.0",updatedAt:"2026-04-25"};
2424
const GROUPS=[
2525
{id:"done",title:"已达成",items:[
2626
{v:"v0.10.0",title:"JSON 到在线编辑到导出闭环",date:"2026-04-24"},
@@ -31,13 +31,13 @@ <h1>NextFrame Milestones</h1>
3131
{v:"v0.14.0",title:"V2 语音字幕预览编辑导出闭环",date:"2026-04-24"},
3232
{v:"v0.15.0",title:"完整 showreel 有声字幕导出稳定",date:"2026-04-24"},
3333
{v:"v0.16.0",title:"导出诊断和性能地图",date:"2026-04-25"},
34-
{v:"v0.17.0",title:"高上限 JSON 组件工程化",date:"2026-04-25"}
34+
{v:"v0.17.0",title:"高上限 JSON 组件工程化",date:"2026-04-25"},
35+
{v:"v0.18.0",title:"在线编辑真正可用",date:"2026-04-25"}
3536
]},
3637
{id:"current",title:"进行中",items:[
37-
{v:"v0.18.0",title:"在线编辑真正可用",date:"next"}
38+
{v:"v0.19.0",title:"TTS 和字幕生成闭环",date:"next"}
3839
]},
3940
{id:"planned",title:"未来规划",items:[
40-
{v:"v0.19.0",title:"TTS 和字幕生成闭环",date:"planned"},
4141
{v:"v0.20.0",title:"炫技样片生成器",date:"planned"}
4242
]}
4343
];
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# NextFrame 质量审计 · 2026-04-25 09:59
2+
3+
**版本**: `v0.13.0`
4+
**模式**: gate
5+
**总分**: 8.0 / 10
6+
**门禁**: 2 绿 / 0 红 / 2 N/A (4 硬门禁中)
7+
8+
## 维度打分
9+
10+
| # | 维度 | 硬度 || 说明 |
11+
|---|---|---|---|---|
12+
| G1 | 编译 + lint | 门禁 | **C** | rust 过 · 其他瑕疵(rust=1 clippy=1 fmt=0 ts=skip) |
13+
| G2 | 架构边界 | 门禁 | **A** | 0 违约 · 依赖方向单向 |
14+
| P1 | frame pure | 门禁 | **NA** | nf-engine 未实现(4 行) · v0.3+ 上 property test |
15+
| P2 | 3 模式像素 | 门禁 | **NA** | nf-runtime 未实现(5 行) · v0.3+ 上 diff harness |
16+
| G3 | AI 可操作 | 报告 | **** | (未跑) |
17+
| P3 | 视觉 token | 报告 | **** | (未跑) |
18+
| P4 | 零框架 | 报告 | **** | (未跑) |
19+
20+
## 关注项
21+
22+
- 🟡 **G1** C: rust 过 · 其他瑕疵(rust=1 clippy=1 fmt=0 ts=skip)
23+
-**P1** N/A: nf-engine 未实现(4 行) · v0.3+ 上 property test
24+
-**P2** N/A: nf-runtime 未实现(5 行) · v0.3+ 上 diff harness
25+
26+
---
27+
28+
_自动产出 · `./scripts/audit.sh` · 标准见 `spec/standards/`_

0 commit comments

Comments
 (0)