---
title: Screen Recorder (record, edit and share your screen)
---

# Screen Recorder

## What it is

> **The capture itself is desktop-app only** — it needs Electron's `desktopCapturer` plus hidden BrowserWindows to run `MediaRecorder` and the offscreen export bake, none of which exist in a headless process or a browser. **But an agent session does NOT need IPC to drive it:** the CLI control server exposes `/capture/*` routes that ASK the running app to do the work — `POST /capture/recording/start` · `/capture/recording/stop` · `/capture/recording/device-prompt` (answers the "a device you chose did not open" question a take can stop at; `GET /capture/state` shows it as `pendingDevicePrompt`) · `/capture/screenshot`, `GET /capture/state` · `/capture/recordings` · `/capture/:id`, and the frame routes in [Agent frame access](#agent-frame-access-cli) below. Reach for those first; `screen-recorder:*` IPC is for main-process code, not for agents. (This note previously said no CLI route existed and none was planned — that was already wrong when several `/capture/*` routes had shipped, and it sent agents off to build IPC plumbing they did not need.)

Screen Recorder is the video-capture half of Omniscio's built-in screen capture — the built-in Loom alternative. Capture your screen (with optional microphone and webcam), review the result, trim/rename it in a built-in editor, and share a public link or upload to Vimeo. Its sibling is the [Screenshot / Snip Tool](screenshot-snip-tool.md) — both store rows in the same `screen_recordings` table (a snip is a row with `type='image'`, a recording is `type='video'`), so the store, editor, review, and share surfaces are shared infrastructure. The **library is NOT shared UI anymore**: each area shows only its own type (see below).

The feature appears as a **Screen Recording** entry (Camera icon, red-400) in the Omniscio sidebar under the **Communication** group. Sidebar-name history: it shipped as "Screen Recordings", was rescoped to the unified "Screen Capture" umbrella (v0.1.67, covering snip + record in one panel), then **split apart again 2026-08-03** into two sibling entries — **Screen Recording** (this doc; video only) and **[Screenshots](screenshot-snip-tool.md)** (the snip tool + a snips-only library). The underlying sentinel `__screen_recordings__` and the `screenRecorderEnabled` flag are byte-stable and never change — only the displayName moved. The sibling Screenshots area is a second integration (`__screenshots__`) that shares the same table and store, but as of the **2026-08-08** split it has its **own** `screenshotsEnabled` toggle — each area's toggle shows/hides only that area (Screen Recording stays on `screenRecorderEnabled`; see [Screenshots](screenshot-snip-tool.md)).

A recording can also be handed to an AI to review with you — mark the moments that matter while you record, then send the whole thing to one conversation that shows you a cropped screenshot every time it refers to something. That mode is documented separately in [Record for Agent](record-for-agent.md); everything on this page still applies to it, since it is a mode on top of this recorder rather than a second one.

All capture is **local-first**. A hidden host renderer streams `MediaRecorder` chunks to the main process, which writes them to `<userData>/screen-recordings/<id>/segments/`, then a one-shot ffmpeg pass transcodes (and, for multi-source, composites) the WebM chunks into a single H.264 + AAC MP4. The MP4 stays on disk until you share or discard. Sharing uploads to the Omniscio Shares Firebase project and mints a public link; nothing leaves your machine until you explicitly share.

While a capture is live, the hidden host renderer is held at **HIGH** CPU priority and Omniscio's own main process is capped one step below it, so the recording outranks the thread that writes its frames — including while you work in the app being recorded, which is the normal case. This is the one place a renderer is allowed to outrank the app's event loop, on the reasoning that a dropped frame cannot be recovered while a late chunk write can. It is released the moment the capture ends (stop, discard, abort, or a crash), works whether or not `boostAmcProcessPriority` is on, and has its own kill switch `AMC_DISABLE_SCREEN_RECORDER_PRIORITY=1`. Disk I/O priority is deliberately NOT raised: Windows refuses a non-elevated process any I/O priority above Normal, and the app already sits there while every agent process sits below it. Governed by `recorder-outranks-the-event-loop-while-recording` in [app-tree-priority-contract.md](/.claude/memory/contracts/app-tree-priority-contract.md); read the applied state from `_debugAppTreePriorityState()` (`recording`, `recordingPid`, `recordingClass`).

## Where to find it

### Opt-in toggle

