diff --git a/docs/reference/lint-rules.mdx b/docs/reference/lint-rules.mdx index 4a099df1a4..150a7e3c26 100644 --- a/docs/reference/lint-rules.mdx +++ b/docs/reference/lint-rules.mdx @@ -20,7 +20,8 @@ Severities: Source: `packages/lint/src/rules/structure.ts`. -The four codes marked **Warning or error** are warnings when the CLI lints a project, and errors when Studio lints the file it is editing. +The five codes marked **Warning or error** are warnings when the CLI lints a project, and errors when Studio +lints the file it is editing. ### `nested_structure_needs_subcomposition` @@ -52,6 +53,15 @@ The four codes marked **Warning or error** are warnings when the CLI lints a pro **Fix:** Extend the root `data-duration`, or make the clip end at or before it. +### `nested_clip_never_visible` + +**Warning or error.** A timed clip sits inside another timed element, and its time window never overlaps that +element's, so it never appears. A nested clip's `data-start` counts from the composition's start, not from the +element around it. Runs in sub-composition files too. + +**Fix:** Give the clip a `data-start` inside its parent's window, in composition time, or move it out of the +parent. + ## Composition contract Source: `packages/lint/src/rules/composition.ts`. diff --git a/packages/cli/src/audio/analysis/targets.test.ts b/packages/cli/src/audio/analysis/targets.test.ts index cefc9b442f..1e02812ed2 100644 --- a/packages/cli/src/audio/analysis/targets.test.ts +++ b/packages/cli/src/audio/analysis/targets.test.ts @@ -20,6 +20,7 @@ function row( host: null, hostRow: null, children: [], + parentRow: null, trackIndex: 0, src: "audio/a.wav", sourceFile: null, diff --git a/packages/cli/src/timeline/a2Shared.ts b/packages/cli/src/timeline/a2Shared.ts index 44ddd5e6d2..44bab9d666 100644 --- a/packages/cli/src/timeline/a2Shared.ts +++ b/packages/cli/src/timeline/a2Shared.ts @@ -137,6 +137,19 @@ export function diff(before: string, after: string): string { return output.length <= 8_000 ? output : `${output.slice(0, 7_997)}...`; } +function isAncestorOf(ancestor: TimelineRow, row: TimelineRow, timeline: ProjectTimeline): boolean { + for (let pointer = row.parentRow; pointer; ) { + const parent = rowAt(timeline, pointer); + if (parent === ancestor) return true; + pointer = parent.parentRow; + } + return false; +} + +function sameLineage(a: TimelineRow, b: TimelineRow, timeline: ProjectTimeline): boolean { + return isAncestorOf(a, b, timeline) || isAncestorOf(b, a, timeline); +} + function overlap( row: TimelineRow, timeline: ProjectTimeline, @@ -146,6 +159,7 @@ function overlap( return allRows(timeline).find( (candidate) => candidate !== row && + !sameLineage(candidate, row, timeline) && candidate.file === row.file && candidate.trackIndex === row.trackIndex && spansShareTime(start, end, candidate.start, candidate.end), @@ -429,6 +443,7 @@ export function mutationConflict( if (verb === "duplicate") { const conflict = allRows(timeline).find( (candidate) => + !sameLineage(candidate, row, timeline) && candidate.file === row.file && candidate.trackIndex === row.trackIndex && isInsideSpan(nextStart, candidate.start, candidate.end), diff --git a/packages/cli/src/timeline/describeProject.inPoint.test.ts b/packages/cli/src/timeline/describeProject.inPoint.test.ts index 1caf194d04..79b95c3cd1 100644 --- a/packages/cli/src/timeline/describeProject.inPoint.test.ts +++ b/packages/cli/src/timeline/describeProject.inPoint.test.ts @@ -18,12 +18,12 @@ const SCENE = `