Skip to content

Add Notch island: one opt-in overlay for dictation, meetings and call prompts - #1876

Merged
r3dbars merged 9 commits into
mainfrom
claude/notch-island
Sep 28, 2026
Merged

r3dbars merged 9 commits into
mainfrom
claude/notch-island

Conversation

@r3dbars

@r3dbars r3dbars commented Sep 26, 2026 •

Copy link
Copy Markdown
Owner

What

A third Dictation window style in Settings > General: Notch island. It's one black shape that grows out of the MacBook notch and carries dictation, the meeting pill and the call-detected prompt. On a display without a notch it hangs from the top edge as a small tab and is gone when idle. Default stays Near text box, so nothing changes unless you pick it.

  • Dictation: mic + "Listening" on one side of the camera, live bars + timer on the other. Hover opens a drop-down with the target app, the mic, and Cancel / Insert now. After a paste it lingers ~2.6 s ("Pasted · 12 words") with Copy / Paste again on hover.
  • Meetings: red dot + timer, meters (or "Mic only" / "Audio unverified"). Hover shows You/Call lanes and Stop. Warnings (no audio, system audio, Bluetooth, mic boost), transcribing progress, Saved + Open, and errors all show here.
  • Call prompt: "Call" + Record. The drop-down opens by itself with Not now / Remind me soon / Record and the countdown.
  • A meeting keeps the left wing while a dictation takes the right. A call prompt waits until a dictation lands. With the drop-down open, the wings only report status, so no button shows twice.

How

The three controllers (FloatingOverlayController, MeetingOverlayController, CapturePillController) keep their state machines, timers, Esc handling and actions. In island mode they skip their own panels and push plain snapshots to NotchIslandController. It composes them with the Foundation-pure NotchIslandPresentation, sizes them with NotchIslandGeometry (notch from safeAreaInsets + the auxiliary top areas), and draws in pure AppKit (NotchIslandView, no SwiftUI hosting). NotchIslandPanel sits at .statusBar level, is excluded from screen capture (sharingType = .none), and can never become key, so clicks never pull focus from the app you're dictating into.

Motion

Built for a fast first moment. The panel is created at launch and opens at a fixed size, so the island is on screen the same frame as the key press. A continuous-corner mask then springs out of the notch in Core Animation, off the main thread, so it doesn't stutter while the mic starts. On a display without a notch it swells from a small nub at the top edge, on a softer spring. Content sits in place from the first frame and blurs in as the shape uncovers it (~200 ms). When the island resizes, the wings ride the same spring. The window never resizes mid-animation, which avoids a one-frame corner snap. Mouse monitors let clicks through everywhere except over the island. Measured before this: content fully in at ~320 ms after the key press; now fully formed at ~170 ms.

Verification

  • bash build.sh --no-open: clean
  • TZ=America/Chicago bash run-tests.sh: 18665/18665 pass. New NotchIslandPresentationTests / NotchIslandGeometryTests; the panel added to the screen-capture privacy allowlist with a behavioral suite (sharing, focus, level). That suite caught a real bug: isFloatingPanel = true reset the level under the menu bar.
  • check-source-pins --changed-only, check-build-source-lists, bash -n on the runner: pass
  • Rendered all 20 states offscreen on a 14" notch and on an external display. Also drove the real NotchIslandController through dictation → recent insert → meeting → call prompt and checked the window's level and frame.
  • Dictated with it for real on a 14" MacBook Pro: starts, stops and pastes clean.

Added: faster, clearer paste-back

Paste standoff (every overlay mode). About 1 in 3 pastes took ~110 ms longer than needed, since at least 2026-09-22. Right after ⌘V, the paste-confirmation loop asked the target over Accessibility whether the text had landed. When that question arrived while the target was mid-paste (blocked on our main thread for the lazily provided string), each side waited on the other until the AX messaging timeouts (2 × 50 ms) ran out. Now nothing is asked over Accessibility until the target has read the clipboard. The text can't have landed before that, so no outcome changes. Found with per-step timing in the confirmation wait: every slow paste was one check step of ~102 ms. On a 14" MacBook Pro, 16 of 44 dictations hit it before the fix and 0 of 20+ after. Paste p95 went from 366 to under 40 ms.

