Skip to content

feat: add macOS native capture and cursor pipeline#1

Closed
EtienneLescot wants to merge 2 commits into
feat/cursor-pipelinefrom
feat/macos-native-capture-pipeline
Closed

feat: add macOS native capture and cursor pipeline#1
EtienneLescot wants to merge 2 commits into
feat/cursor-pipelinefrom
feat/macos-native-capture-pipeline

Conversation

@EtienneLescot

Copy link
Copy Markdown
Collaborator

Description

This draft PR starts the macOS-only port of the native capture and cursor pipeline introduced for Windows in PR siddharthvaddem#217.

The goal is to bring the same architectural separation to macOS: Electron owns recording/session orchestration, while a native ScreenCaptureKit helper owns screen/window capture, cursor exclusion, audio/webcam timing, encoding, and muxing.

This PR is currently stacked on feat/cursor-pipeline so the diff stays focused on macOS. It should be retargeted to main after the Windows-native PR lands.

What changed

  • Adds a macOS native recorder roadmap at docs/engineering/macos-native-recorder-roadmap.md.
  • Defines the first macOS helper contract in src/lib/nativeMacRecording.ts.
  • Adds source-id parsing helpers for ScreenCaptureKit display/window ids.
  • Adds focused tests for macOS source-id parsing.
  • Adds npm run build:native:mac as a placeholder build entrypoint.
  • Documents the expected macOS helper layout in electron/native/README.md.

macOS-only scope

This PR does not change Windows native capture behavior and does not add Linux native capture.

The macOS helper implementation is not shipped yet. This first step is the architecture/contract scaffold so the native helper can be added behind a clean boundary, matching the Windows WGC helper separation.

Architecture notes

The intended macOS backend follows the same separation used by the Windows helper and the same broad platform approach seen in Recordly:

  • macOS capture through ScreenCaptureKit-based native helpers.
  • Windows capture through a separate WGC helper.
  • Electron as the process/session coordinator.
  • Renderer code consuming stable contracts rather than helper-specific process details.

Validation run

Passed locally on Windows:

  • npm run build:native:mac (skips successfully on non-macOS hosts)
  • npm run build-vite
  • npx vitest run src\lib\nativeMacRecording.test.ts --pool=threads
  • npm test (137 tests passing)

Maintainer test checklist

Capture + Launch

  • pre commit check on macOS
  • Source selector opens and lists screens/windows with thumbnails
  • Selecting a source works for full screen and app window
  • Record start/stop works from launch window
  • Tray “Stop Recording” works while recording
  • Opening existing video file works from launch window
  • Opening existing project works from launch window

Audio

  • Mic toggle on/off works before recording
  • Mic device selection works
  • System audio toggle works across recordings
  • Mic-only recording works
  • System-audio-only recording works
  • Mic + system audio mix works and levels are balanced

Editor Load + Playback

  • Playback, pause, seek works.
  • Cursor telemetry overlays correctly

Timeline

  • Add/edit/remove different elements (zoom/ annotation/ trim/ speed), see how they are in playback and if they are 1:1 with exported video
  • Region drag/resize snaps and persists correctly
  • No overlap/ordering bugs on timeline items (apart from annotations)

Project Persistence

  • save/ load works

Other

  • Tweaking sliders/ wallpapers/ other effects.

@EtienneLescot
EtienneLescot force-pushed the feat/macos-native-capture-pipeline branch from a4786a5 to 603e06c Compare May 11, 2026 08:33
@EtienneLescot

Copy link
Copy Markdown
Collaborator Author

Closing this duplicate PR because the macOS native capture branch is now opened against the upstream repository: siddharthvaddem#573

EtienneLescot added a commit that referenced this pull request Jun 25, 2026
PR #1 of the docs site effort tracked in ROADMAP.md (Stability & quality tier).

- website/: minimal Docusaurus 3 site (TS config, Infima-tweaked theme,
  hero landing with WIP badge, intro doc only).
- .github/workflows/docs.yml: build on PR, deploy to GitHub Pages on push
  to main. Concurrency group, minimal permissions.
- Local postcss.config.cjs to override monorepo Tailwind config so
  Docusaurus CSS pipeline doesn't try to load Tailwind.

PR #2 will polish the landing (full bento, demo, footer polish).
PR #3 will migrate docs/ -> website/docs/.
EtienneLescot added a commit that referenced this pull request Jul 1, 2026
Three follow-up bugs from the last test pass:

#1 — Navigator strip not visible. After moving it into
TimelinePane (commit 0d3600e) the section's flex layout shrunk it to
0 because the wrapper has overflow: hidden and the section is
height: 100% with a flex: 1 viewport sibling. Fix: .timelineNavigator
gets flex-shrink: 0 + 4px margin (visual separation from the
viewport above). The strip now has a stable ~20px row of its own
below the header.

#2 — Place-skip button is redundant. Per user feedback the
correct button is the Scissors (Trim) one in the top toolbar — the
header 'Place skip' button I added in C3 is removed. The Scissors
now toggles pendingCutPlacement (was: called tl.addSkip() to drop
a skip at currentTimeSec). The 'T' keyboard shortcut in
NewEditorShell also toggles via a ref (togglePlaceSkipRef) that
Bottombar registers through a setTogglePlaceSkip prop drilling.

#3 — Place-skip didn't accept clicks on clip blocks. The handler
excluded any target inside [data-clip-idx], so clicking the visible
clip track area did nothing. Fix: only buttons (skip chevrons / edit
/ delete) are excluded. Clicks on clip blocks, lanes, ruler, and
empty space all dispatch addCut. Stops propagation on the click so
the clip's onClick (select) doesn't also fire.

State ownership: pendingCutPlacement + pendingCutPreviewSec + Esc
handler + body class live in Bottombar. TimelinePane reads them as
props. NewEditorShell's 'T' shortcut reads them via ref.

Verified: tsc clean, biome clean, 412 tests pass.
EtienneLescot added a commit that referenced this pull request Jul 1, 2026
#1 — Navigator strip was missing visually despite flex-shrink: 0
(commit fb2bfe2). Root cause: the .timelineNavigator CSS lived in
NewEditorShell.module.css, but TimelinePane applies the className
via its own styles import (TimelinePane.module.css). CSS modules
generate per-file class hashes — .timelineNavigator in
NewEditorShell becomes NewEditorShell_timelineNavigator__xxx, while
styles.timelineNavigator in TimelinePane resolves to
TimelinePane_timelineNavigator__yyy. The two never match, so the
element had no styles at all → 0 height → invisible.

Fix: move the entire .timelineNavigator + child class block from
NewEditorShell.module.css into TimelinePane.module.css so the CSS
hashes line up with the JSX className application.

#2 — Tightened the place-skip crosshair override to also cover
.timelineHeader, every button inside it, and the VtBtn toolbar
buttons. Some of these have their own cursor values that were
overriding the body-level crosshair rule.

Verified: tsc clean, biome clean, 412 tests pass.
EtienneLescot added a commit that referenced this pull request Jul 2, 2026
Three follow-up bugs from the last test pass:

#1 — Navigator strip not visible. After moving it into
TimelinePane (commit 0d3600e) the section's flex layout shrunk it to
0 because the wrapper has overflow: hidden and the section is
height: 100% with a flex: 1 viewport sibling. Fix: .timelineNavigator
gets flex-shrink: 0 + 4px margin (visual separation from the
viewport above). The strip now has a stable ~20px row of its own
below the header.

#2 — Place-skip button is redundant. Per user feedback the
correct button is the Scissors (Trim) one in the top toolbar — the
header 'Place skip' button I added in C3 is removed. The Scissors
now toggles pendingCutPlacement (was: called tl.addSkip() to drop
a skip at currentTimeSec). The 'T' keyboard shortcut in
NewEditorShell also toggles via a ref (togglePlaceSkipRef) that
Bottombar registers through a setTogglePlaceSkip prop drilling.

#3 — Place-skip didn't accept clicks on clip blocks. The handler
excluded any target inside [data-clip-idx], so clicking the visible
clip track area did nothing. Fix: only buttons (skip chevrons / edit
/ delete) are excluded. Clicks on clip blocks, lanes, ruler, and
empty space all dispatch addCut. Stops propagation on the click so
the clip's onClick (select) doesn't also fire.

State ownership: pendingCutPlacement + pendingCutPreviewSec + Esc
handler + body class live in Bottombar. TimelinePane reads them as
props. NewEditorShell's 'T' shortcut reads them via ref.

Verified: tsc clean, biome clean, 412 tests pass.
EtienneLescot added a commit that referenced this pull request Jul 2, 2026
#1 — Navigator strip was missing visually despite flex-shrink: 0
(commit fb2bfe2). Root cause: the .timelineNavigator CSS lived in
NewEditorShell.module.css, but TimelinePane applies the className
via its own styles import (TimelinePane.module.css). CSS modules
generate per-file class hashes — .timelineNavigator in
NewEditorShell becomes NewEditorShell_timelineNavigator__xxx, while
styles.timelineNavigator in TimelinePane resolves to
TimelinePane_timelineNavigator__yyy. The two never match, so the
element had no styles at all → 0 height → invisible.

Fix: move the entire .timelineNavigator + child class block from
NewEditorShell.module.css into TimelinePane.module.css so the CSS
hashes line up with the JSX className application.

#2 — Tightened the place-skip crosshair override to also cover
.timelineHeader, every button inside it, and the VtBtn toolbar
buttons. Some of these have their own cursor values that were
overriding the body-level crosshair rule.

Verified: tsc clean, biome clean, 412 tests pass.
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.

2 participants