AI-Assisted Editing
Once a video document exists, you refine it three ways: chat (edit by conversation), the inspector (precise per-scene controls), and the timeline. All three mutate the same canonical ResolvedIR in the Zustand store, with full undo/redo.
Components: web/src/components/ChatPanel.tsx, web/src/components/inspector/*, web/src/components/TimelineV2.tsx
Store: web/src/document/irStore.ts
API: POST /api/director/edit
Editing by chat
The ChatPanel lets you describe a change in plain language. It posts the current document (and the selected scene, if any) plus your instruction to /api/director/edit, which returns an updated IR and a list of the concrete changes it made.
sequenceDiagram
User->>ChatPanel: "Shorten to 15s and add a CTA scene"
ChatPanel->>API: POST /api/director/edit { doc, sceneId?, instruction }
API->>LLM: apply edit, validate props vs schemas
API-->>ChatPanel: { ir, changes[] }
ChatPanel->>IRStore: setIr(ir) (pushes onto undo stack)
IRStore->>Player: re-render preview
- Quick-action chips for common edits: Make it punchier · Shorten to 15s · Add a CTA scene · Try 9:16 · More energetic · Tighten the copy
- Per-document history — the chat thread is persisted per document (session id)
- Undo / redo — every edit is reversible from the store's
past/futurestacks - Media upload — attach images/audio/video into the library (📎) for use in scenes
The inspector
Precise, structured controls. Two scopes:
Scene inspector — inspector/SceneInspector.tsx
Editing for the selected scene:
- Template swap + duration (in seconds)
- Content props via a recursive
PropEditor(numbers, strings, lists, nested objects) - A live Zod validity badge —
requireModule(templateId).schema.safeParse(props)shows whether the props are valid for the template - Voiceover — script text, audio source, base volume, and a volume envelope (
EnvelopeEditor: fade in/out + keyframes) - Media inputs — image/audio/video pickers (
MediaInput, backed by/api/assets?kind=…) - Background — per-scene background layer (
BackgroundEditor: solid / gradient / animated / image / video)
The most-edited controls are surfaced first (recent UX pass: reorganize Scene inspector most-edited-first; group scene actions).
Document inspector — inspector/DocInspector.tsx
Document-level settings:
- Theme tokens — accent, background, fonts (brand overrides)
- Document background — solid / gradient / animated / image / video
- Music — background track + volume envelope (optional)
- Logo overlay — placement, size, opacity
- Voice — narration voice for the whole document
The timeline — TimelineV2.tsx
A pro-editor-style timeline in one scroll container with sticky-left labels:
- Collapsible Scenes (parent rows) → Elements (children: template · background · voiceover)
- Time-aligned bars show duration and transition overlaps; a ruler row enables scrubbing
- Click a clip or row to select that scene and jump the playhead
- Volume envelopes are visualized per scene (audio automation)
- Default zoom fits the whole document to the viewport
- Times are shown in seconds (recent UX pass), not raw frames
The canonical store — irStore.ts
A single Zustand store holds one ResolvedIR per session, the selectedSceneId, and past/future stacks for undo/redo. It is persisted to localStorage, so an in-progress edit survives a refresh. Actions include:
loadIr · setIr · setSceneProp · addScene · removeScene · duplicateScene
setSceneTemplate · setSceneVoiceover · setSceneBackground · setDurations
setThemeToken · setMusic · setLogo · setVoice · setTitle
Because chat, inspector, and timeline all funnel through these actions, the live preview, the timeline, and any subsequent render always reflect exactly the same document state.
Saving & loading
Documents are saved to the server via /api/director/docs (see Library & Media). The editor can list saved documents, reopen them, and the Director's HANDOFF step writes finished documents here automatically.