Not pasted notice (island only). When a dictation lands on the clipboard instead (no text box, or focus moved), the island shows "Not pasted", the words in quotes (2 lines), "Click where it goes, then press ⌘V.", and Dismiss / Paste. A ring around Dismiss runs down over 15 s and pauses on hover. Pressing ⌘V yourself (the words still on the clipboard) turns it into ✓ Pasted. Paste pastes into the app in front. It never runs by itself, and it sits next to the words, so a user can see whether an ambiguous paste already landed. Other overlay modes keep the plain clipboard notice.

Tests

  • New: the paste never asks over Accessibility before the clipboard is read. It fails without the fix (the unread take was asked 300k+ times).
  • New: the island labels and keys a missed paste by its words.
  • Updated: the copied-fallback pin now points at the new notice, and pins that the words are shown.
  • bash build.sh --no-open, TZ=America/Chicago bash run-tests.sh (18678/18678), bash run-slow-pasteback-smoke.sh (all pass), check-source-pins --changed-only, check-build-source-lists

Still to check by hand

  • A live meeting (hover → Stop, Mic only chip) and a real call-detected prompt. Covered by tests and the scripted run, not yet on hardware.
  • Return / Esc on the call prompt only work with the old pill (the island never takes focus). Use the buttons.

🤖 Generated with Claude Code

r3dbars and others added 9 commits September 26, 2026 15:37
… prompts

A third Dictation window style (Settings > General). The island grows out
of the MacBook notch, or hangs from the top edge of a display without one,
and carries dictation, the meeting pill and the call-detected prompt.
Hover opens a drop-down with the buttons; messages and prompts open it by
themselves. The default stays Near text box.

The three overlay controllers keep their state machines, timers and
actions. In island mode they skip their own panels and push plain
snapshots to NotchIslandController, which composes them with the
Foundation-pure NotchIslandPresentation and sizes them with
NotchIslandGeometry. The panel sits at the status-bar level, is hidden
from screen capture and never takes focus.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The island used to grow by resizing its window on the main thread, with
its content hidden until the grow finished: nothing readable for ~150 ms
after the key press and fully in only at ~320 ms.

Now the panel is built at launch and opens at a fixed size, and a
continuous-corner mask springs out of the notch in Core Animation (on a
display without a notch it swells from a small nub at the top edge, on a
softer spring). Content sits in place from the first frame and blurs in
as the shape uncovers it; when the island resizes, the wings ride the
same spring. The window never resizes mid-animation, which removes a
one-frame corner snap, and mouse monitors let clicks through everywhere
but the island itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The island stays hidden from screen sharing, recordings and screenshots by
default, like the other overlays. A new toggle under Settings > General >
Dictation window (shown while Notch island is picked) makes it capturable
for demos and screenshots. The island reads it each time it draws, so the
switch applies at once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hiding waited for Core Animation's completion before ordering the panel
out. If that callback were ever lost (sleep mid-shrink), an invisible
envelope window would linger at the top of the screen. A backstop now
runs the same guarded finish after 1.2 s, past the shrink's settling time.

Stress-tested with the real controller: 40 rapid press cycles, show/hide
within one frame, hide mid-grow, dictation over a meeting with a call
prompt and warning countdown, a two-hour timer, level floods, a display
change mid-grow and 300 random state changes: 102/102 checks (window
gone after hide, shape settles on its content, click-through outside the
island, never off screen), every update under 4 ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
startDictation ran its telemetry events and admission checks (~12 ms on
the main thread in the logs) before the overlay was asked to show. In
Notch island mode the island doesn't need the target field, so it now
goes up first and the checks follow; a refused start replaces the dot
with its message as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…clipboard

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the clipboard

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…down ring

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@r3dbars
r3dbars merged commit 240c182 into main Sep 28, 2026
8 checks passed
@r3dbars
r3dbars deleted the claude/notch-island branch September 28, 2026 00:35
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