Segment bar: stable clip numbers, scroll preservation, boundary-accurate playback - #127
Segment bar: stable clip numbers, scroll preservation, boundary-accurate playback#127morepriyam wants to merge 1 commit into
Conversation
|
@morepriyam Do we have a test package? I want to be able to click a button and pull down 2-3 projects that we know to be simple, average, and complex. Average being 10-15 clips, Complex being like 80. |
|
@horner Yes, locally. Dev builds have one-tap seed buttons on the library screen (
Seeding is idempotent, re-pressing is a no-op. I haven't gone as high as 80. Long runs at that scale heat my phone up noticeably and repeated test runs take a real bite out of the battery. Can add a ~80 clip tier from the same fixtures if we want it, it just needs a device someone doesn't mind cooking. |
There was a problem hiding this comment.
Pull request overview
This PR improves the recorder segment bar’s UX and the preview player’s boundary correctness while introducing a stable, per-clip creation ordinal in the DB so segment identity survives reorder/delete. It also extracts and tests the track px↔ms mapping used by the playhead cursor for more reliable boundary behavior.
Changes:
- Add
created_ordinaltosegmentsplus a unique(project_id, sort_order)index; update segment insertion/reorder/import paths to be race-safe and ordinal-stable. - Preserve segment-bar scroll position on delete (and stop auto-scroll-to-newest on record) while updating the bar’s drag handle UI to display the stable ordinal badge.
- Fix preview playback boundary behavior (end-of-draft parking, boundary position reset, play-to-end advance) and extract track mapping math into unit-tested helpers.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/features/recorder/use-preview.ts | Playback boundary correctness, end-of-draft behavior, and swap error handling improvements. |
| src/features/recorder/track-metrics.ts | Adjust vertical lane metrics to accommodate the new ordinal/handle pill. |
| src/features/recorder/track-mapping.ts | New pure ms↔px mapping helpers extracted from the playhead cursor for testability. |
| src/features/recorder/track-mapping.test.ts | Unit tests covering draft/segment boundary mapping and round-trips. |
| src/features/recorder/segment-bar.tsx | Scroll preservation on delete, removal of auto-scroll-to-newest, and ordinal pill drag-handle UI. |
| src/features/recorder/playhead-cursor.tsx | Use extracted mapping helpers and tweak cursor line sizing/layout. |
| src/features/draft-transfer/unpack.ts | Ensure imported segments get sequential createdOrdinal after sorting to satisfy unique order index. |
| src/db/schema.ts | Add createdOrdinal column and unique index definition for (projectId, order). |
| src/db/legacy-migration.ts | Backfill createdOrdinal for legacy draft migration inserts. |
| src/db/drafts.ts | Transactional max()+1 order/ordinal assignment and two-pass reorder to satisfy the unique index. |
| drizzle/migrations.js | Register the new Drizzle migration. |
| drizzle/meta/0011_snapshot.json | Drizzle schema snapshot update reflecting the new column and index. |
| drizzle/meta/_journal.json | Drizzle journal update for migration 0011. |
| drizzle/0011_segment_ordinal.sql | Migration to add/backfill created_ordinal, dedupe/renumber sort_order, and add unique index. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
This is a really good PR. I love the screenshots. |
|
301ac80 adds forward-looking schema support for user-renamable clips: a nullable Design notes:
Type-check and full test suite pass (119 tests). |
d6fd01a to
fa183a8
Compare
fa183a8 to
c55bb21
Compare
…te playback - add stable clip badges via segments.label minted from projects.lastClipNumber - migration backfills labels/order and adds unique (project_id, sort_order) index - preserve segment bar scroll position on delete; drop auto-scroll-to-newest - merge ordinal pill and drag handle; swap strategy for reorder drags - fix preview boundary jank, play-to-end advance, and end-of-draft parking - extract px<->ms track mapping into track-mapping.ts with unit tests - migration uses SQLite-compatible ranking instead of ROW_NUMBER() - reorderSegments uses bulk two-pass updates instead of per-row awaits Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
c55bb21 to
febf743
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 16 out of 17 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
src/db/schema.ts:50
- The PR description (and linked issue notes) still describe a new
created_ordinalcolumn onsegments, but the shipped schema implements stable clip identity viaprojects.lastClipNumber+segments.label. Please update the PR description to match the actual schema (or rename the columns to match the documented concept) so future migrations/data-debugging don’t have two competing models for “stable clip numbers.”
// Monotonic badge counter: the highest clip number ever minted for this draft. Bumped on
// every clip added, never decremented — so deleting (or renaming) a clip can never cause
// its number to be reused.
lastClipNumber: integer('last_clip_number').notNull().default(0),
|
The tests are done on a physical device again after the changes, ready to merge |
Fixes #126
What changed
Stable clip numbers
labelcolumn onsegments, minted from a new monotoniclast_clip_numbercounter onprojects— assigned once when the clip is recorded/imported, never renumbered on reorder, gaps left by deletes are intentional. Shown as a small badge on each thumb, so "move 7 between 3 and 12" stays meaningful however the draft is shuffled.labelis cosmetic metadata only — it is never part of the segment-set signature, so a future rename feature can't invalidate a merged transcript.sort_order, dedupes any historically raced order values, seedslast_clip_numberper draft, then adds a unique(project_id, sort_order)index (Add DB indexes (schema defines none) #64). The renumber step avoids SQLite window functions (noROW_NUMBER()), so it runs on older bundled SQLite builds.addSegmentcomputesorderviamax()+1and mintslabelfromlast_clip_numberinside one transaction — fixes thecount()order race (recorder: stopRecording idempotency, cameraReady latch, segment-order race #96, item 3).reorderSegmentsrenumbers in two bulk passes (park on negatives, then a singleCASEupdate) so the unique index holds mid-update without per-row round-trips..pulseimports renumber sequentially (a foreign bundle with duplicate orders can no longer abort the import).Scroll ownership
Preview playback correctness + playhead polish
positionMsat the outgoing clip's near-end value for one render, so the knob jumped mid-thumb into the next thumbnail, then slid back to its left edge. Position is now reset in the same render as the selection swap — the knob lands exactly on the incoming thumb's left edge.timeUpdatehandler advanced up to 60 ms before the out-point, cutting the last frames of every clip. Under the destructive-edit model the out-point is the file's end, soplayToEnd(which always fires) is now the sole advance trigger.replaceAsync(...).then(begin)now has a.catchthat disarms the swap guards (also from player: gapless segment preview (preload next player) + end-of-draft seek bug + missing-file handling #97).Tests
track-mapping.tsand unit-tested: draft start → x 0, draft end → last thumb's right edge, segment boundaries → incoming thumb's LEFT edge, proportional in-clip mapping, zero-length-clip skipping, px↔ms round-trip.Scope notes
Manual test checklist
.pulsebundle → clips numbered 1..n in bundle orderScreenshots
Stable clip numbers — delete clip 2 of 5
main: bar renumbers)Scroll preservation on delete + stable numbers through reorder
ScreenRecording_07-26-2026.14-08-40_1.mov