Skip to content

Menu bar: recording indicator, adaptive appearance, quick menu, timer, dead-code cleanup#1351

Open
r3dbars wants to merge 4 commits into
mainfrom
claude/macos-menu-bar-app-r4z57s
Open

Menu bar: recording indicator, adaptive appearance, quick menu, timer, dead-code cleanup#1351
r3dbars wants to merge 4 commits into
mainfrom
claude/macos-menu-bar-app-r4z57s

Conversation

@r3dbars

@r3dbars r3dbars commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Why

The menu bar is the app's primary surface, but it wasn't behaving like one: the status item glyph was completely static (no signal that recording was active), the popover header said "Ready" during a meeting with no elapsed time anywhere, the popover forced dark appearance regardless of the system setting, everyday states carried permanent explainer copy, and ~1,150 lines of Sources/UI/MenuBar/ were orphaned code kept alive only by contract tests that read the files.

Product Impact

  • Affects: dictation / meetings
  • Lane: activation
  • Why this matters: "am I recording?" is the one question a menu bar recording app must answer at a glance — the status item, the popover, and a right-click quick menu now all answer it. The popover itself gets quieter, native-feeling, and state-colored instead of decorated.

Preview

Menu bar redesign preview — light idle, dark idle with hover, dark recording, right-click quick menu

Code-derived mockup (.agent-review/visuals/menu-bar-premium-redesign.{html,png}) rendered from the exact MenuTokens values, row heights, and copy on this branch — not a live macOS capture (this session had no macOS host). Fonts, material blur, and icons are approximations; geometry, colors, copy, and states match the code.

What changed

Commit 1 — recording indicator, popover timer, dead-code removal

  • Status-item button shows a red record.circle while a meeting records and a red mic.fill while dictating, with matching tooltip and accessibility label, through a single presentation writer so the indicator and the Sparkle badge can't fight.
  • Meeting row's trailing slot shows the live elapsed mm:ss timer while recording (5 Hz core ticks collapsed to whole seconds; no refreshes while the popover is closed).
  • Deleted five orphaned MenuBar files, removed their contract-test assertions, trimmed dead MenuTokens, updated docs and the QA matrix pointer.

Commit 2 — adaptive appearance, quick menu, header policy, icon passthrough

  • Popover follows the system light/dark setting: forced darkAqua removed, MenuTokens colors made dynamic, CALayer-bound colors re-resolved via NSView.menuResolvedCGColor(_:) on viewDidChangeEffectiveAppearance().
  • Right-click / control-click on the status item opens a lean quick menu (Start/Stop Dictation, Start/Stop Meeting Recording, Open Home, Quit) reusing existing entry points; left-click keeps the popover.
  • Header status precedence extracted into Foundation-pure MenuBarHeaderStatusPresentation with a fast test; utility updates row uses the computed state symbol.

Commit 3 — premium polish: quieter popover, native material, motion

  • Steady states stop talking: ready-state subtitles removed ("Paste spoken text anywhere" / "Capture mic and system audio" / "Recording now") — subtitles now only carry setup/failure state, so the everyday popover is clean single-line actions.
  • "Paste Last Dictation" hides until a saved dictation exists (smoke override still forces it visible for launch automation).
  • Update callout demoted: the loud callout is reserved for restart-to-install; update-available stays in the quiet utility row with an Install affordance. Quit loses its warning tone; "Submit Feedback" gets title case (smoke expectation updated).
  • Color discipline: primary rows are monochrome; the meeting row turns red (Tone.recording) only while recording. The accent tone is gone.
  • Bug fix: the red "Recording" header from commit 2 never actually rendered — MenuBarHeaderLayoutPolicy.intrinsicHeight returned 0 for ready+quiet regardless of recording. The policy now takes isRecording and gives it a compact visible row, with new fast-test cases.
  • Native material: the content view stops painting an opaque surface + border; NSPopover's own material provides background, corner chrome, and Reduce Transparency handling. Header and home dividers removed (one divider remains above the utility section).
  • Motion: row hover eases in/out over 120ms via AccessibilityDisplayPolicy.motionDuration (Reduce Motion collapses it); presses stay instant. Status-item update badge loses its dark border, shrinks to a 7px dot.

Commit 4 — visual evidence under .agent-review/visuals/.

Deliberately not done (need visual iteration on a Mac): a custom-drawn status glyph to replace mic.and.signal.meter, and replacing NSPopover with a borderless panel. Both are design-taste calls not worth making blind from a Linux container.

