0038 — Reel text layout (positioning + multiple text blocks per scene)¶
Status: DRAFT (decisions resolved) — from Matt's 2026-07-23 studio driving
notes (0029 §9). D1 resolved with Matt 2026-07-23: free drag with normalised
frame-fraction coords (snap guides at thirds/centre; d-pad + numeric nudge as
the keyboard fallback). Schema resolved: texts[] supersedes text when
present; absent → today's single text + mode-default placement (parity guard).
Parked 2026-07-23 (Matt): a valuable big feature — deliberately pinned while
further studio bug/niggle rounds land first. Pick up when the current review
cycle settles.
Heaviest of the three 2026-07-23 specs: changes the storyboard schema and the renderer,
not just the studio.
Date: 2026-07-23
Relates: 0001 §4 (reel pipeline; parity facts), 0003 (video panels — the earlier
multi-panel design), 0020 (reel make), 0021 (in-memory render), 0002 R-WS-*
(storyboard schema requirements).
1. Problem¶
A card has exactly one text block, always laid out by the renderer's fixed convention (centred block / lower-third overlay caption). Matt wants: 1. Positioning control over a scene's text — drag-and-drop placement in the studio preview (preferred) or a d-pad nudge fallback. 2. Multiple text areas on one scene (e.g. a headline + a footnote, independently placed/styled).
2. Why this is a core-model spec, not a studio tweak¶
- The storyboard schema (
internal/reel/storyboard.go, mirrored by the Python parity scripts and R-WS-*) has no position field and a singletext. - The renderer (card compositor in
internal/gen/reel) computes layout (wrap width, centring, scrim-relative caption box) from the mode alone. - The studio preview (CardPreview) and the ffmpeg render must agree on the layout model, or the preview lies.
3. Direction to review¶
- Schema (additive, back-compat):
card.textstays (block #0); optionalcard.texts: [{text, pos: {x,y} (frame-fraction anchors), align, size?, role?}]supersedes it when present. Absentpos= today's mode-default placement, so every existing storyboard renders identically (parity guard). - Renderer: honour
pos/alignper block; wrap width derives from the anchor; accent-word markup unchanged per block. - Studio: CardPreview becomes the layout surface — drag a text block within the
9:16 frame (snap guides at thirds/centre); d-pad + numeric nudge for keyboard/a11y.
Scenes inspector gains an "+ add text" list bound to
texts. - Timing model untouched — VO still drives duration; text blocks are visual only.
4. Open questions (for review)¶
- D1: normalised frame-fraction coords (rec) vs 9-cell grid presets? (Drag wants continuous; grid is a snapping layer, not the model.)
- D2: per-block styling scope v1 — size + palette-role colour only (rec), or fonts too?
- D3: does the cover card join the model, or stay bespoke?
- D4: schema parity — this deliberately diverges from the Python scripts (they have no layout model). Confirm the parity contract is "old boards render identically", not "schema frozen".
5. Non-goals¶
- Animated/kinetic text; per-block timing; rich text beyond the
*accent*markup.