fix(studio): warn on anonymous timeline clips#533
Conversation
vanceingalls
left a comment
There was a problem hiding this comment.
Staff review: requesting changes.
The display-label fix is the right direction, but this still conflates user-facing labels with internal timeline identity. In packages/studio/src/player/hooks/useTimelinePlayer.ts, anonymous clips now flow through id = clip.id || label and id = el.id || compId || label. That means common timelines with two anonymous clips sharing the same class/label, for example two .card clips, can produce duplicate TimelineElement.id values.
That is risky because other Studio paths still treat id as identity: mergeTimelineElementsPreservingDowngrades builds a Set from element.id, selection/update paths fall back to key ?? id, and several edit/error paths still report or compare element.id. The new label field solves the user-facing name problem, so the internal identity should stay unique or all identity-sensitive paths need to be key-first throughout.
Please add coverage with two anonymous clips that generate the same friendly label and verify they remain distinct through runtime-manifest sync/fallback parsing and are not merged, dropped, or updated together.
788ed66 to
e4fd0d7
Compare
|
@vanceingalls all was addressed btw. |
e4fd0d7 to
90e9027
Compare
vanceingalls
left a comment
There was a problem hiding this comment.
P1: Root timeline-visible compositions can still trigger studio_missing_editable_id.
The new root skip compares tag.index with rootTag.index, but those indexes come from different strings. tags are extracted from the full source, while findRootTag() extracts from bodyContent, so a normal <html><body>... document does not match. Result: the composition root can get studio_missing_editable_id if it has data-start, even though the rule tries to skip it. The new test around core.test.ts:111 should catch this. Compare by tag.raw, return source-relative indexes from findRootTag, or identify the root from the same tags array.
P2: Cross-window media metadata is still skipped.
resolveMediaElement() now uses the iframe owner window constructors, but applyMediaMetadataFromElement() still checks mediaEl instanceof HTMLMediaElement against the parent window. For preview iframe media this remains false, so sourceDuration and playbackRate are not populated. That weakens the trim bounds that depend on those fields in Timeline.tsx:749. Use the media element owner-window constructor for this check too.
|
@vanceingalls addressed both review follow-ups in
Verified:
Re-requesting review now. |
Merge activity
|
Problem
Studio timeline editing still had two rough edges that made the latest alpha feel less polished when testing it like a video editor would:
__node__index_*, which made the timeline look broken instead of authored.idcould still appear in the timeline and canvas editor, but authors did not get direct lint guidance that those elements are weaker targets for Studio and agent edits.What this fixes
studio_missing_editable_idlint warning for timeline-visible elements that do not have anid.hero-titleorscene-1-card.Cardbut still stay separate timeline entries.Root cause
The runtime manifest used synthetic node-index ids as both identity and display fallback for timeline nodes that had no stable author-provided id. Studio then treated those internal values as user-facing clip names.
The first pass improved the display label, but it also risked using that friendly label as internal identity. Two anonymous clips with the same label could then collapse into the same logical timeline element. The fix separates display labels from internal identity and prefers the timeline key whenever Studio needs to match an element.
The linter also had correctness checks for render and runtime behavior, but it did not teach authors when a timeline-visible element would be harder for Studio and agents to patch reliably. That left missing ids as a silent authoring quality issue instead of actionable guidance.
Verification
Local checks
bun run --cwd packages/core test -- src/lint/rules/core.test.ts src/runtime/timeline.test.ts-> 41 tests passbun run --cwd packages/studio test -- src/player/hooks/useTimelinePlayer.test.ts src/player/components/timelineTheme.test.ts-> 23 tests passbun run --cwd packages/core typecheckbun run --cwd packages/studio typecheckbun run --cwd packages/studio build-> passes with the existing Vite chunk-size warningbunx oxlint $(git diff --name-only origin/main...HEAD)-> 0 warnings, 0 errorsbunx oxfmt --check $(git diff --name-only origin/main...HEAD)git diff --check origin/main...HEADBrowser verification
/tmp/hf-pr533-conflict-verifywith two timed anonymous.cardclips that both label asCard.pr-533-conflict-verify.agent-browserto verify the timeline renders two separateCardclips instead of collapsing duplicate anonymous labels.agent-browserto open the Studio lint modal and verify it shows human-readable missing-id warnings, not internal node-index labels.agent-browserto click Play after the lint pass and confirm the timeline remains usable.agent-browser.Notes
main; conflict resolution preserved both the newer mainline Studio shortcut/lint behavior and this PR's anonymous-clip identity split..codex-artifacts/pr-533-conflict-rebase-2026-04-29/.