How I checked it

  • scripts/dev/agent-preflight.sh
  • Selected checks from .agents/test-matrix.yml — via CI: build-and-test (build + fast tests + launch UI smoke) green through commit 3, including the updated smoke expectations (hidden idle header, gated paste row, "Submit Feedback")
  • bash build.sh --no-open — via CI (no macOS toolchain in this session)
  • bash run-tests.sh — via CI, including the new MenuBarHeaderStatusPresentation and recording-header layout-policy cases
  • bash run-integration-smoke.sh — meeting sources untouched
  • swift test — core seam untouched
  • Manual check: light + dark popover, hover fade, right-click menu, recording state end-to-end (red icon, red header row, ticking timer), update-available stays quiet, paste row hidden on a fresh account

Caveat: all build/test verification ran through CI rather than a local Mac. The popover still deserves a human visual pass in both appearances before merging — the native-material change is the one most worth eyeballing against the preview above.

Risk Review

  • Privacy / local-first behavior reviewed — no new off-device payloads; quick menu reuses the allowlisted menu_bar_action_clicked event
  • Storage path or migration impact reviewed — none
  • Public-facing copy stays concrete and matches current product scope
  • Release/update impact reviewed — update affordances preserved (badge, utility row, restart callout); only the loud pre-download callout was demoted
  • Agent PRs link the issue/workpad and stay draft until human review — marked ready at the requester's direction
  • UI changes include sanitized .agent-review/visuals/ evidence — menu-bar-premium-redesign.{html,png} (code-derived mockup; noted as such)
  • No private transcripts, audio, tokens, personal paths, or customer data are included

Notes

Launch UI smoke: header assertion unaffected (status strings stay populated even when the header view is hidden); paste-row visibility satisfied via the smoke's existing visibility override; "Submit Feedback" expectation updated in scripts/entrypoints/build.sh in the same commit. Focus-order contracts unchanged. OverlayTokens (recording HUD) deliberately stays dark.

Agent handoff

COORD_DONE: GREEN | https://github.com/r3dbars/transcripted/pull/1351 | recording indicator + adaptive appearance + quick menu + timer + quieter popover + native material + hover motion + dead-code removal + visual evidence | none | custom status glyph & borderless panel deferred (need visual iteration) | CI green (build, fast tests, launch UI smoke) + agent-preflight | human visual pass on a Mac (light/dark), then merge

🤖 Generated with Claude Code

https://claude.ai/code/session_017MdpDAQ4jVvGbiFa2VA91n

claude added 2 commits July 2, 2026 12:11
The status item glyph never changed, so a recording app gave no menu-bar
signal that capture was active. The status button now shows a red
record.circle while a meeting records and a red mic.fill while dictating,
with matching tooltip and accessibility label, through a single
presentation writer so the indicator and the update badge can't fight
over button state.

The popover now answers the same question: the header shows a red
Recording status while a meeting records, and the meeting row's trailing
slot shows the live elapsed mm:ss timer (via MeetingDurationFormatter)
instead of the start shortcut. Duration ticks arrive at 5 Hz, so the
refresh subscription collapses them to whole seconds and skips entirely
while the popover is closed.

Also removes the orphaned menubar UI kept alive only by contract tests:
MenuBarSettingsView, MenuBarShortcutsView, MenuBarModelStatusView,
MenuIconButton, and MenuOutlineButton were no longer in the live popover
hierarchy. Their contract-test assertions, now-unused MenuTokens, and
stale doc/QA-matrix pointers are cleaned up in the same change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017MdpDAQ4jVvGbiFa2VA91n
… status policy

The popover forced darkAqua at three levels (popover, panel controller,
content view) with dark-only white-alpha tokens. MenuTokens colors are
now dynamic — dark variants keep the original look, light variants
mirror them — and the forced appearances are gone so the popover follows
the system setting. Colors written into CALayers are appearance
snapshots, so views re-resolve them through NSView.menuResolvedCGColor
and re-apply from viewDidChangeEffectiveAppearance. Also drops the
now-dead SwiftUI wrappers and onboarding window tokens.

Right-clicking (or control-clicking) the status item now opens a lean
quick menu — start/stop dictation, start/stop meeting recording, open
Home, quit — reusing the same session/overlay entry points as the
popover and command menus. Plain left-click keeps the popover; the menu
is attached only for the duration of the click.

