- computePeaksInWorker now accepts an AbortSignal; terminates the worker
immediately on abort and rejects with AbortError so orphaned workers
cannot outlive the effect cleanup
- useAudioPeaks wires an AbortController into the effect; controller.abort()
fires alongside the existing cancelled flag on URL change or unmount
- loadSourceFile handles data: URLs by parsing the MIME type, deriving a
safe filename (e.g. video.mp4), and setting Blob type correctly
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add docstrings to all undocumented functions and methods across the
files touched in this branch: getAudioCtx, Row component, and all
StreamingVideoDecoder methods (loadSourceFile, computeSegments,
getExportMetrics, splitBySpeed, getDemuxer, cancel, destroy, withTimeout).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move the peaks null-check after clearRect so a previously drawn
waveform is erased when peaks transitions to null (e.g. on URL change
or decode failure) rather than lingering on screen.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use 剪輯 (editing/clip) instead of 修剪 (trimming) to match the term
already used in the trim section throughout zh-TW strings.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reset peaks to null immediately when an uncached URL is set, so the
previous video's waveform is not shown during decode. Also clear on
decode failure so stale data does not persist indefinitely.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Rename to BackgroundWaveform to reflect its visual role rather than
structural placement. Remove the redundant wrapper div — Row already
provides relative overflow-hidden, so the canvas can sit absolute
inset-0 directly and observe itself via ResizeObserver.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add timeline.title and timeline.waveform keys to every settings.json
locale file. Replace all hardcoded "Timeline" and "Show Audio Waveform
on Trim Track" strings in SettingsPanel with t() calls.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move computePeaks into audioPeaksWorker.ts — runs off the main thread
with zero-copy channel buffer transfer
- Replace module-level peaksCache Map with a useRef scoped to the hook
instance — no global mutable state, toggle off/on still instant
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a single exported loadFileAsArrayBuffer in streamingDecoder.ts that
owns the IPC-vs-fetch branching for local and remote video URLs.
StreamingVideoDecoder.loadSourceFile and useAudioPeaks both delegate to
it, removing the duplicated readBinaryFile/fetch logic.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move fetch/decode/cache logic out of RowWaveform into a standalone
useAudioPeaks hook. RowWaveform is now a pure canvas renderer that
accepts pre-computed peaks as a prop. TimelineEditor calls the hook
and passes peaks down, skipping decoding entirely when waveform is off.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The 0.25 bump was a workaround for hint visibility over the waveform
background, but is now stale — waveform defaults to off and hints only
appear on empty rows anyway. Restores consistency with all other row hints.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace AudioWaveform icon with Brackets on the Timeline panel tab and section header
- Rename toggle label from "Waveform" to "Show Audio Waveform on Trim Track"
- Make toggle full-width to accommodate the longer label
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add "Timeline" as a dedicated SettingsPanelMode with AudioWaveform icon
- Waveform toggle now lives exclusively in the Timeline panel (not in every panel)
- Default showTrimWaveform to false so trim row starts with visible hint text
- Guard AccordionItem render with activePanelMode === "timeline" to prevent bleed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add RowWaveform component (refactored from TrimWaveform) that decodes
audio via AudioContext, caches peaks per URL, and renders a faint
waveform canvas behind any timeline row using useTimelineContext() for
zoom/pan alignment
- Wire RowWaveform as background of the trim row in TimelineEditor
- Add showTrimWaveform boolean to EditorState / ProjectEditorState with
default true; persists across saves/loads
- Add Timeline Settings accordion section in SettingsPanel (always
visible) with a Waveform toggle to enable/disable the visualization
- Bump hint text opacity from 0.12 to 0.25 so "Press T to add trim"
remains readable over the waveform background
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
PR #600 (now on main) removed WEBCAM_TARGET_WIDTH/HEIGHT and switched
this call site to width/height: 0 so the native helper picks the
camera's native dimensions. Align this branch with that so CI's
fresh PR-merge stops erroring on the undeclared identifiers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes MAX_PLAYBACK_SPEED and DEFAULT_WEBCAM_SIZE_PRESET (TS6133) and
runs biome's organize-imports to satisfy the Lint check.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Record browser webcam sidecar when native Windows capture is active.
Add native webcam sidecar output and DirectShow NV12/YUY2 fallback.
Sample exported webcam frames by source timestamp.
The aspect ratio dropdown showed 'Native', which is video-industry jargon
that isn't self-explanatory for most users. Renaming it to 'Original'
makes it immediately clear that this option preserves the source video's
own dimensions.
The internal `"native"` value in the AspectRatio union type is unchanged;
only the display string returned by `getAspectRatioLabel()` is updated.
Closes#607
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The a686fa0 override replaced findDominantRegion's resolved region with the
raw stored region (forcing strength=1 / transition=null). findDominantRegion
already resolves focus via getResolvedFocus — for focusMode:"auto" it
interpolates the cursor-followed focus from telemetry and applies clamp/blend/
transition. The override bypassed all of that, so previewing an auto-focus
zoom showed a stale static focus and an instant un-eased zoom that did not
match real playback/export.
Hold-to-preview now shows the natural zoom for the current playhead frame
(true before/after compare). The isPreviewingZoom flag is kept — it only
disables the un-zoomed editing guard so findDominantRegion's result is shown.
Previewing while the playhead is outside any zoom shows no zoom by design.
- VideoPlayback: while holding Preview, render the SELECTED zoom at full
strength regardless of the playhead, instead of whatever findDominantRegion
returns at currentTime (which is none/another zoom when the playhead is
outside the selection). Uses getZoomScale/getRotation3D for the region's
configured scale and 3D preset.
- SettingsPanel: require both onZoomPreviewStart && onZoomPreviewEnd to render
the button (full lifecycle), and add keyboard support — Space/Enter keydown
(repeat-guarded) starts preview, keyup/blur ends it.
When a zoom region is selected and paused, the editor shows the full
un-zoomed frame for focus-point placement. This adds a press-and-hold
"Preview" button so editors can momentarily see the zoomed result at the
current focus + depth — like a before/after compare — without entering
playback.
- VideoPlayback: new transient isPreviewingZoom prop; shouldShowUnzoomedView
now also requires !isPreviewingZoom, so the zoom transform is applied at
the playhead while previewing
- VideoEditor: isPreviewingZoom state wired to VideoPlayback and to
onZoomPreviewStart/End handlers
- SettingsPanel: hold button in the zoom controls (pointer down/up/leave/
cancel)
- i18n: zoom.previewHold added across all 11 locales
Prototype for #612 — placement (panel vs overlay) and hold-vs-toggle still
open for maintainer direction.