Accept codegen requests queued while no machine was connected: the poll
loop applies them like any other edit and logs "queued by <name>"
attribution. Update the editor docs to describe pending edits and code
sync.
A slug used at two distinct call sites silently merged both actions into
one runtime identity (the second became a phantom loop-repeat, its edits
unreachable) and was only caught reactively as an ambiguous-edit-id.
Static analysis now finds every duplicate up front, keeps the first
occurrence, and re-stamps the rest with fresh, never-reused slugs. Wired
into record (before the record run), the screenci edit startup handshake,
and the interactive codegen apply (retry-after-resolve, still-ambiguous
still throws). Genuine loops (one call site) stay untouched.
- codemod: collectEditIdOccurrences, renameEditIdAtCall
- editIdStamp: planDuplicateEditIdFixes (skips in-use slugs)
- record path reuses one --list discovery pass for both title and
duplicate-editId checks
blockRemoveEdit now unwraps autoZoom blocks (not only hide/speed/time), and
codeSync orders the unwrap before the two re-wraps in one pass, so splitting a
code-authored autoZoom yields two sibling brackets carrying the original zoom
options. Adds codeSync.spec coverage and documents the flow in editor.md.
Overlays no longer vary by language. video.overlays(...) accepts only a
names-only array or a shared content-major object; the language-major
spelling is rejected at the type level and throws a clear ScreenciError at
declare time. Narration (and the hidden values/audio) keep the per-language
form.
Removes the runtime per-language overlay resolution (buildOverlays is now
shared-only), the overlay language contribution/warning paths, the
AssetTranslation type and asset translations fields, and the assetStart branch
of the translation fold. Docs updated.
Make recorded waitForTimeout calls web-editable and keep the source in sync
with timeline drags:
- Unlock recorded numeric waitForTimeout delays (instrument) so their
durationMs can be rewritten in code instead of being locked.
- Add a durationMs codegen path (codeSync): locate the wait via a stamped
neighbor action (findRecordedWait) and rewrite the numeric arg in place, or
remove the call at 0. Refuse safely when the source has drifted.
- Coalesce back-to-back waitForTimeout statements into one summed call
(mergeAdjacentWaitEdits), run as a post-pass on every file a sync changed.
Adjacency is judged per source block, so it never crosses control flow;
comment-separated sleeps are preserved.
- Carry schemaKind on editable snapshot entries so codegen can tell a recorded
delay apart from other unstamped entries.
Docs and unit tests included.
BREAKING: the name-first overloads of hide/speed/time are removed. A block's
stable identity now lives in the trailing options object, like an action's:
hide(fn, { editId: 'setup' }). The dev-startup handshake that stamps action
editIds now stamps blocks too (the options object lands right after the
callback argument), so every block gains a stable identity when an edit
session starts. blockRemoveEdit resolves blocks by the editId option (legacy
string-name args still match), making every stamped block web-removable.
A gap span could only anchor forward (a sleep after an interaction's end or
a delay into it), so a cut over the footage leading into the first
interaction was unrepresentable. fromLeadMs pulls the span start to before
its anchor (codegen emits it as a leading waitForTimeout inside the wrap);
untilLeadMs is its end-boundary twin for editor-only bare splits. Bare-split
detection treats matching leads like matching sleeps.
Editor edits no longer fetch backend-stored overrides at record time.
Instead every edit is codegenned into the user's .screenci.ts sources
over the dev channel (or fails when no machine is connected).
- Remove the CLI override fetchers and their env plumbing
(SCREENCI_VALUES_OVERRIDES / _RECORD_OPTIONS / _WEB_LANGUAGES), the
parsers/merge in runtimeMode, resolveEffectiveRecordOptions, and the
SCREENCI_DEBUG_OVERRIDES debug dump.
- Reconcile the languages model: drop the 'studio' sentinel, studioSeed,
studioOwned, pending, and web-language unioning. A video with no
.languages() is the implicit 'en' default; an explicit .languages([...])
set is unioned with per-feature keys and filtered by --languages.
- Add codegen edit kinds and codemods: languagesEdit (setVideoLanguages),
valuesEdit (setValuesValue), and editorMediaEdit (setEditorMedia).
- Add the { editor: '<name>' } media declaration for backend-hosted,
editor-uploaded overlays / narration audio / audio tracks: the bytes
stay in the backend, but the item is explicit in code (no local path).
- Rename internal resolveStudio* helpers to resolve*Base; keep wire/DB
keys that deployed CLIs and stored recordings depend on.
- Update docs (languages, editor, overlays, narration, video-script-basics).
Editor codegen edits and dev startup editId stamping now format each
changed file with the user project's own prettier install before
writing. Formatting is gated on prettier resolving and a Prettier
config file being found for the edited file, so it is opt-in by
presence and never fails the edit: errors pass the content through
unchanged with a warning.
screenci init enables it by scaffolding a minimal user-editable
.prettierrc (2-space indent, single quotes, no semicolons, matching
the generated example style) and installing prettier in the island,
plus a format script and README note. Docs updated (editor.md,
manual-setup.mdx).
Remove the stale screenci status and sync sections from the CLI reference
(both commands are gone on this branch, codegen writes edits into sources
directly) and reword the timeline editing intros that still claimed edits
apply without code changes.
Rewrite editor.md around the sync-gated codegen model (connected machine,
live preview, timeline, Export-first), fix the record vs export flow in
overview, agent-integration, manual-setup, cli, ci-setup, version-history,
public-urls-and-embeds, update-screenci, and video-script-basics, fix the
broken captureAudio examples (boolean | { gain }), document accent and the
web narration/overlay editing, add enableCaptureAudio to configuration, and
rename the CLI's Studio strings to Editor.
- planCodeSync unappliable items now carry a typed UnappliableReason
plus the human message; applyCodegenRequest errors name the reason so
the editor toast is actionable.
- Block/point/effect calls resolve through the file's import table, so
aliased imports (autoZoom as az) are found, wrapped, unwrapped, and
reused on insert; unresolvable functions refuse with
unresolved-import.
- plannedRemoval unwraps stale editor-owned zoom/hide/speed/time wraps
(anonymous blocks over the anchors) instead of leaving them in code.
- timelineEdits gains paramEditIdFor/renameEditIdFor and parseEditId,
exported via the new screenci/timeline-edits subpath so the web app
compiles against the same wire types and id encoding.
- Tests: refusal reasons, alias resolution, stale wrap unwrapping,
TS-absent guard, plain-JS parsing, locator alias chains, editsOverlap.
- Docs: refusal reasons and alias support in cli.mdx and editor.md.
Adapts the scissors additions to the codegen architecture: blockRemoveEdit
is routed as a codify record through splitTimelineEditsByVideo and applied
by the dev codegen channel; bare-split hides stay editor-only (skipped by
the planner); the sync-era parse/apply/report subsystem removed on the
editor branch stays removed.
- optionsEdit and narrationEdit records (timeline edits v4): render/record
option snapshots merge into .renderOptions/.recordOptions builder calls
(section added when missing), narration cue values merge into the
video.narration declaration via the new setNarrationValue codemod
(content-major and language-major forms, conversion to language-major on
a non-default language edit, names-only declarations stay app-managed)
- applyCodegenRequest wires the new records into planCodeSync
- screenci dev watches the managed videos' test sources and
screenci.config.ts (src/devWatch.ts): a real content change re-records
the affected videos as previews through a new machine-local record job
in the listen loop; codegen self-writes are baseline-suppressed;
--no-watch disables it
- docs: cli.mdx dev watching, editor.md option/narration codegen coverage
- Record a hiddenAction marker event for every instrumented action inside
hide(), so the web editor can show what a hide suppresses.
- Skip zero-width hide gapSpanEdits at sync: bare split markers stay
editor-only state and are never codified as empty hide() blocks.
- New blockRemoveEdit record + unwrapBlockCall codemod: unwrap a NAMED
hide/speed/time block from source, keeping the wrapped calls and their
waitForTimeout pacing.
Editor edits now arrive over the dev channel as codegen requests and are
written straight into the .screenci.ts sources; recordings always run purely
from code values.
- Keep data.json across uploads (media-only cleanup, last-data.json rename)
and stamp a sourceHash of the test file into its metadata.
- screenci dev startup handshake: skip recording when the source hash matches
and every editable action has an editId; otherwise stamp missing editIds
and re-record as a preview. New --grep, --force-record and
--record-kill-window flags.
- Dev loop: killable background record slot, serial codegen applies acked
over /cli/dev/report-codegen, fast-poll during active editing, kill-or-queue
policy for superseding triggers, sync-state timeline locking.
- Removed: editable-actions.json and action-params.json snapshots,
SCREENCI_TIMELINE_EDITS injection, runtime override application,
screenci sync / status / reset-web-edits and dev --sync.
Comment out (not delete) the screenshot(), narration positioning
(moveNarration/resizeNarration), setBackground, background audio, and
selected() dependency exports; remove values()/audio() from the public
builder type while keeping them attached at runtime. Move the values,
audio, screenshots, and dependencies guides out of the docs manifest
(sources now live in docs/removed/ at the mono repo root), trim the
overlay-updates guide to the still-exported APIs, and scrub mentions
from the remaining docs and the agent skill.
Editor edits now arrive over the dev channel as codegen requests and are
written straight into the .screenci.ts sources; recordings always run purely
from code values.
- Keep data.json across uploads (media-only cleanup, last-data.json rename)
and stamp a sourceHash of the test file into its metadata.
- screenci dev startup handshake: skip recording when the source hash matches
and every editable action has an editId; otherwise stamp missing editIds
and re-record as a preview. New --grep, --force-record and
--record-kill-window flags.
- Dev loop: records run in a killable background slot while the loop keeps
polling; codegen requests apply serially via the codemod pipeline and are
acked over /cli/dev/report-codegen; fast-poll during active editing;
kill-or-queue policy for superseding record triggers; sync-state reporting
locks editor timelines during the startup handshake.
- Remove the server-override machinery: editable-actions.json and
action-params.json snapshots, SCREENCI_TIMELINE_EDITS injection, runtime
override application, screenci sync / status / reset-web-edits and
dev --sync.
- cursorCurve: controlPointsToTuple (inverse mapping) and parseCursorCurve
- instrument: stamp effective moveCurve/moveCurviness into editable defaults
at every pointer wrapper, and apply saved curve overrides at record time
- codeSync: codify cursor-move param edits (move.duration/speed/easing/
curve/curviness/delayAfter, duration, easing, dragSteps) onto the stamped
call, with duration-vs-speed exclusivity and an overlap guard
- easing: browser-safe EASING_NAMES re-export for the web editor
- docs: visual curve editing and cursor-move codesync
Drop timestamp()/waitSince()/place* and the sync-prompt agent path from
the guides; describe effects as call-position block wrappers (autoZoom/
hide/speed/time) plus waitForTimeout gaps, keyed to editId. Move the last
config-level use.recordOptions/renderOptions examples to the per-video
video.recordOptions()/renderOptions() builder form.
Option edits resolve their call sites via the editId recorded on
action-param records; sleepBefore, autoZoom offsets, and placed events
anchor by slug too. The selector/occurrence lexical matcher, line
anchors, and loop detection are gone: unstamped actions are never
guessed at and route to the agent prompt until a record plus sync
stamps them.
New renameEdit timeline record { target: { editId }, newEditId }.
screenci sync and dev --sync apply it by replacing the slug string
literal (renames run last so earlier edits key on the old slug);
invalid or unlocatable renames fall through to the agent prompt.
Slugs validate as [A-Za-z0-9_-]+.
Actions and autoZoom blocks accept an optional editId slug (click1,
zoom2). screenci sync and dev --sync stamp missing slugs automatically
using the call sites captured at record time, allocating from the
committed .screenci/edit-ids.json (numbers never reused, ids never
removed). With an editId the stable key IS the slug, so editor edits
and placed-event anchors survive refactors, moved lines, and locator
changes; sync locates call sites by exact slug instead of heuristics.
Loop executions key as slug#N and stay web-runtime-only.
Also: record --no-render uploads the recording and editable data
without dispatching a render, for a fast first editor sync.
New command applies action-option changes, sleepBefore waits, autoZoom
offsets, and placeHide/Speed/Time/Zoom calls directly to .screenci.ts
files using the TypeScript parser with raw text splices (formatting and
comments preserved). Anything ambiguous (loops, spreads, stale edits,
narration content) falls back to the existing agent sync prompt.
Dry-run diff by default; --write saves, --write --reset clears web
edits for fully applied videos.
Add a /cli/studio-sync fetch and a pure studioSync builder so screenci
sync-prompt emits SET directives for the render and record options the web
editor holds, plus NOTE lines for narration/text/audio/overlay edits, letting
an agent codify render options into the scripts too.
Add three point kinds (background, narrationBox, recording) to the
timeline-edits wire format so the web editor can place presentation
updates without a legacy authored path. applyPlacedEvents inserts the
matching backgroundUpdate/narrationUpdate/recordingUpdate events, and
screenci status/sync-prompt now apply the -g grep filter to the
timeline-edits and placed-events blocks too.
- hide('name', fn) and time('name', ms, fn) overloads with editable metadata
(hide read-only span; time durationMs web-editable with shadow warnings)
- resetZoom() editable (easing/duration); page.goto emits navigation marker
events (hard borders, app time, never editable)
- redact() emits a marker event with web-editable mask styling (color,
radius, css) applied at the next record
- Presentation updates editable: moveNarration/resizeNarration,
resizeRecording/hideRecording/showRecording, setBackground (corner, size,
css, transition duration)
- Editable descriptors capture the user-code call site (file:line) so
sync-prompt can emit exact placement instructions; snapshot carries it
- Authored events: span kind 'time' (render-time remap) and point kinds
narrationUpdate/recordingUpdate/backgroundUpdate
- sync-prompt v2: CHANGE/INSERT/WRAP placement instructions with call sites
and a ripple-scope note; screenci reset-web-edits clears the web layer
- Docs: what is editable from the web, resetting web edits
- sleepBefore field on every pointer action: the SDK sleeps that long after
the previous event before the cursor starts moving, recorded as a leading
wait so the editor shows it; accepted as an override even for recordings
made before the field existed in defaults
- SCREENCI_DEBUG_OVERRIDES=1: the CLI dumps every override set fetched from
the backend (per channel, with explicit '(none)'), enables fetch warnings,
and the SDK logs each override value and authored event as it is applied
(debug flag lives in dependency-free debugFlags.ts to avoid import cycles)
- Docs: debugging overrides section, sleepBefore note
- Per-field lockedFields: explicit code values no longer block web edits;
overrides apply with a shadow warning at record time and in the editor
- Stamp zoomTo (manual zoom) with editable metadata so zooms show on the
editor's camera row
- New editable-actions.json snapshot: pre-record shadow warnings and
screenci status classification (shadows code / changes default / stale)
- Web-authored events: hides and speed blocks anchored to a known event
(stable key or timestamp name) plus offset, with duration or end anchor;
applied into data.json at write time, skipped with a warning on broken
anchors; reported by screenci status
- Web languages channel: fetch /cli/web-languages and union web-added
languages into each video's recorded set (SCREENCI_WEB_LANGUAGES),
separate from the restricting --languages filter
- Remove recordOptions.implicitEditable (everything is always editable)
- Docs updated (editor, configuration, languages)
Overrides can target defaulted values too; only an override that changes
the used value is logged and recorded (as `used` in actionParams, so the
editor can update its options from what the recording ran with).
ACTION_PARAM_DEFAULTS is exported so the backend can detect overrides
that merely restate a default. The CLI now fetches overrides from
GET /cli/action-overrides by default (404 = none), and gains
`screenci status` (compare editor edits with the latest run) and
`screenci sync-prompt -g <regex>` (agent-ready change/remove
instructions to bring code back in sync).
Breaking change: the editable() marker is gone. Arrays declare blank
editor-owned names, plain objects are code values the web app may
override, and every video.languages(...) set is web-owned (union of web
selection, code seed, and per-feature keys). Render/record options are
always studio-flagged.
New: every instrumented action records which option values were explicit
at the call site vs defaults. The provenance rides in data.json
(actionParams), is snapshotted to .screenci/action-params.json (never
wiped), and editor overrides (SCREENCI_ACTION_OVERRIDES, fetched behind
the stubbed ActionOverridesClient) are applied during recording with an
info line per application plus a record-start warning when an override
shadows an explicit code value.
Replace video.use({ renderOptions, recordOptions }) with dedicated
chainable builder methods video.renderOptions(...) / video.recordOptions(...)
(and the screenshot counterparts). They accept a flat object (shared across
languages), a language-major object ({ default, de, ... }) for per-language
overrides, or editable() to hand the bag to the web app, mirroring
video.narration(...).
- Add src/optionsDeclare.ts: normalization (flat / language-major / editable)
reusing declare.ts disambiguation, plus deep-merge helpers (flat
mergeRecordOptions, group-wise mergeRenderOptions) and layer combiners.
- builder.ts: pre-merge per-pass options (shared base, language override,
each-variant patch) onto internal fixtures; throw on per-language options
with mode: 'shared'; warn on unused language keys.
- video.ts / screenshot.ts: replace the public recordOptions/renderOptions
fixtures with internal ones and combine the config-level default layer with
the per-video builder patch at each consumption site. Fixes an existing bug
where an each-variant recordOptions clobbered config-level options instead
of merging.
- config.ts: defineConfig remaps use.recordOptions/renderOptions onto the
internal fixtures, keeping config as the project-wide default layer.
- Migrate docs, doc-source scripts, SKILL, and voices/localize JSDoc.
Tests: add optionsDeclare and builder coverage (merging, editable, shared-mode
throw, each-variant patch); update config remap assertions. Full unit suite
(1312) and e2e (104) pass.