Phase A3 of AUDIT-AND-FIX-PLAN.md. Replaces the SwiftUI `Settings { Text("Settings — coming soon.") }` placeholder and the menu-bar Settings… → wizard stopgap (commits c7242ed + c75ea9b) with a real `SettingsView` hosted in an `NSWindow`.
Architecture mirrors `OnboardingWizardController`:
Setup… → `OnboardingWizardController` (sequential first-run flow, terminal Save)
Settings… → `SettingsWindowController` (freeform panel, every row independently editable)
Both surfaces share the same `WizardState`, so canonical truth lives in
one place:
• API key → Keychain (`Keychain.set(.anthropic, …)`)
• Input Monitoring → IOHIDCheckAccess (queried on each refresh)
• Global hotkey → UserDefaults `Jarvis.hotkey`
Edits in either surface write through to those stores, so re-opening the
other surface reflects the change immediately. Window discipline matches
the wizard: a single reusable `NSWindow`; clicking Settings… repeatedly
brings the existing window forward and `state.refresh()`-es rather than
stacking new windows.
Hotkey-edit hot-reload: `SettingsView.onHotkeyChanged` flows to
`AppDelegate.openSettings`'s closure, which calls `bindHotkeyFromWizard`
(reused from the wizard path). The new shortcut takes effect immediately
without a relaunch.
Files added:
• App/Settings/SettingsView.swift (was already in tree as untracked WIP;
296 LOC, three sections: API key / Permissions / Hotkey)
• App/Settings/SettingsWindowController.swift (new, 70 LOC; window
lifecycle + dependency injection)
Files modified:
• App/AppDelegate.swift — `settingsWindowController` property,
instantiation alongside `wizardController`, new `openSettings()`
private helper, menu wiring change
(`settingsAction: { ... openSettings() }` instead of `openWizard`).
• App/JarvisApp.swift — replaced the "coming soon" Text body of the
SwiftUI `Settings` scene with `EmptyView()`. The scene exists only
to satisfy the `App` protocol's Scene requirement; with
LSUIElement=YES the auto-generated app-menu Settings… is never
user-visible. AppDelegate manages the real Settings panel via
`SettingsWindowController`.
• App/MenuBar/MenuBarContextMenu.swift — comment update describing
the new Settings… semantics (no longer points at the wizard).
• Jarvis.xcodeproj/project.pbxproj — added Settings/ PBXGroup with
the two new files, PBXBuildFile entries for both, and group
reference under the App PBXGroup's children. Sources phase
references both build files. UUIDs generated via `uuidgen`.
Verification:
• `scripts/check-app-builds.sh`: PASS — App target compiles cleanly
with the new Settings/ group and new files.
• SwiftUI/AppKit hosting: `NSHostingController(rootView: SettingsView)`
matches the existing wizard host pattern.
• No new test regressions expected (no tests touch Settings yet —
test coverage is a Phase D-Phase-3 / Phase E line item).
Refs: .planning/AUDIT-AND-FIX-PLAN.md Phase A3, Phase C2 ("resolved by
Phase A3"). Closes the C2 finding; F-A1-01 (HUD rings static) and
F-A2-01 (stale `JarvisBusWorld` test) remain open in
.planning/AUDIT-FINDINGS.md.
19 lines
752 B
Swift
19 lines
752 B
Swift
import SwiftUI
|
|
|
|
@main
|
|
struct JarvisApp: App {
|
|
@NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
|
|
|
|
var body: some Scene {
|
|
// LSUIElement=YES keeps us out of the Dock; we don't declare a
|
|
// WindowGroup here — the AppDelegate manages NSPanels + NSWindows
|
|
// directly, including the real Settings panel
|
|
// (`SettingsWindowController` invoked via the menu-bar Settings…
|
|
// item). The SwiftUI `Settings` scene below exists only to satisfy
|
|
// the `App` protocol's Scene requirement; with LSUIElement=YES the
|
|
// auto-generated app-menu Settings… item is never user-visible, so
|
|
// an EmptyView body is the correct no-op.
|
|
Settings { EmptyView() }
|
|
}
|
|
}
|