# Propose timeline edits

Create reviewable clip edits that use the Editor’s existing undo history.

### Interactive timeline mixer

[Download Timeline mixer](/block-sdk/examples/timeline-mixer.stillmade.json), or
choose **Timeline mixer template** in the Block builder. Its complete interface
lists video and audio clips with individual level sliders, live percentages,
reset, empty-state guidance, and a Review mix action. It uses `StillMade.render`
and `StillMade.onAction`; the isolated production function returns a typed edit
proposal. Existing Editor permissions, conflict checks, review, and undo still
apply. No account APIs or provider keys are exposed to its interface.

Import the package, run its fixture, and change a slider in Preview. To use real
clips, add it to a Project Type after Editor and open its Step in a project with
a timeline. Review the proposed levels before applying them. It edits up to 100
clips per invocation; the interface does not play or render the timeline.
Download its source and SDK from the builder to modify the HTML, CSS, interface
JavaScript, production function, and fixtures independently, then reimport.


A Block can return a `timeline-edit` output to propose changes to existing Editor
clips. Declare `permissions.project: ["context.timeline.read", "timeline.propose"]`
and an input `{type:"timeline",context:"timeline",primary:true}`. The output is
`{schemaVersion:1,title,timelineId,commands}`. Copy `timeline.activeTimelineId` into
`timelineId`. Each command is `{collection,operation,before,values}`: collection is
`clips` or `audioItems`, and `before` is the exact unchanged target object from the
input snapshot. Use at most 100 commands, with one command per target.

| Operation | Values | Behavior |
| --- | --- | --- |
| move | `{start: seconds}` | Move an existing item; no silent ripple |
| trim | `{trimStart: sourceSeconds, duration: timelineSeconds}` | Trim within known source duration; variable-speed, frozen and looping clips defer to Editor |
| split | `{at: timelineSeconds, newId: "unique-clip-id"}` | Split into two source-aligned clips, at least 0.05 seconds from either edge |
| volume | `{volume: 0..200}` | Set the existing Editor volume percentage |
| mute | `{muted: boolean}` | Mute or unmute an existing item |
| remove | `{}` | Remove the timeline instance, preserving source media |
| grade | `{lut, color}` | Grade clips only; retain original media and timing |

Run the Block in a project, accept its result, and open the Editor's media panel.
Timeline edits from Blocks displays a visual before/after strip and each target's values and applies
all commands together using ordinary Editor undo history. Desktop and mobile use
the same proposal contract. A changed target, different timeline version, locked
track, source overrun, or new clip overlap rejects the proposal. The Block cannot
set arbitrary fields, insert external URLs, run code inside playback, or silently
write the timeline. Re-run it against current context after a conflict.

[Download Quieter clips](/block-sdk/examples/quieter-clips.stillmade.json) for a
complete isolated JavaScript example with a realistic timeline fixture. The
proposal type is a sink for Editor review; it is not interchangeable with a
`timeline` snapshot.


### Caption proposals

Use `collection:"captions"` with these commands:

| Operation | Before | Values |
| --- | --- | --- |
| caption.add | null | `{id,trackId,text,start,end}` |
| caption.update | Exact existing caption object | `{text,start,end}` |
| remove | Exact existing caption object | `{}` |

Caption times are seconds with end greater than start; text must be nonempty and
at most 5,000 characters. Adding uses an unused item ID and an unlocked existing
caption track. The standard `captions` track is created if absent. New captions
use StillMade's existing caption defaults; updating text or timing preserves the
caption's styling. Reapplying an insertion rejects its duplicate ID. The host
compares existing captions against their captured snapshots before update/removal.

[Download Caption from text](/block-sdk/examples/caption-from-text.stillmade.json).
This complete Block takes text plus a scoped timeline, chooses an unused caption
ID, and proposes insertion without modifying the project. Its output connects to
the Editor's proposal review. Add or run it after opening a timeline, then review
its result in the Editor's media panel.

## Timed captions and caption styling
[Download Captions 2.0.0](/block-sdk/examples/captions.stillmade-block). This
standalone, remixable Block converts a transcript into editable timeline captions.
It owns phrase grouping, word timing and caption appearance, runs in the JavaScript
sandbox, and returns ordinary `timeline-edit` proposals for review and Apply.
It does not transcribe audio or spend execution credits.

The optional context binding `{ "type": "transcript", "context": "transcript",
"required": false }` requires `context.transcript.read`. At whole-project scope
it supplies a copied `{schemaVersion:1, words:[{text,start,end}], text, audioUrl?}`
from the saved voiceover when word timings exist. It is omitted when absent and
for scene, shot or asset selections. URL metadata grants no media access.
Explicitly connected transcript inputs remain separate from this fallback.

Captions uses a connected transcript first, then entered text, then the project's
saved transcript. Text without word timings uses estimated timing, identified in
the proposal title. Start offsets word times into the timeline. Duration zero
keeps transcript timing; a positive duration clips the result to that interval.
A proposal supports at most 100 lines and rejects longer results without silently
truncating them. Existing captions stay intact and locked tracks reject Apply.

`caption.add` retains its required `{id,trackId,text,start,end}` values and also
accepts these optional fields. Unknown fields and values outside these limits
are rejected by the same SDK validator and host apply path:

| Field | Accepted values |
| --- | --- |
| `words` | Up to 1,000 `{text,start,end}` entries, ordered by start, contained in the caption interval; nonempty text up to 5,000 characters, end at or after start |
| `font` | `Hanken Grotesk` |
| `size` | 8–200 |
| `color`, `highlightColor` | Three- or six-digit hex color |
| `background` | `none` or three- or six-digit hex color |
| `backgroundOpacity`, `backgroundPadding`, `backgroundRadius` | 0–100 |
| `position` | `top`, `middle`, or `bottom` followed by `left`, `center`, or `right`, separated by one space |
| `animation` | `none` or `fade` |

The host fills omitted appearance values with its existing defaults, preserves
provided styling and clones word timings before saving through ordinary edit
history. Caption updates retain their existing `{text,start,end}` contract.
Package source, MIT notices, lineage and three fixtures are retained in the
canonical archive; validate, test and remix it using the public CLI.
