Skip to content

Add a first-run setup wizard - #93

Merged
oNaiPs merged 6 commits into
mainfrom
feature/onboarding
Oct 17, 2026
Merged

oNaiPs merged 6 commits into
mainfrom
feature/onboarding

Conversation

@oNaiPs

@oNaiPs oNaiPs commented Oct 17, 2026

Copy link
Copy Markdown
Owner

A small step-by-step window shown the first time dmenu-mac is opened as an app, so macOS prompts don't pop up later mid-use.

  1. Welcome + hotkey: the global shortcut recorder, a note about Spotlight's ⌘Space, and a button to System Settings → Keyboard Shortcuts.
  2. Launch at login toggle (same LaunchAtLogin.Toggle as Settings).
  3. Terminal programs: the $PATH toggle and terminal picker (backed by CommandSettingsModel). For Terminal and iTerm2, which TerminalLauncher scripts with Apple Events, a Grant Access button asks for Automation consent now via AEDeterminePermissionToAutomateTarget on a background queue, launching the terminal hidden first if needed since TCC only answers for a running target. Shows granted / denied / not determined; when denied it links to Privacy & Security → Automation.
  4. Done: key cheat sheet.

Every step is skippable; Enter continues, Esc skips (and closes on the last page). Finishing or closing the window stores onboarding.completed. Run Setup Again… in the menu bar menu reopens it.

Shown only when launched by LaunchServices (parent is launchd), not with --exit, not when reading a pipe, and never under XCTest.

Permissions: only Automation (Apple Events) is covered, because it is the only TCC-protected API the app uses (NSAppleScript in TerminalLauncher). The entitlement and NSAppleEventsUsageDescription were already in place. No Accessibility, Full Disk Access or App Management.

New code is in new files (Onboarding.swift, AutomationPermissions.swift, OnboardingView.swift); AppDelegate gets one call and one menu item.

Testing

  • OnboardingTests: gating (first launch, completed, shell, --exit, pipe, XCTest), completion persistence, step navigation, Automation logic through a mock AutomationPermissions (no real TCC), OSStatus mapping, and a check that usesAppleEvents matches TerminalLauncher's plans.
  • Full test suite passes (230 tests), swiftlint --strict is clean.
  • Launched the Debug build with the key cleared: the window appears centered, Enter/Esc move between steps, Get Started closes it and sets the key, and a relaunch doesn't show it again. The real consent prompt from Grant Access was not clicked through.

🤖 Generated with Claude Code

Walks through the hotkey (and the Spotlight conflict), launch at login, and
terminal programs, asking for Automation access to Terminal or iTerm2 up
front. Shown once when opened as an app, never from a shell, a pipe or
under XCTest; Run Setup Again in the menu bar menu reopens it.
As an accessory app, a window that slipped behind others couldn't be found
again. While Setup or Settings is open the app becomes a regular app with its
icon in the Dock and the app switcher; clicking the Dock icon brings the
window back. Closing the last one, by any means, returns to accessory and
hands focus back to the previous app. Cmd-W now closes the setup window.
@oNaiPs

oNaiPs commented Oct 17, 2026

Copy link
Copy Markdown
Owner Author

Pushed 5f81840: while Setup or Settings is open the app is a regular app (Dock icon + ⌘Tab); clicking the Dock icon brings that window back. Closing by any path (Get Started, Esc on the last page, close button, ⌘W) returns to accessory and re-activates the previously frontmost app. Policy switching is behind DockPresence with an injectable ActivationPolicyApplying, covered by DockPresenceTests (238 tests pass, swiftlint clean).

On macOS 14+ the cooperative activate() was refused after switching to a
regular app, so the window opened behind the frontmost app and never became
key. Activate with the older call, and do it again on the next run loop turn
once the new activation policy has taken effect. Dock presence tests move to
their own file.
Existing installs are recognised by settings an earlier version wrote and
the setup is marked as seen, so it only opens by itself for new installs.
The setup view edits the same command settings its model checks, and
closing Setup or Settings returns to the app that was active last rather
than the one active when the window opened.
Store onboarding.completed = false when the wizard opens on first run, so
the next launch isn't mistaken for an upgrade.
# Conflicts:
#	dmenu-mac.xcodeproj/project.pbxproj
@oNaiPs
oNaiPs merged commit f42b9e9 into main Oct 17, 2026
3 checks passed
@oNaiPs
oNaiPs deleted the feature/onboarding branch October 17, 2026 13:31
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