2 Commits
Author SHA1 Message Date
james.maesandClaude Opus 4.7 8ebdf76980 fix(hud): wrap text in wizard + banner instead of tail-truncation
Both the first-launch wizard's TCC stage and the HUD banner panel were
showing tail-truncated body text (Grant Permissions descriptions ending
in "...", Limited hotkey mode body cut at "The hotke..."). Two layered
SwiftUI layout bugs:

1. The body Text inside row HStacks (`VStack(title+body) + Spacer +
   button`) didn't have `.fixedSize(horizontal: false, vertical: true)`,
   so when the trailing button was wide ("Denied — Open System Settings")
   SwiftUI's HStack layout compressed Text and chose tail-truncation
   over multi-line wrap.

2. The wizard's outer frame was `560×440`, leaving only ~318h for
   stageBody after header(64) + dividers(2) + footer(56). The TCC stage
   with 4 permission rows needed ≥520h. The vertical squeeze cascaded
   into more aggressive Text compression and clipped the bottom Back row.

Fixes:
- WizardView: bump frame to 600×640.
- WizardStageTCCView: `.fixedSize(horizontal: false, vertical: true)` on
  description + each row's body. `.frame(maxWidth: .infinity, alignment:
  .leading)` on inner row VStacks so they receive the available width
  before the trailing button claims it. Spacer minLength bumped to 16
  for breathing room between body text and button.
- HUDBanner: same `.fixedSize` + `.frame(maxWidth:)` pair on the title
  +body VStack. The 360pt-wide banner needed it most because
  `HUDBannerPanel.positionTopTrailing` re-reads the SwiftUI fittingSize
  on host — without wrapping, the panel was sized to a single-line body
  and forced all subsequent body text to truncate.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 16:08:02 -05:00
james.maesandClaude Opus 4.7 ebefb91043 feat(01-04): build first-launch wizard + full AppDelegate launch wiring
- AnthropicKeyValidator: local sk-ant- prefix check + URLSession-injectable
  GET to api.anthropic.com/v1/models; returns typed enum, never logs key
- WizardState: @MainActor ObservableObject; D-09 stateless re-entry via
  refresh(); firstUnresolvedStage advances apiKey → tcc → hotkey → complete
- WizardStageAPIKeyView: SecureField, validator + Keychain round-trip; key
  value lives only in Keychain (SEC-01)
- WizardStageTCCView: Input Monitoring row actively probes; Mic/Camera/
  Automation rows are explainer-only (D-08/SEC-04); no permission APIs
  called from the view
- WizardStageHotkeyView: ShortcutRecorderView + CollisionDetector wiring;
  Skip leaves hotkey unbound (SHELL-02)
- WizardView: three-stage root with header dots + Back footer
- OnboardingWizardController: NSWindow hosting; first-launch .modalPanel
  + close-button disabled until API key stored (D-06)
- AppDelegate: full 8-step launch chain — logging bootstrap (S-8) →
  entitlement gate (Plan 03) → config load (NSAlert on malformed, D-19/S-6) →
  menu bar + HUD + banner install → Keychain fetch (.keychainEmpty banner
  on itemNotFound) → Input Monitoring probe (.inputMonitoringDenied banner
  on denial, SHELL-06) → HotkeyBinder init → Wizard open on first launch.
  copyStateDump writes boolean-only presence; API key VALUE never clipboarded
- JarvisApp: Settings scene stub; real NSPanels managed by AppDelegate
- Tests: 11 new (WizardState 4, AnthropicKeyValidator 3, WizardTCC 1,
  AppDelegateWiring 4) + updated AppDelegateEntitlementTests for the new
  launch chain; all 25 JarvisAppTests pass
- Re-inserted PBXCopyFilesBuildPhase for Contents/Helpers per Plan 01/03
  precedent (xcodegen strips empty copyFiles)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 14:07:04 -05:00