The header status line (Recording > Ready > warmup subtitle precedence)
moves into a Foundation-pure MenuBarHeaderStatusPresentation policy with
fast-test coverage, and the utility updates row now uses the
state-specific symbol the presentation helper already computed instead
of a hardcoded icon.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017MdpDAQ4jVvGbiFa2VA91n
@r3dbars r3dbars changed the title Menu bar: live recording indicator, popover meeting timer, dead-code cleanup Menu bar: recording indicator, adaptive appearance, quick menu, timer, dead-code cleanup Jul 2, 2026
@r3dbars r3dbars marked this pull request as ready for review July 2, 2026 14:14
claude added 2 commits July 2, 2026 14:48
…er motion

Subtraction first: steady states stop talking. Ready-state subtitles
("Paste spoken text anywhere", "Capture mic and system audio",
"Recording now") are gone — subtitles now only carry setup/failure
state, so the everyday popover reads as clean single-line actions. The
Paste Last Dictation row hides until a saved dictation exists instead of
advertising its own empty state (the launch-smoke override still forces
it visible for automation). The update callout is reserved for
restart-to-install; available-but-not-downloaded stays in the quiet
utility row. Quit drops its warning tone, feedback gets title case, and
the header/home dividers are gone — one divider remains above the
utility section.

Color is now reserved for state: primary rows are monochrome and the
meeting row turns red only while recording. This pass also fixes a real
bug from the recording-header change: intrinsicHeight returned 0 for
ready+quiet regardless of recording, so the red Recording header never
actually showed. MenuBarHeaderLayoutPolicy now takes isRecording and
gives the recording state a compact visible row, with new fast-test
coverage.

Material and motion: the content view stops painting its own opaque
surface so NSPopover's native material provides the background, corner
chrome, and Reduce Transparency behavior. Row hover states ease in and
out over 120ms through AccessibilityDisplayPolicy.motionDuration so
Reduce Motion collapses the fade; presses stay instant. The status-item
update badge loses its dark border and shrinks to a 7px dot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017MdpDAQ4jVvGbiFa2VA91n
Code-derived mockup of the popover states on this branch (light idle,
dark idle with hover, dark recording, right-click quick menu), rendered
from the exact MenuTokens values, row heights, and copy. Not a live
macOS capture — this session had no macOS host — so fonts, material
blur, and icons are approximations; geometry, colors, copy, and states
match the code.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017MdpDAQ4jVvGbiFa2VA91n
@r3dbars

r3dbars commented Jul 2, 2026

Copy link
Copy Markdown
Owner Author

Maestro hold review: HOLD at head 973c5ff. This is the only non-draft PR in the batch and CI is green, but it changes the primary menu-bar surface: status glyph, recording indicator, right-click/control-click quick menu, popover appearance, timer, hidden paste row, update affordance, and deletes old menu components. The attached visual is code-derived, not a live Mac capture. Smallest clear path: run a local Mac visual/UI pass in light and dark mode, verify right-click/control-click menu actions, live dictation/meeting recording state, update-ready badge coexistence, and paste-row visibility; then recheck exact head and merge if unchanged.

@r3dbars

r3dbars commented Jul 2, 2026

Copy link
Copy Markdown
Owner Author

Maestro local proof follow-up: HOLD at head 973c5ff3b6b4cfe951b95106840e672fb54bdac7.

What passed locally on this Mac:

  • python3 scripts/dev/check-build-source-lists.py
  • bash -n build.sh, bash -n scripts/entrypoints/build.sh, bash -n run-tests.sh, bash -n scripts/entrypoints/run-tests.sh
  • git diff --check origin/main...HEAD
  • bash build-deps.sh --force
  • bash run-tests.sh -> 11225 tests, 11225 passed, 0 failed
  • Direct app smoke JSON: app launched, status item exists, popover configured, onboarding completed.
  • Real visual/UI proof captured on this Mac: light popover, dark popover, control/right-click quick menu, and recording-state popover with Recording, Stop Meeting, red meeting row icon, and timer visible.

Still held because the required mapped command bash build.sh --no-open exits nonzero at the repo-owned launch smoke step:

Running launch smoke check...
Transcripted exited during launch smoke.
Smoke log:
_LSOpenURLsWithCompletionHandler() failed with error -10810.

The built/signed app can launch directly and the UI behavior looks good, but the repo-owned gate does not pass, so I did not merge. Local evidence is under /Users/redbars/Documents/Codex/2026-07-02/transcripted-pr1351-ui-proof-merge/outputs/pr1351-ui-proof/.

Smallest next action: fix or account for the LaunchServices open -n -g -F -W ... launch-smoke failure in build.sh, rerun bash build.sh --no-open, then merge with exact-head if it passes.

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