Screen Recording is **off by default for new installs**. Flip **Settings → Features → Enable Screen Recording** (`screenRecorderEnabled`) to turn it on — its own toggle since the **2026-08-08** split from the sibling Screenshots area (each has an independent switch; see [Screenshots](screenshot-snip-tool.md)). When off: the sidebar row is hidden, every `screen-recorder:*` IPC call is rejected at the handler with a `{ success: false }` envelope before the service is touched, and no recorder hotkeys register. Existing MP4s and library rows are left untouched. An in-flight recording started before you flipped the toggle off is left running — the gate fires on new start/quick-start calls, not on active captures. Settings search keywords "screen recorder", "loom", "recording", "capture", "video", "share recording" all land on this toggle. Every OTHER Screen Recording option (shortcuts, quality, webcam, click ring, countdown, watermark, extras, link expiry, Vimeo token) lives IN the tool — the gear icon in the Screen Recording header opens the **Screen Recording settings** page (its own page since the 2026-08-08 split; moved out of Settings 2026-07-07). That page is laid out as **five tabs**, so no single screen is a wall of settings:

| Tab | Holds |
| --- | --- |
| **Recording** | quick-record shortcut · recording defaults · recording quality · countdown before recording · play-a-sound-when-recording-starts · auto-title recordings |
| **Webcam & clicks** | webcam shape · webcam preview size · webcam preview corner · click-ring style, colour and size |
| **Watermark & intro** | watermark mode, text, image and position · intro card and its name |
| **Sharing** | copy-a-share-link-when-I-stop · quick-share link expiry · default link expiry · Vimeo access token |
| **Advanced** | auto-zoom to clicks · click sound · Record for Agent · the Record-for-Agent video pass · do-not-disturb while recording · export folder · take the tour |

Only the open tab's settings are on screen at a time — that is what keeps the page short — and **Ctrl/Cmd+Tab cycles the tabs** (Ctrl/Cmd+Shift+Tab goes backwards) from anywhere inside the dialog. The **Screenshots** settings page is deliberately NOT tabbed: it holds only a handful of rows. Every setting id, description and default is unchanged by the tab layout — the shelf a setting sits on is the only thing that moved.

## How it behaves

### How to use it

### Start a recording

Two entry points:

While Screen Recording is enabled, Omniscio keeps the recorder countdown and controls loaded in
the background without capturing screen, microphone, or camera media. Capture cannot begin until
the current control HUD has positively confirmed that its React controls mounted and painted.
During an unusually slow startup or renderer recovery, Quick Record instead says that nothing was
recorded; it never queues a delayed surprise start.

- **Quick Record (Fastpath)** — press `Ctrl/Cmd+Alt+R`. It records your **default preset's** exact scene with no picker and no modal; with no default preset it records the built-in default (the last-used source when it's still available, else the primary monitor, + remembered webcam + remembered mic). The hotkey is acted on at once — the take is created immediately — but the first ACTUAL frame lands a few seconds later while the devices and the desktop capturer open (see **When recording actually begins** below), and the HUD says "Starting…" until then. See **Presets & Templates + Quick Capture** below.
- **New recording** (library header button) or the picker hotkey — opens the Record tab's **two-step composer**. The live composed canvas preview stays visible on BOTH steps (top of the view, ≥45% height); only the bottom sheet swaps. **Step 1 — Choose what to record:** the grid of capture sources (every screen and window the OS exposes), the webcam toggle and camera picker, plus the presets & templates bar (a preset IS a "what to record" choice — loading one jumps to Step 2 with the restored scene); the **Next** button stays disabled until the composition is startable (same `resolveCompositionStart` rule as Start). **Step 2 — Recording options:** every option — output size, resize mode, layout arrangement, mic/system audio, webcam shape — and the **Start recording** button; **Back** returns to Step 1 with the composed layout kept. Source enumeration is **bounded at ~8 seconds** — Electron's `desktopCapturer.getSources()` can hang on GPU-starved/RDP/locked machines — degrading to a "No capture sources available" empty state rather than an infinite spinner.

**Multi-source recording is supported.** You can compose several sources (screens/windows + a webcam) into one canvas. Selecting a webcam without an explicit layout auto-generates a picture-in-picture layout (`buildLoomDefaultLayout`); the scene is persisted as a layout sidecar beside the WebM, and ffmpeg composites the sources into a single MP4 on stop. Quality, frame rate, countdown, and delayed-start options come from the recorder settings.

### The video block's shape (rounded by default)

A recorded **screen or window** comes in with **softened corners** — a camera-less recording is rounded too.

