Skip to content

feat(timeline): make an audio resize read as the crop it is - #564

Merged
EtienneLescot merged 1 commit into
feat/imported-audiofrom
claude/audio-crop-ux
Sep 1, 2026
Merged

feat(timeline): make an audio resize read as the crop it is#564
EtienneLescot merged 1 commit into
feat/imported-audiofrom
claude/audio-crop-ux

Conversation

@EtienneLescot

@EtienneLescot EtienneLescot commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

Draft — UX prototype, not a finished feature. Opened against feat/imported-audio so it can be looked at and argued with, not merged as-is.

An audio pill is the only timeline object that edits media you cannot see. Every other pill holds a value over a span; a clip's crop produces a clip that is right there on screen. Resizing an audio pill crops an invisible file — and nothing said where in that file the edges had landed, or stopped them at its content. You could pull an edge past the end of the recording and get silence, with no way to know that is what you had done.

Three additions, all renderer-side:

  • Ghost extent. The file's waveform is drawn dimmed around the pill, bounded by the file's own start and end (audioGhostExtent). The pill reads as a window onto a tape, and a resize shows what is still available on each side.
  • Hard stops. The left edge stops where the in-point would hit the file's start, the right where the window would run off its end (audioContentBounds). A null duration means no stop — a failed probe must never freeze a pill it cannot measure.
  • Readout. While an edge is pulled, a chip pinned to the pointer shows in → out over the file's length. 0:00.0 and out = duration are the boundary states; they are self-evident, which is why this prototype adds no locale keys — nothing to translate until the wording is settled.

The left-edge trim keeps its NLE semantics: the ghost follows the in-point live during the drag.

What to argue with

  • Whether the ghost should be permanent or only appear during a drag. Permanent shows the affordance without discovery, at the cost of a busier lane.
  • Whether the hard stop should be hard. A soft stop with resistance is the other convention, and it keeps the edge draggable past the file for someone who wants the silence deliberately.
  • The chip's position — pinned to the pointer competes with the waveform underneath it.

Related issue

Refs #350, and stacked on the audio work in #543. Requires the region model that PR introduced (placeAudioRegions, the pill lanes).

Type of change

  • Enhancement

Release impact

  • No release note needed (draft)

Desktop impact

  • Not platform-specific

Screenshots / video

Not captured. The three additions are only visible mid-drag, so a still frame under-sells them; worth a short capture before this leaves draft.

Testing

  • npm run test2346 passed, 5 skipped, 0 failed (190 files)
  • npx tsc --noEmit and npx tsc -p tsconfig.test.json --noEmit — clean
  • npm run lint (Biome) — clean (15 pre-existing warnings in untouched files)

audio-placement.test.ts covers audioGhostExtent and audioContentBounds, including the null-duration case where the stops must not engage.

Not done: any pass in the real app. The behaviour is a drag gesture, so the unit tests pin the maths and nothing else.

🤖 Generated with Claude Code

An audio pill is the only timeline object that edits hidden media: the
other pills hold values over spans, a clip's crop produces a visible
clip, but resizing an audio pill crops an invisible file - and nothing
on screen said where in that file the edges were, or stopped them at
its content. Three additions, all renderer-side:

- Ghost extent: the file's waveform is drawn dimmed around the pill,
  bounded by its own start and end, so the pill reads as a window onto
  a tape and a resize shows what is still available on each side
  (audioGhostExtent).
- Hard stops: the left edge stops where the in-point would hit the
  file's start, the right edge where the window would run off its end
  (audioContentBounds). Null duration = no stop, so a failed probe
  never freezes a pill.
- Readout: while an edge is pulled, a chip pinned to the pointer shows
  in -> out over the file's length; in 0:00.0 and out = duration are
  the boundary states, self-evident without copy (which is why this
  prototype adds no locale keys).

The left-edge trim keeps its NLE semantics: the ghost follows the
in-point live during the drag.
@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@EtienneLescot
EtienneLescot marked this pull request as ready for review September 1, 2026 17:15
@EtienneLescot
EtienneLescot merged commit 4e0a721 into feat/imported-audio Sep 1, 2026
15 checks passed
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.

1 participant