feat(settings): add app presence modes - #112
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4ea0580f-655c-457e-9b12-dd7e8dc1dfe5
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Dock-only update installation and activation during persisted-mode restoration need correction.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (2)
What changed in this PR
Adds persisted Menu Bar Only, Dock Only, and Dock and Menu Bar presence modes with runtime activation-policy handling.
Changes:
- Added presence selection, persistence, fallback behavior, and tests.
- Updated Settings and menu-bar scene integration.
- Updated documentation, metadata, accessibility evidence, changelog, and agent guidance.
| File | Summary |
|---|---|
Tests/OpenWritrTests/AppPresenceTests.swift |
Presence behavior tests |
Sources/OpenWritr/UISnapshotRenderer.swift |
Deterministic snapshot configuration |
Sources/OpenWritr/SettingsView.swift |
Presence picker and guidance |
Sources/OpenWritr/OpenWritrApp.swift |
Presence state and activation-policy integration |
README.md |
User-facing documentation |
Info.plist |
Application metadata |
docs/self-assessment.md |
Project self-assessment |
docs/index.html |
Website description |
docs/accessibility.md |
Accessibility evidence |
CHANGELOG.md |
Release notes |
AGENTS.md |
Architecture guidance |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Surfaces reviewed: Settings (light, dark, large accessibility text), About (light, dark, large accessibility text), and overlay states — listening, transcribing, enhancing, done, error (light and dark) — all rendered snapshots, correlated with the PR diff for Info.plist, OpenWritrApp.swift, SettingsView.swift, and UISnapshotRenderer.swift.
Finding
- 🟠 HIGH —
Sources/OpenWritr/OpenWritrApp.swift,menuBarInsertionbinding (new in this PR)
Impact: The newMenuBarExtra(isInserted: menuBarInsertion)binding only handles two of the possible transitions:When the app is in the default "Menu Bar Only" mode (set: { isInserted in guard isInserted != viewModel.appPresence.showsMenuBar else { return } if isInserted { viewModel.setAppPresence(viewModel.appPresence.showsDock ? .dockAndMenuBar : .menuBarOnly) } else if viewModel.appPresence.showsDock { viewModel.setAppPresence(.dockOnly) } }showsDock == false) and the user removes the icon with the standard macOS Command-drag-out-of-menu-bar gesture, AppKit setsisInserted = false, but theelse ifbranch does nothing becauseshowsDockis alreadyfalse.appPresencestays.menuBarOnly, the activation policy stays.accessory(no Dock icon), and the menu bar icon disappears — leaving the app with zero reachable UI surfaces (no menu, no Settings, no Quit) until force-quit via Activity Monitor. This is a regression introduced by this PR's new presence-switching logic and violates the requirement that windows/menus reliably remain reachable for anLSUIElementapp.
Correction: In theisInserted == falsebranch, also handle the!showsDockcase (e.g., fall back to.dockOnlyor otherwise ensure a Dock icon appears) whenever the menu bar icon is removed while there is no Dock presence, so the user always retains at least one way to reach the app.
No other high-confidence, actionable HIG defects were found. The Settings "App" section's new presence Picker and explanatory caption render correctly and legibly across light, dark, and the larger accessibility-text variant (the window grows proportionally with no clipping or overlap), and the About and overlay surfaces are unaffected by this PR's changes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4ea0580f-655c-457e-9b12-dd7e8dc1dfe5


Summary
NSApplication.ActivationPolicythrough a testable, fail-closed adapter owned byAppViewModelMenuBarExtrainsertion to the selected mode and initialize independently of menu-bar visibilityBackground-only mode is intentionally excluded per maintainer decision so OpenWritr always retains a persistent, discoverable UI entry point.
Closes #80
Validation
swift build -c release -Xswiftc -warnings-as-errorsswiftlint lint --strictswift test— 42 tests passedswift test --filter AppPresenceTests— 5 tests passed after the final documentation/test refinementbash scripts/build-app.sh— signed app bundle built and signature verifiedThe built app was not opened for the final visible runtime mode-switch check because opening newly built software requires separate interactive approval, which was unavailable. The activation-policy boundary and persistence behavior are covered by injected unit tests.
Risk
Agent disclosure
Generated with GitHub Copilot CLI.