- **Shape + Roundness** (Step 2): **Square / Rounded / Circle** plus a slider, 0–50% of the *shorter* side, so a corner looks identical at any resolution.
- Preview, editor and the exported MP4 derive the corner from **one shared value**, so the export matches what you approved.
- The shape rides in the scene, so a **preset** brings it back; **Screen Recording → Video block shape / corner radius** sets the standing default (both optional — older scenes still open).
- **Camera beside window** — one press pairs camera and window as two equal, resizable tiles; **PiP**/**Pack** puts it back, keeping its shape.

### While recording (the HUD)

Window creation and page-load completion do not count as ready. The current hidden HUD generation
must prove its controls are rendered before any recording entry point may touch media; a close,
failed load, or renderer crash invalidates that proof and starts bounded background recovery.

A floating recording HUD shows the elapsed timer and the live controls. It opens on the display where the Omniscio window is (not necessarily the primary monitor), and the pill itself is a **drag handle** — grab anywhere that isn't a button to move it if it's covering something; your placement is kept for the rest of that recording (the next recording re-centers). The HUD window is pre-warmed at recording start so it appears the moment capture begins:

- **Pause / resume** — stops capturing but keeps the session live; the MP4 resumes into the same file.
- **Stop** — finalizes. Status moves `recording` → `transcoding` → `ready` and the row appears in the library (the review window auto-opens on the transcode→ready edge). With auto-share-on-stop on, the share link is copied to your clipboard **instantly** on Stop (see [Sharing](#sharing) below).
- **Stop & share** — Stop AND force the instant-share link + clipboard copy even when auto-share-on-stop is off.
- **Settings** (gear) — foregrounds the app and opens Screen Recording settings without ending the recording.
- **Discard** — drops the segments and the row (two-step confirm; nothing is written).
- **Panic-mute** — instantly mutes mic + camera without stopping.

For a **webcam recording**, the small live **self-view** (the Loom-style floating camera pill, content-protected so it's excluded from the capture) is pre-warmed during the countdown the same way the HUD is — so it's on screen and live the instant recording starts, not a few seconds later. Its size and docked corner come from Screen Capture settings, and it's draggable.

#### When something you asked for can't be captured

The microphone, your computer's sound and the camera are each **optional**: losing one never stops a recording. It does mean the take is missing something you asked for, and the HUD says so as soon as capture is live — the same banner the warnings channel already uses, so you see it while the recording is still running and can still act on it.

- **No microphone audio was recorded** — your microphone could not be opened (disconnected, or held by another app). Screen and system audio are unaffected.
- **Your computer's sound was not recorded** — the system-audio capture could not start.
- **Your chosen camera could not be opened — this recording uses "‹name›" instead** — a camera IS being recorded, just not the one you picked.

Each device capture gets one immediate retry first, so a microphone or camera that was only briefly busy records normally and you see no message at all. What the take actually captured is also written beside the recording as `capture-report.json`, next to `layout.json` (which records what you *asked* for) — so a recording made without a microphone is never mistaken for one made with it.

### Review window

Click any `ready` recording (or stop a capture) to open the **Review Window**:

- Inline `<video>` preview of the local MP4 (shows a "not available yet" state during transcode).
- **Name** field — rename the recording inline (commits on Enter/blur; writes `source_label`). A recording you have not named stores `source_label = ''` (the column's NOT NULL default) and DISPLAYS a fallback — the AI title once **Auto-titling** produces one, otherwise its capture time. **Never persist a placeholder string here:** `source_label` means "a human named this", and writing a display default into it is exactly what silently disabled auto-titling for every recording ever made (auto-title's first guard is "manual title wins", which a placeholder satisfies). A display fallback belongs at the render site (`cardTitle`), never in the column.
- **Saved on this computer** — a **Show in folder** button and **Copy file path** button.
- **Link expires after** — a dropdown (1 day / 7 days / 30 days / Never) applied when you share.
- **Publish to Vimeo** — appears when a Vimeo access token is set in Screen Capture settings (the gear icon); uploads the MP4 with a progress bar and surfaces the Vimeo URL with a copy button.
- **Footer** — before sharing: **Discard · [Publish to Vimeo] · Share** (Ctrl+Enter shares). After sharing: **Discard · Copy link · Done** (the link is auto-copied; the same trio shows when you reopen an already-shared recording).

### The editor

Open a `ready` recording in the built-in editor via the **Edit** (pencil) action on its library card (or the review window's edit/annotate link). The editor is a single in-app view (`screen-recorder-editor`). Recordings and snips open in the **light** tier (calm resting surface: stage + chip timeline + transport + the always-visible annotation tool palette); projects open in **full**. The **Open full editor** button in the transport row escalates light → full (gated by `FULL_EDITOR_ENABLED` in `src/renderer/src/features/screen-recorder/editor/editor-mode-gate.ts`), revealing the layers panel + retime/keyframe rail. (The old separate "Annotate" toggle is gone — the tools are plain toolbar controls, not a mode.)

- **Breadcrumb** — Back to the library + an inline-editable name.
- **Compositor stage** — a live canvas preview (`CompositorStage`) that composites every layer at the playhead via the pure `renderCompositeFrame`, the same function the export bake uses (preview/export parity). Transport row: play/pause, time, seek slider, a shuttle-rate badge, and a keyboard-shortcuts button.
- **Playback keys** (stage focused): `Space` play/pause · `J`/`L` shuttle backward/forward at 1×/2×/4× · `K` pause · `←`/`→` frame-step (`Shift` = 1s) · `Home`/`End` · `?` opens the shortcut sheet documenting every editor key.
- **Multi-track chip timeline** — tracks are z-lanes ("layers, not lanes"); chips drag/trim with snapping (`Alt` bypasses snap), `S` splits at the playhead, split clips **Merge** back losslessly, Forward/Back buttons move clips across lanes, multi-select (Ctrl-click / marquee) with bulk delete, and **linked groups** move/trim/retime as one unit.
- **Keyframes** — transform and opacity animate via per-clip keyframe lanes (diamond add/remove/drag, named easings, and a custom cubic-bezier curve editor). The compositor resolves every animatable prop through the `resolve(prop, t)` seam.
- **Clip effects** — time-bound **zoom regions** (Alt-drag pans the zoom framing; mouse wheel zooms the selected source), time-bound **mute ranges**, **retime** (target-duration speed ramps), per-clip **style** (border / shadow / glow / corner radius), preset masks, and **layout presets** (one-click source arrangement).
- **Annotations** — text / arrow / rect / blur are first-class clips in the z-stack, time-bound on the timeline, and baked into exports via the timeline sidecar. Pick a tool then **click-and-drag on the stage to draw the shape out** (rubber-band; an arrow drags tail → head); a plain click places a default-size shape at the click point.
- **Cursor & click overlay** — when a recording captured cursor/click sidecars, a geometry-aware cursor glow + click rings render in preview (toggleable) and bake into exports. Event times are **media-relative**: they are anchored to the video's FIRST FRAME, not to the moment you pressed record. Those differ by the capture warm-up (on Windows, WGC can take a second or two after `getUserMedia` returns before it delivers decoded frames), so before this was anchored every ring rendered late and drifted further with each pause. One conversion owns it — see screen-recorder-contract I19.
- **Undo/redo** — doc-snapshot history (`Ctrl+Z` / `Ctrl+Shift+Z`), drag-coalesced so one gesture is one undo step.
- **Export** — bakes an edited MP4 via an offscreen export host with progress / complete / failed events and **cancel** (`export`, `cancel-export`); options cover resolution (source/1080p/720p/480p), codec (h264/vp9/av1), bitrate, and framerate.

Non-destructive edit data (trim ranges, layout, bookmarks, click events, annotations, cursor track, webcam-position track, timeline doc) persists as JSON **sidecars** beside the WebM, so reopening rebuilds the scene without touching the immutable source bytes.

### Sharing

**Share** uploads to the Omniscio Shares Firebase project and mints a public link of the form `https://shares.omniscio.com/s/<shareToken>/run`, honoring the chosen expiry. A **video** share uploads the raw MP4 as its own Storage object (`shares/<token>.mp4`) and the viewer streams it from the Range-serving `/s/<token>/video` route — capped at **500 MB**, and the upload deadline scales with file size plus one automatic retry on a transient network failure (a ~20 MB clip on a slow uplink shares reliably). A recording past the cap is **refused before anything leaves your machine** ("Video is <n> MB (cap 500 MB)") — that cap exists because the MP4 lands in the product-paid Storage bucket and is Range-streamed on every view, so an unbounded recording is a real cost/abuse vector; longer recordings are steered to the **Vimeo** path instead, which uploads to your own Vimeo account. A **snip** share inlines its PNG as a base64 data URI (10 MB cap). `screen-recorder:get-share-info` re-reads an already-published recording's live link + expiry so the review window can re-display and copy it without re-publishing. The legacy `screen-recorder:publish` channel is **deprecated** in favor of `screen-recorder:share`. `screen-recorder:reveal-recording` opens the rendered MP4 in the OS file manager (main owns the absolute path, so this sidesteps the project-scope gate the generic file-reveal channels enforce).

**Instant share link on Stop (reserve → finalize).** When auto-share-on-stop is on — or you use the HUD's **Stop & share** — the share link is RESERVED and copied to your clipboard the **instant** you Stop, before the transcode + upload finish, so you can paste it right away; the recording finishes uploading in the background and attaches to that same link. At Stop, `reserveInstantShare()` (`src/main/services/screen-recorder/screen-recorder-share-inline-ops.ts` → `src/main/services/screen-recorder/screen-recorder-share.ts`) mirror-writes the recording's own share token to claim ownership and parks a self-refreshing "still uploading" placeholder at `/s/<token>/run` (`buildProcessingViewerHtml` in `screen-recorder-viewer-html.ts` — a self-refreshing placeholder, so an early open flips to the video on its own once it lands, never a 404). The post-`ready` finalize calls `share({ reuseToken })`, which pins the real upload to that SAME token (asserting the relay echoed it back). A failed finalize tears the reserved link down (no dead link); if the reserve itself can't run (offline / signed out) it falls back to today's post-upload share + toast. There is no in-app "Processing…" pill — the instant toast is the feedback, and the content-protected HUD owns the live controls. Locked by **I18** in the `contract`.

**Third-party-PII share warning.** A share uploads the raw media **and its transcript captions** — which can contain what other people said or showed — to a public-read bucket anyone with the URL can open. Before the FIRST share on an install, a one-time ConfirmDialog discloses this (mirrors the meetings-consent pattern); acknowledging sets `screenRecorderShareWarningAcknowledged` and is never shown again. The acknowledgment is enforced fail-closed in the **main** process — `shareRecording()` refuses to mint/upload until it's set — so every path is covered (the renderer dialog, the CLI `POST /capture/:id/share`, and the default-ON auto-share-on-stop). Because auto-share-on-stop has no UI, it **HOLDS** the first time (keeps the local recording, pushes a "review before sharing" prompt instead of publishing) until you acknowledge once, then resumes its silent one-paste behavior. Choosing the **Never** expiry stays available but, once acknowledged, asks for an extra confirmation each time (the indefinite-public choice is never silent); `never`→null expiry is otherwise unchanged (the `src/main/services/share/share-reaper.ts` grace-expires old-and-unviewed never-shares separately). The interactive dialogs live in `src/renderer/src/features/screen-recorder/share-warning-gate.ts`; the main-side backstop + auto-share hold in `src/main/services/screen-recorder/screen-recorder-share.ts` / `src/main/services/screen-recorder/screen-recorder-service.ts`.

### Viewer analytics

A shared recording's review window shows **Viewer analytics** — how your public link was
watched. Because a recording is served at `/s/<token>/run` (which never hits the shell
view-counter), a recording has **no view events on its own**; the analytics come from a
lightweight **watch beacon** the public viewer page sends as it plays.

- **What's captured** — one record per play-session: how far it got (max %), seconds
  watched, and — computed **server-side** — coarse country + a **salted, pseudonymous**
  hash of the viewer's IP + user-agent (NEVER a raw IP/UA), auto-expiring after 180 days.
  Same privacy posture as [share view events](share-view-events.md); no viewer-facing notice
  (the data is hash-only). The beacon is fire-and-forget and can never affect playback.
- **The panel** shows **views over time · unique viewers · avg watch % · drop-off**, plus a
  **"notify me when watched"** toggle (opt-in, off by default — fires an OS notification the
  first time each new viewer watches). To protect small audiences, the detailed charts only
  appear once a recording has **5+ views** (below that you see just the two counts).
- **Library badge** — a shared recording's card shows a live view-count badge (an eye + the
  number) once it has been watched.
- **Where it lives** — the beacon endpoint is the `serveShareWatch` cloud function
  (`POST /s/<token>/watch`); the desktop mirrors sessions locally via the relay `watch-read`
  op into `share_watch_sessions` and aggregates with k-cohort suppression. Full invariants:
  `screen-recording-watch-analytics-contract.md`.

### Recovered recordings

If Omniscio crashes mid-recording, orphan chunks may be left on disk. On next launch the recovery loader scans `<userData>/screen-recordings/` for sessions in `recording`/`paused` state and surfaces them in the library as a **Recovered** section. Click to recover (transcode the existing chunks into a final MP4) or discard.

### Hotkeys

All six recorder hotkeys are registered globally (gated by `screenRecorderEnabled`) and are **accelerator lists** — each can hold several keys (multi-binding), matching every other global hotkey; a legacy single-key config is coerced on read, no migration. **Quick Record is rebindable in the Screen Recording settings panel (the gear icon in the Screen Recording header)** through the multi-key "add another key" pill field; the **Snip** hotkey moved to the **Screenshots** settings page with the 2026-08-08 split — it registers separately, gated on `screenshotsEnabled` (see [Screenshots](screenshot-snip-tool.md)). The other four recorder hotkeys register from their stored value with no in-panel editor. Defaults:

| Action                      | Default                    | Notes                                                                                                                                                                                                                                                                                                                                                                             |
| --------------------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Snip (region screenshot)    | `Ctrl/Cmd+Alt+S`           | The snip half — registered separately from the six recorder hotkeys and gated on `screenshotsEnabled`, not `screenRecorderEnabled` (2026-08-08 split). Opens the interactive snip — Windows: Omniscio's own capture-then-select window (no external tool); macOS: `screencapture -i`; Linux: gnome-screenshot·Spectacle·slurp+grim. See the [Snip Tool](screenshot-snip-tool.md). |
| Recorder picker             | `Ctrl/Cmd+Shift+5`         | Opens the source picker.                                                                                                                                                                                                                                                                                                                                                          |
| **Quick Record (Fastpath)** | `Ctrl/Cmd+Alt+R`           | Instant capture of your default preset (or the built-in default) — skips the picker. (Default rebound from `Ctrl+Shift+R`, which collided with browser hard-reload.)                                                                                                                                                                                                              |
| Stop                        | _(empty)_                  | Unset by default; bind your own.                                                                                                                                                                                                                                                                                                                                                  |
| Pause / resume              | `Ctrl/Cmd+Alt+P`           | Default moved off `Ctrl/Cmd+Shift+P` (2026-09-29): that is ALSO the Quick Launch **Commands** tab default, whose group registers first — so the pause key lost the slot and never bound on any install with Screen Recording on. |
| Discard & restart           | `Ctrl/Cmd+Shift+Backspace` | Drops the current take and immediately restarts with the same source.                                                                                                                                                                                                                                                                                                             |
| Panic-mute                  | `Ctrl/Cmd+Shift+0`         | Instantly mutes mic + cam without stopping.                                                                                                                                                                                                                                                                                                                                       |

The global-hotkey planner (`src/shared/global-hotkeys.ts`) registers these after the app's other global hotkeys, so any default collision loses to an established binding. Editing a hotkey re-registers it live — no restart needed.

### Library view

The Screen Recording library lists your **recordings** (videos only); snips live in the separate **[Screenshots](screenshot-snip-tool.md)** area's own library. Both areas render the same `ScreenRecorderLibraryView`, scoped to one media type via a `mediaScope` prop:

- **Grid** (default) or **list** view.
- Each card shows a thumbnail (clip poster frame or snip PNG via the `screenshot://` protocol), a type badge (Recording/Snip), duration/format, a status chip (ready / processing / shared / edited / exported / partial / error), and meta (relative time · size).
- **Inline rename** — hover a card to reveal a pencil next to the title; rename in place (Enter saves, Esc cancels) without opening the item.
- Because each area's library is scoped to one type, the old **All / Recordings / Snips** type tabs are hidden (a **Recordings ⇄ Projects** toggle appears only when the gated Screen Capture Projects feature is on); title text search, an always-visible sort (**newest / oldest / longest / largest**), and a badged **Filters** control (date / shared / edited) all apply within the scoped list.
- **New recording** and **New snip** header buttons. Clicking a clip opens the Review Window; clicking a snip opens the [snip viewer](screenshot-snip-tool.md).

### Storage & retention

A separate **Storage** panel, opened from the library toolbar rather than sitting in the capture flow. It is where recordings actually get deleted, so it is deliberately explicit about what it is about to do.

- **Usage** — how much disk the recordings store is using, split into video and image, plus the number of captures and the free space on the drive that holds them. The figure is **approximate** by default, because the database's size column undercounts; **Calculate exact** walks the store and replaces it with a measured total when you want the real number.
- **Auto-cleanup** — a policy that deletes capture files on a schedule, in one of three modes: **off** (the default — captures are kept until you delete them), **by age** (anything older than the number of days you set), or **by size** (the oldest captures, deleted once the library passes a GB limit). The settings are `screenRecorderRetentionMode`, `screenRecorderRetentionDays` (default 30) and `screenRecorderRetentionMaxGb` (default 50).
- **Live-shared captures are never auto-deleted**, in any mode — a recording that is currently shared stays until you unshare it.
- **Clean up now** — applies the policy immediately. It always **previews the exact set first**: the dialog names the count and the total size, and lists the individual titles when there are eight or fewer. Confirming **permanently deletes** those files; it cannot be undone.
- **Relocate** — moves the whole store to a different folder. It **copy-verifies before touching the source**, so a failure leaves your recordings where they were, and a failure is reported in plain language rather than as a raw filesystem error.

### Presets & Templates + Quick Capture

Two distinct saved-scene concepts, both built on the recorder's canonical `LayoutDocument`:

- **Preset** — a layout WITH everything bound: the exact sources (which screen / window / camera), their positions, scales, crop/fit, shape, and the audio devices. "Record with this preset" reproduces that exact scene with no setup, the same sources every time. A preset is essentially a saved `LayoutDocument` + a name (`src/shared/screen-recorder/screen-capture-preset.ts`: `extractPreset`, `presetStartTarget`). Persisted as `screenCapturePresets[]`; the starred one is `defaultScreenCapturePresetId`.
- **Template** — a source-less layout _skeleton_: the same geometry / roles / crop / shape, but with the sources stripped to empty slots (`src/shared/screen-recorder/screen-capture-template.ts`: `extractTemplate`, `templateSlots`, `bindTemplate`). Applying a template asks you to pick a source for each slot, then `bindTemplate` drops them into the saved geometry to produce a record-ready layout. Persisted as `screenCaptureTemplates[]`.

**In the Clip composer** (`src/renderer/src/features/screen-recorder/clip/ClipPresetTemplateBar.tsx`): **Save as preset** / **Save as template** (inline name field — no browser prompt). Each preset row offers **Record** (records the bound scene immediately), **Load** (loads it into the composer to tweak), a **★ default** toggle, and delete. Each template row offers **Apply** — which opens the per-slot source picker (`src/renderer/src/features/screen-recorder/clip/TemplateApplyPanel.tsx`) — and delete. Saving your first preset makes it the default automatically.

**Quick Capture** is zero-click: it records the **default preset's** exact scene. With no default preset set it falls back to the **built-in default** — a PiP of the last-used source when it's still available (else the primary monitor) + the remembered webcam (only when one is set) + the remembered mic — so it works before any preset is saved (no "record once manually first" requirement). The pure decision (`pickPrimaryMonitorSource` + `resolveQuickCapturePlan`) lives in `src/shared/screen-recorder/quick-capture.ts`; `screenRecorderService.quickStart()` does the IO (enumerate screen sources, read the primary display, pre-flight that the planned source is still connected, call `start`). Failures push `SCREEN_RECORDER_QUICK_START_FAILED` so hotkey/tray callers are never silent.

Triggers (all gated on `screenRecorderEnabled`): the **Start Recording** system-tray entry, the **Fastpath global hotkey** (`screenRecorderQuickRecordHotkey`, default `Ctrl/Cmd+Alt+R`), and the `screen-recorder:quick-start` IPC channel / CLI capture route.

**Start speed + cues (updated 2026-09-02).** Once Screen Recording is enabled, Omniscio keeps the recorder engine, the configured countdown, and a fresh hidden control HUD loaded in memory. The countdown is prepared again immediately after every use, and changing its duration prepares the new version before the next hotkey press. Quick Record never waits for background warm-up work: monitor capture can reuse a topology-checked screen list, while window capture uses a bounded fresh check so a closed window is never recorded by mistake. Disabled users do not pay the startup or idle-memory cost. None of the resident surfaces holds a desktop, microphone, or camera stream while idle. The countdown includes **None (start instantly)**; a **"Play a sound when recording starts"** toggle (on by default) provides an audible cue. With system-audio capture on, that chime can be faintly re-recorded at the start.

**When recording actually begins (updated 2026-09-29).** Opening the microphone/camera and spinning up the desktop capturer is a cold-start cost after the hotkey — measured 2.6–5.9 s on the development box, on top of roughly 1.4 s to reach the capture host. For that stretch nothing is being recorded, so the HUD says **"Starting…"** with no live REC dot and no running clock, and only swaps to the red dot + timer when the host reports its first real frame. The elapsed timer therefore starts where the video starts: the timer you watch and the length of the file you get now agree. The start chime rings at that same moment (once per take), so it means "you are on camera now" rather than "the take exists".

**The start has two phases, and nothing records until the hardware is confirmed.** The hotkey opens the microphone, camera and screen and **holds** them; the recorder is not built and not started until that moment. When the devices that opened are the ones you chose, the take begins immediately. When one of them is **not** — a device that is unplugged, held by another app, or whose stored id has gone stale — Omniscio does **not** quietly record with a different one. It asks: *"Nothing has been recorded yet. One of your devices did not open"*, naming the device that did open, with **Record anyway** and **Don't record**. Declining (or not answering within 30 seconds) leaves **nothing** — no take, no entry in the library, no partial file — because a take that never recorded a byte should not appear among your recordings. A missing *system-sound* capture is not asked about: that is a platform limitation rather than a substitution, so it warns and carries on.

**Your devices are checked against what is actually connected.** A saved microphone or camera id is a per-profile handle, not a permanent hardware name: it changes when the app's profile does (a data-directory move), and never comes back. A stored id that is no longer present is never requested, so a dead id costs nothing instead of two failed attempts and a retry delay on the start path. If your chosen device turns out to be gone, Omniscio falls back to the system default, **names the device it actually used**, and logs the real reason — including the failure's name and the constraint it refused, which is empty in the raw browser error. This is the path every UI entry point takes — Quick Record, the Record composer and the picker all send a composed layout.

**The exception, so the promise above is not read as universal:** a direct `SCREEN_RECORDER_START` with no layout takes the older single-source host path, which still asks for the stored device id exactly and, if it cannot open, **fails the take with an error** rather than substituting a device or falling back. It never records with hardware you did not choose — which is the property that matters — but it does not get the rescue, the device check, or the naming above.

Nothing else presents the take as recording over that window either: the library's list row shows **no status chip** until capture is live (it used to paint a red "recording" chip immediately), and **Pause is not offered** — there is nothing captured to hold, so the main process refuses the request and the button is hidden. **Stop and Discard remain**, and are the ways out of a warm-up that is taking too long. If you want a run-in before you are captured, set a countdown; with the countdown on **None** there is no 3-2-1, and the HUD's "Starting…" is the only cue that the warm-up is still running.

### Auto-titling

**On by default.** After a recording finishes, Omniscio transcribes it locally (Whisper) in the background, then generates a short **AI title** from the transcript via **Claude Haiku** and saves it to `ai_title`. The title shows as the recording's name in the library, the review window, and the public share page — but ONLY when you haven't named the recording yourself (a manual `source_label` always wins). Turn it off with **Auto-title recordings** in the Screen Recording settings (`screenRecorderAutoTitleEnabled`, default on). It is **fail-soft** (a failure just leaves the recording showing its capture-time fallback — it never affects the recording or captions), **credential-gated** (no API key → skipped), **daily-cost-capped**, and **PII-scrubbed** before the transcript reaches the model, and it never overwrites an existing title. The manual **Summarize** action still produces the fuller `ai_tldr` + `ai_chapters_json`.

**A wordless recording is skipped, not titled.** Whisper never returns an empty transcript for a capture with no speech — it narrates the audio instead (`"(upbeat music)"`, `[BLANK_AUDIO]`, ♪-wrapped lyrics). Titling from those spends a real call to describe the SOUNDTRACK rather than the recording, so `hasSpeech()` strips the bracketed / ♪-wrapped markers and requires a letter or digit to survive; no speech → no title, and the card keeps its capture-time label. Speech that merely _mentions_ music is unaffected (the marker is stripped, the narration around it still counts).

**Two guards that look redundant and are not** (`auto-title-service.ts`): the `source_label` check means "a HUMAN named this", and the `ai_title` check means "already auto-titled". Collapsing them, or feeding either a placeholder, re-breaks the feature silently — auto-title fails by doing nothing, so nothing surfaces. Pinned by `tests/unit/services/screen-recorder/auto-title-gate.test.ts`.

### Not yet (deferred)

**Recording / Project shared:**

- **Auto TL;DR + chapters on finish** — only the AI **title** auto-generates after a recording (see **Auto-titling**); `ai_tldr` / `ai_chapters_json` still come only from the manual **Summarize** action.
- Cross-lane chip **drag** gesture (lane moves use the Forward/Back buttons — the vendored ChipTrack primitive needs an upstream change for drag-across-lane).
- Audio **waveform** lane, and canvas background/padding presets.

**Projects-specific:**

- **Multi-asset bake** — export of projects with multiple video sources or image-asset compositions (single-source only today).
- **Projects card share/copy-link parity** — library cards for projects currently don't show a quick-share icon like recording cards do; parity pending.

## Related

- [Screenshot / Snip Tool](screenshot-snip-tool.md) — the image sibling; same table, same library/share surfaces.
- [Artifact sharing](artifact-sharing.md) — the same Firebase project that backs shares hosts shared session artifacts; the recording viewer reuses that hosting.
- a sandbox (see the `amc-sandbox` skill) — recommended environment for testing screen-capture UX without touching your real recordings dir.
