KQ-1734 · TIMECODE/NOTES

Timecode Notes

Review notes pinned to moments in a clip: the clip plays in a procedural frame (or your own `renderFrame`), every note is a marker on the timeline, and the notes list sits beside the player, under it below a 40rem container. As the playhead passes a marker it pulses on springs.snap and its note rises into the foot of the frame on springs.recoil, holds, and leaves on the exit ease; choosing a note, marker or cluster glides the playhead there on springs.glide while the frame cuts to its scene, and markers closer than 14px merge into counted bubbles. Space plays, M adds a note at the playhead, and the timeline is a slider whose arrows, Page keys, Home, End and [ ] brackets move by seconds and between notes.

KQ-1734 · TIMECODE/NOTES

Waylight onboarding cut · v3

1:24 · 6 notes · 5 open

Space plays and pauses, M adds a note at the playhead. Arrows step a second, Shift with an arrow five, Page Up and Page Down ten. Left and right bracket move to the previous and next note.

Cold open
0:00 / 1:24

ready · 1:24 · 6 notes · 5 open

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/timecode-notes

Props

media
TimecodeMedia
default defaultTimecodeMedia
The clip: { title, kind: "video" | "audio", duration, scenes?: { at, label }[], peaks?: number[] }.
notes / defaultNotes
TimecodeNote[]
default — / defaultTimecodeNotes
Notes as { id, at (seconds), author, text, createdAt, resolved? }, controlled or initial.
onNotesChange
(notes) => void
default —
Fires with every note after an add or a resolve.
time / defaultTime
number
default — / 0
The playhead in seconds, controlled or initial; a controlled change of more than a second seeks.
onTimeChange
(seconds) => void
default —
Fires on every seek and once a second while playing, with whole seconds.
playing / defaultPlaying / onPlayingChange
boolean
default — / false
Playback, controlled or initial, and the callback from the button, key or end of clip that changed it.
onAdd
(note) => void
default —
A note was added at the playhead; it is already in the list.
onSeek
(time, noteId?) => void
default —
The visitor moved the playhead; `noteId` when a note, marker or cluster was chosen.
onResolve
(noteId, resolved) => void
default —
A note was resolved (true) or reopened (false).
renderFrame
(time, media) => ReactNode
default procedural frame per scene
Draw the frame yourself; called with whole seconds and on every seek.
markerStyle
"tick" | "dot" | "avatar"
default "tick"
A hairline tick with a cap above the track, a dot on the track, or the author's initial on a stem.
density
"compact" | "regular" | "roomy"
default "regular"
List type and padding, and the size of the player's controls.
accent
string
default "var(--accent-bright)"
Playhead, fill, markers and the current note's edge — any CSS colour; fills take its hue at a fixed lightness.
holdMs
number
default 2600
How long a passed note stays risen over the frame, in ms.
author
string
default "Noor Haddad"
The name new notes are written under.
now
number | Date
default defaultTimecodeNow
The moment relative times count from, so server and browser render the same text.
title
string
default media.title
The heading and the surface's accessible name.
status
"ready" | "loading" | "error"
default "ready"
Loading shows a skeleton frame and list; error shows Try again.
onRetry
() => void
default —
Try again was pressed after the clip failed to load.
height
number
default 540
The surface's height in px; the player and notes scroll inside it.
disabled
boolean
default false
Read only: the clip plays and notes can be read and chosen, nothing added or resolved.
className
string
default —
Classes for the surface.