SourceSelector.test.tsx uses toBeInTheDocument but never imported
@testing-library/jest-dom, and vitest.config.ts has no setupFiles to
register the matchers globally. It only passed when test ordering
happened to leak the matcher from another file, making CI intermittently
red on main (e.g. the #652 merge). Importing jest-dom directly makes the
test self-sufficient and deterministic.
- EditorEmptyState shown when no video is loaded: Import Video, Load
Project, and drag-drop of .openscreen files.
- "Open Studio" button in the recording HUD (replaces the two separate
open-video / open-project buttons).
- New Project flow that clears back to the dashboard, prompting to save
on unsaved changes; Load Project now prompts too (variant-aware dialog).
- Rebuilt on current main, preserving main's export refactor, cursor-clip
/ zoom-preview / trim-waveform work and the vertical/horizontal HUD tray.
- Replace lastEmittedAssetId with a process-wide Set<String> so each unique
cursor shape is serialised at most once even across non-adjacent repeats
(e.g. arrow → text → arrow no longer resends the arrow bitmap)
- Wrap the sampling loop body in autoreleasepool{} to prevent Cocoa objects
(NSBitmapImageRep, PNG Data, base64 String) from accumulating for the
lifetime of the helper during long recordings
- Update stale Accessibility comments: missing Accessibility only disables
text/pointer affordance detection; native bitmap capture is unaffected
Capture the real system cursor image during macOS recording so custom and
default cursors render natively instead of being mapped to bundled SVGs,
bringing macOS in line with the Windows WGC capture path.
- macOS cursor helper grabs NSCursor.currentSystem as a PNG asset (SHA256 id,
intrinsic scale factor, pixel hotspot); the bitmap payload is emitted once per
shape and referenced by assetId thereafter
- helper returns nil cursorType instead of an arrow fallback so default/custom
cursors fall through to the captured bitmap while text/pointer stay beautified
- MacNativeCursorRecordingSession collects deduped assets, tags samples with
assetId, and reports provider "native" when bitmaps are captured
Maps KeyboardEvent.key values (e.g., "ArrowDown", " ") to their
Electron accelerator equivalents (e.g., "Down", "Space") to prevent
global shortcut registration failures.
The per-column fillRect skipped columns with amplitude < 0.5 px, causing
the waveform to disappear entirely at certain zoom levels. Revert to the
closed polygon + top-edge stroke which always renders something.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two fixes:
1. topInset/bottomInset both set to 3 — items are center-aligned in the
36 px row (3 px gap top and bottom), so the drawing zone must match.
topInset=0 was drawing 3 px above the trim box top.
2. Replace closed polygon with per-column fillRect so silent sections
(barH < 0.5 px) produce no pixels instead of a flat green baseline.
The top-edge stroke lifts the pen across silent gaps.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the single `verticalInset` CSS prop (which tried to shrink the
canvas element) with `topInset`/`bottomInset` drawing-space offsets.
The canvas remains `absolute inset-0` (full row height); the polygon
baseline is drawn at `H - bottomInset` and the amplitude is scaled to
`drawHeight * 0.9` — so the waveform is guaranteed to stay within
[topInset, H - bottomInset] regardless of sub-pixel CSS rounding.
Trim row: topInset=0, bottomInset=6 (row minHeight 36 − item height 30)
eliminates the faint green line that was visible below the trim box.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The trim item content is 30 px tall inside a 36 px row, leaving 3 px of
vertical padding top and bottom. Previously the canvas used `inset-0` so
the waveform filled the full row height. A new `verticalInset` prop
(defaulting to 0) offsets the canvas top/bottom edges; the trim row
passes `verticalInset={3}` so the waveform top and bottom align exactly
with the trim box bounds.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the bipolar ±center polygon with a rectified half-waveform:
amplitude per column is max(|min|, |max|) and the filled shape rises
from the bottom of the row upward, matching the single-sided waveform
display common in audio/video editors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Switch the trim-row waveform from faint white vertical strokes to a
green filled polygon (upper max edge left→right, lower min edge
right→left) with crisp edge strokes — matching the look of Final Cut
Pro / DaVinci Resolve waveform displays.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Codex re-review: if openRecordingStream exists but appendRecordingChunk
does not (renderer/main version skew), the recorder would open the stream
and switch to streaming mode, but every append silently no-ops and the
save ends up empty. Require both IPC methods before streaming; otherwise
fall back to in-memory buffering. Adds a regression test.
Verified: tsc --noEmit clean; biome clean; vitest 183/183.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses the CodeRabbit + Codex re-review of the prior commit.
- Normalize a rejected append (channel/handler error, not just a
{ success: false } result) into appendError, so the write queue never
rejects and isStreaming() stays consistent after a failure (CodeRabbit).
- Handle a rejected open-stream IPC the same as a failed open: fall back
to in-memory buffering instead of leaving the recorder stuck "pending"
with an unhandled rejection (CodeRabbit).
- Discard a streamed webcam whose write failed even when the screen save
succeeds. The cleanup gate is now per-recorder, so a webcam omitted from
a successful screen-only save no longer leaks its stream and partial
file (Codex).
Adds tests for the rejected-append and rejected-open paths.
Verified: tsc --noEmit clean; biome clean; vitest 182/182.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>