Skip to content

feat(studio): timeline leaf helpers — audio inspector, zoom math, UI prefs#2192

Open
ukimsanov wants to merge 1 commit into
mainfrom
studio-dnd/pr01-timeline-leaf-helpers
Open

feat(studio): timeline leaf helpers — audio inspector, zoom math, UI prefs#2192
ukimsanov wants to merge 1 commit into
mainfrom
studio-dnd/pr01-timeline-leaf-helpers

Conversation

@ukimsanov

@ukimsanov ukimsanov commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

What

extends three leaf modules to their final NLE-stack form, tests in the same change: timelineInspector (isAudioTimelineElement, resolveBeatSourceTrack), timelineZoom (zoom/pps math incl. computePinnedZoomPercent), and studioUiPreferences (persisted editor prefs).

Why

leaf dependencies of the NLE timeline stack; landing them first keeps the later glue PRs to wiring.

How

additive from the consumer side — every export main already uses is unchanged (typecheck against main's consumers passes untouched); every new export is exercised by a test in this PR.

Test plan

bunx vitest run on the three test files; tsc --noEmit in packages/studio; fallow audit --base origin/main clean.


Stack position 1/25 — studio NLE overhaul (CapCut-parity timeline + canvas). Graphite manages bases; merge from #2192 upward. Temporary TEMP(studio-dnd) fallow entries (unwired-component windows) are all removed by #2213 (app-shell swap), whose tree is byte-identical to the fully-verified integration branch.

ukimsanov commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

This was referenced Jul 11, 2026
@ukimsanov ukimsanov force-pushed the studio-dnd/pr01-timeline-leaf-helpers branch 2 times, most recently from 68911ca to 1f6c9fe Compare July 11, 2026 01:15

@miga-heygen miga-heygen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed as part of the 25-PR NLE overhaul stack. Full stack review on #2205 (the keystone). No blockers on this PR. — Miga

@vanceingalls vanceingalls left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 LGTM — leaf helpers land clean, persistence key stable, math checks out.

Focus items — verified.

Persistence key (studioUiPreferences.ts). The STUDIO_UI_PREFERENCES_KEY = "hf-studio-ui-preferences" constant is byte-identical to main, and every new field (timelineSnapEnabled, timeDisplayMode, timelineZoomMode, timelineManualZoomPercent) is strictly additive to StudioUiPreferences — existing users' saved prefs still parse under the same key, and each new field is guarded by its own runtime type-check in readStorage so garbage / missing fields fall through to undefined. No migration risk. snapEnabled (canvas) and timelineSnapEnabled (timeline magnet) coexist as distinct fields — that's a naming close-call but it's an addition, not a rename, so no upgrade loss.

computePinnedZoomPercent math (timelineZoom.ts:110-123). In manual mode getTimelinePixelsPerSecond computes pps = fitPps × (percent / 100). Solving for percent gives (currentPps / fitPps) × 100, which is exactly what this returns. The four-way guard (!isFinite, <= 0 on both inputs) covers zero-divide + NaN/Infinity + negative — falls back to 100 (a no-op pin at the current fit), which is the right degenerate. Clamp to [MIN, MAX] prevents a ratio outside slider bounds from silently becoming an unreachable zoom. Round-trip test on line 62 verifies getTimelinePixelsPerSecond(fitPps, "manual", percent) restores currentPps — closes the loop.

Log-slider math (timelineZoomPercentToSlider / timelineSliderToZoomPercent). LOG_MIN = ln(10) ≈ 2.303, LOG_MAX = ln(2000) ≈ 7.601. At percent = 100: (ln(100) - ln(10)) / (ln(2000) - ln(10)) × 100 ≈ 43.4 — test at line 35 asserts this exact formula. Round-trip test on lines 80-88 verifies slider→zoom→slider stays within ±1% for [10, 100, 500, 2000] — sufficient tolerance for a slider that only takes integer pixel positions. Slider input is clamped [0, 100] before applying, so out-of-range inputs can't produce non-finite output.

Test coverage. Every new export has a direct test: computePinnedZoomPercent (4 tests: no-op / round-trip / clamp / degenerate fallback), timelineZoomPercentToSlider / timelineSliderToZoomPercent (3+2 boundary + round-trip), timelineSnapEnabled (2: round-trip + non-boolean reject), timelineZoomMode+timelineManualZoomPercent (2: round-trip pinned mode + invalid-input reject), isAudioTimelineElement (5), isMusicTrack (5), resolveBeatSourceTrack (7). No dark exports.

One nit, non-blocking. resolveBeatSourceTrack docstring says "the LONGEST untagged audio clip is used as a fallback" — actual code (if (!best || el.duration > best.duration) best = el) picks the first max on ties. Not wrong, just worth knowing if the ordering matters for beat analysis. Tests don't exercise a tie.

R1 by Via

@ukimsanov ukimsanov force-pushed the studio-dnd/pr01-timeline-leaf-helpers branch from 1f6c9fe to c161c77 Compare July 11, 2026 02:56
@ukimsanov ukimsanov force-pushed the studio-dnd/pr01-timeline-leaf-helpers branch from c161c77 to df6ca19 Compare July 11, 2026 08:18
@ukimsanov ukimsanov marked this pull request as ready for review July 11, 2026 09:06
@ukimsanov ukimsanov force-pushed the studio-dnd/pr01-timeline-leaf-helpers branch from df6ca19 to 063395d Compare July 11, 2026 10:49
…prefs

What: extends three leaf modules to their final NLE-stack form, tests in the
same change: timelineInspector (isAudioTimelineElement, resolveBeatSourceTrack),
timelineZoom (zoom/pps math incl. computePinnedZoomPercent), and
studioUiPreferences (persisted editor prefs).

Why: leaf dependencies of the NLE timeline stack; landing them first keeps
the later glue PRs to wiring.

How: additive from the consumer side — every export main already uses is
unchanged (typecheck against main's consumers passes untouched); every new
export is exercised by a test in this PR.

Test plan: bunx vitest run on the three test files; tsc --noEmit in
packages/studio; fallow audit --base origin/main clean.
@ukimsanov ukimsanov force-pushed the studio-dnd/pr01-timeline-leaf-helpers branch from 063395d to 4a87da0 Compare July 11, 2026 12:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants