Skip to content

feat: add osd-flash for screenshot indicators#322

Draft
sassman wants to merge 40 commits intomainfrom
feat/osd-flash-for-screenshots
Draft

feat: add osd-flash for screenshot indicators#322
sassman wants to merge 40 commits intomainfrom
feat/osd-flash-for-screenshots

Conversation

@sassman
Copy link
Copy Markdown
Owner

@sassman sassman commented Jan 15, 2026

Summary

Adds on-screen display (OSD) flash indicators for visual feedback during recording:

  • New crates/osd-flash crate with GPU-accelerated animations via Core Animation
  • Recording start indicator (pulsing red dot)
  • Screenshot taken indicator (camera flash)
  • Optional feature osd-flash-indicator (disabled by default)

Dependencies

  • Depends on #XXX (crossterm-input-interception) being merged first
  • After that PR lands, this branch needs to be rebased on main

Changes

  • crates/osd-flash/ - New OSD flash library
    • Platform-agnostic API with macOS Core Animation backend
    • Pre-built compositions (RecordingIndicator, CameraFlash)
    • Custom error types (migrated from anyhow to thiserror-style)
  • crates/t-rec/ - Integration
    • New osd-flash-indicator feature flag
    • OSD presenter for visual feedback during recording

Test plan

  • Rebase on main after crossterm-input-interception merges
  • Verify cargo build works
  • Verify cargo build --features osd-flash-indicator works
  • Test recording with OSD indicators enabled

Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
…tors

- also restructure threading and messaging so that we can broadcast events between threads (e.g. screenshots)

Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Enable cleaner architecture by splitting into workspace with skylight-osd crate for on-screen display functionality.
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
- also add a README.md explaining a quickstart and the API and the examples
- fix glitch with one particular but random white border showing up

Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
…new abstraction

Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
…se backend internals in the prelude module

Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
- Add layout module (Padding, Margin, Border, BoxModel)
- Add style module (Paint, TextStyle)
- Add background() and corner_radius() to OsdFlashBuilder
- Add examples: battery, equalizer, radar, sierpinski
- Fix canvas trait to use ref parameters
- Fix content bounds and center positioning
- Add a keyframe based API to the OsdWindowBuilder
  - interpolates numerical values of color and scale between the phases of keyframes
  - has change detection based on object order in the keyframe "drawing"
  - based on the the architecture at `.planning/osd-flash-animation-architecture.md` the documentation is on a module level at `crates/osd-flash/src/animation/mod.rs`
- Add a pulsing animation example (crates/osd-flash/examples/recording_indicator_pulse.rs)
- todo: there is a small flickering, so we explore if double buffering from CALayer might help us
Add `osd-flash-indicator` feature flag (enabled by default) that:
- Uses dual cfg guard (target_os + feature) for proper platform handling
- Falls back to NoopPresenter when disabled or on non-macOS
- Removes osd-flash from dependency tree when disabled

This provides a rollback strategy if OSD issues arise:
  cargo build --release --no-default-features

Also adds --no-default-features CI testing to verify rollback path.
… animations

Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
…ngs for the core-animation examples

Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
- we are just about to overhaul osd-flash and simplify it greatly

Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Tests failed on CI because the stop timer started before capture began.
On slow CI machines, timeout expired before all frames were captured.

TestApi now sends Stop when the last frame is returned, removing
timing dependency entirely.
- Remove duplicate show_recording_indicator() call in presenter
- Move RecordingStarted event from countdown loop to fire once after countdown
…rate to objc2- crates for t-rec

Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Replace anyhow with custom Error enum for better API stability:
- Add Error::NotOnMainThread for thread safety violations
- Add Error::NoScreenAvailable for missing display
- Replace panics with proper error propagation in apply_position
sassman added a commit that referenced this pull request Jan 22, 2026
## Interactive Recording with PTY and Hotkeys

Replaces the simple subprocess shell with a PTY-based architecture and
adds keyboard hotkeys for in-session control.

- Solves #308 
- Improves the Countdown timer, see #228 

### Changes

**PTY shell spawning**
- Shell now runs in a proper pseudo-terminal (`nix::pty::openpty`)
- Enables raw mode keyboard interception while maintaining full shell
interactivity

**Keyboard hotkeys via crossterm**
- F2: Capture a screenshot (saved as PNG with effects applied)
- F3: Toggle keystroke capture (placeholder for future overlay feature)
- Ctrl+D: End recording
- Other input forwarded to shell unchanged

**Event-driven actor model**
- New `EventRouter` coordinates capture, input, shell output, and
presenter actors
- Uses `tokio::sync::broadcast` for inter-actor communication

**Recording countdown**
- 3-second countdown before capture begins

**On-screen visual feedback (macOS, optional)** 
- moved to separate PR #322 

**Headless recorder library API**
- using t-rec as a library
- `HeadlessRecorder` for programmatic recording of any window to GIF/MP4
- Builder pattern with type-safe config (`Decor`, `BackgroundColor`,
`Wallpaper` enums)
- Includes post-processing pipeline (shadows, corners, wallpaper
backgrounds)

**Lazy logging**
- Log file (`t-rec-recording.log`) only created when something is logged

### Structure

Reorganized as a Cargo workspace:
- `crates/t-rec` — CLI and library

---------

Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
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