Skip to content

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/future stacks
  • 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 badgerequireModule(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.