@lattices/cli 0.4.10 → 0.4.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +13 -13
- package/{app → apps/mac}/Lattices.app/Contents/Info.plist +10 -2
- package/{app → apps/mac}/Lattices.app/Contents/MacOS/Lattices +0 -0
- package/{app → apps/mac}/Package.swift +2 -1
- package/apps/mac/Resources/Pets/assistant-spark/pet.json +62 -0
- package/apps/mac/Resources/Pets/assistant-spark/spritesheet.webp +0 -0
- package/apps/mac/Resources/Pets/scout-ranger/pet.json +6 -0
- package/apps/mac/Resources/Pets/scout-ranger/spritesheet.webp +0 -0
- package/apps/mac/Sources/AppShell/AppActivationCoordinator.swift +27 -0
- package/apps/mac/Sources/AppShell/AppDelegate.swift +189 -0
- package/apps/mac/Sources/AppShell/AppServicesBootstrap.swift +25 -0
- package/{app → apps/mac}/Sources/AppShell/AppShellView.swift +18 -3
- package/{app → apps/mac}/Sources/AppShell/AppUpdater.swift +4 -3
- package/apps/mac/Sources/AppShell/HotkeyBootstrap.swift +87 -0
- package/{app → apps/mac}/Sources/AppShell/LatticesRuntime.swift +43 -0
- package/{app → apps/mac}/Sources/AppShell/MainView.swift +116 -63
- package/apps/mac/Sources/AppShell/MenuBarController.swift +177 -0
- package/{app → apps/mac}/Sources/AppShell/OnboardingView.swift +72 -60
- package/apps/mac/Sources/AppShell/PermissionsAssistantView.swift +366 -0
- package/apps/mac/Sources/AppShell/PermissionsAssistantWindow.swift +70 -0
- package/{app → apps/mac}/Sources/AppShell/Preferences.swift +37 -2
- package/{app → apps/mac}/Sources/AppShell/SettingsView.swift +815 -156
- package/{app → apps/mac}/Sources/AppShell/SettingsWindow.swift +10 -0
- package/apps/mac/Sources/AppShell/WorkspaceInspectorPresenter.swift +13 -0
- package/{app → apps/mac}/Sources/Core/Actions/HotkeyStore.swift +6 -1
- package/{app → apps/mac}/Sources/Core/Actions/IntentEngine.swift +2 -0
- package/{app → apps/mac}/Sources/Core/Daemon/DaemonServer.swift +5 -0
- package/{app → apps/mac}/Sources/Core/Daemon/LatticesApi.swift +365 -0
- package/{app → apps/mac}/Sources/Core/Desktop/OcrModel.swift +17 -13
- package/apps/mac/Sources/Core/Desktop/WindowCapture.swift +33 -0
- package/{app → apps/mac}/Sources/Core/Desktop/WindowDragSnapController.swift +18 -217
- package/{app → apps/mac}/Sources/Core/Desktop/WindowPreviewStore.swift +4 -5
- package/{app → apps/mac}/Sources/Core/Desktop/WindowTiler.swift +19 -13
- package/apps/mac/Sources/Core/Input/EventTapBreaker.swift +124 -0
- package/apps/mac/Sources/Core/Input/EventTapThread.swift +54 -0
- package/apps/mac/Sources/Core/Input/InputCaptureResetCenter.swift +20 -0
- package/apps/mac/Sources/Core/Input/KeyboardRemapController.swift +335 -0
- package/apps/mac/Sources/Core/Input/KeyboardRemapStore.swift +141 -0
- package/{app → apps/mac}/Sources/Core/Input/MouseGestureConfig.swift +155 -20
- package/apps/mac/Sources/Core/Input/MouseGestureController.swift +2271 -0
- package/apps/mac/Sources/Core/Input/MouseShortcutStore.swift +170 -0
- package/apps/mac/Sources/Core/Input/SecureEventInputMonitor.swift +39 -0
- package/apps/mac/Sources/Core/Input/ShapeRecognizer.swift +624 -0
- package/apps/mac/Sources/Core/Input/TapBudgetMeter.swift +56 -0
- package/{app → apps/mac}/Sources/Core/Overlays/ScreenMap/ScreenMapState.swift +8 -8
- package/apps/mac/Sources/Core/Overlays/ScreenOverlayCanvasController.swift +1264 -0
- package/{app → apps/mac}/Sources/Core/Overlays/Voice/VoiceCommandWindow.swift +11 -23
- package/{app → apps/mac}/Sources/Core/Pi/PiChatDock.swift +90 -43
- package/{app → apps/mac}/Sources/Core/Pi/PiChatSession.swift +676 -43
- package/{app → apps/mac}/Sources/Core/Pi/PiProviderSetupCallout.swift +5 -5
- package/{app → apps/mac}/Sources/Core/Pi/PiWorkspaceView.swift +93 -44
- package/apps/mac/Sources/Core/System/Capability.swift +79 -0
- package/{app → apps/mac}/Sources/Core/System/PermissionChecker.swift +43 -8
- package/{app → apps/mac}/Sources/Core/Voice/AudioProvider.swift +225 -56
- package/bin/handsoff-infer.ts +14 -5
- package/bin/handsoff-worker.ts +11 -7
- package/bin/infer.ts +406 -0
- package/bin/lattices-app.ts +57 -7
- package/bin/lattices-dev +40 -1
- package/bin/lattices.ts +1 -1
- package/docs/agent-execution-plan.md +9 -9
- package/docs/api.md +119 -0
- package/docs/app.md +1 -0
- package/docs/companion-deck.md +1 -1
- package/docs/gesture-customization-proposal.md +520 -0
- package/docs/mouse-gestures.md +79 -0
- package/docs/overview.md +2 -2
- package/docs/presentation-execution-review.md +9 -9
- package/docs/proposals/LAT-001-gesture-visual-customization.md +522 -0
- package/docs/proposals/LAT-002-shared-overlay-canvas.md +353 -0
- package/docs/proposals/LAT-003-menu-bar-controller-architecture.md +291 -0
- package/docs/proposals/LAT-004-interactive-overlay-actors.md +534 -0
- package/docs/reference/dewey.config.ts +74 -0
- package/docs/reference/install-agent.md +79 -0
- package/docs/repo-structure.md +100 -0
- package/docs/voice-error-model.md +7 -7
- package/docs/voice.md +18 -0
- package/package.json +23 -13
- package/swift/Package.swift +20 -0
- package/swift/Sources/DeckKit/DeckAction.swift +51 -0
- package/swift/Sources/DeckKit/DeckBridgeSecurity.swift +152 -0
- package/swift/Sources/DeckKit/DeckCockpit.swift +82 -0
- package/swift/Sources/DeckKit/DeckHost.swift +7 -0
- package/swift/Sources/DeckKit/DeckManifest.swift +145 -0
- package/swift/Sources/DeckKit/DeckRuntimeSnapshot.swift +533 -0
- package/swift/Sources/DeckKit/DeckTrackpad.swift +63 -0
- package/swift/Sources/DeckKit/DeckValue.swift +93 -0
- package/swift/Sources/DeckKit/DeckVoiceError.swift +88 -0
- package/swift/Tests/DeckKitTests/DeckKitTests.swift +286 -0
- package/app/Sources/AppShell/AppDelegate.swift +0 -408
- package/app/Sources/Core/Input/KeyboardRemapController.swift +0 -184
- package/app/Sources/Core/Input/KeyboardRemapStore.swift +0 -84
- package/app/Sources/Core/Input/MouseGestureController.swift +0 -1203
- package/app/Sources/Core/Input/MouseShortcutStore.swift +0 -107
- /package/{app → apps/mac}/Info.plist +0 -0
- /package/{app → apps/mac}/Lattices.app/Contents/Resources/AppIcon.icns +0 -0
- /package/{app → apps/mac}/Lattices.app/Contents/Resources/tap.wav +0 -0
- /package/{app → apps/mac}/Lattices.app/Contents/_CodeSignature/CodeResources +0 -0
- /package/{app → apps/mac}/Lattices.entitlements +0 -0
- /package/{app → apps/mac}/Resources/tap.wav +0 -0
- /package/{app → apps/mac}/Sources/AppShell/App.swift +0 -0
- /package/{app → apps/mac}/Sources/AppShell/CliActionLauncher.swift +0 -0
- /package/{app → apps/mac}/Sources/AppShell/HomeDashboardView.swift +0 -0
- /package/{app → apps/mac}/Sources/AppShell/KeyRecorderView.swift +0 -0
- /package/{app → apps/mac}/Sources/AppShell/MainWindow.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Actions/HotkeyManager.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Actions/IntentSchema.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Actions/Intents/CreateLayerIntent.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Actions/Intents/DistributeIntent.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Actions/Intents/FocusIntent.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Actions/Intents/HelpIntent.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Actions/Intents/KillIntent.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Actions/Intents/LatticeIntent.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Actions/Intents/LaunchIntent.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Actions/Intents/ListSessionsIntent.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Actions/Intents/ListWindowsIntent.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Actions/Intents/ScanIntent.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Actions/Intents/SearchIntent.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Actions/Intents/SwitchLayerIntent.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Actions/Intents/TileIntent.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Actions/PaletteCommand.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Actions/VoiceIntentResolver.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Companion/CompanionActivityLog.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Companion/CompanionKeyboardController.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Companion/LatticesCompanionBridgeServer.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Companion/LatticesCompanionCockpit.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Companion/LatticesCompanionSecurityCoordinator.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Companion/LatticesCompanionTrackpadController.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Companion/LatticesDeckHost.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Daemon/DaemonProtocol.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Desktop/AccessibilityTextExtractor.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Desktop/AppTypeClassifier.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Desktop/DesktopModel.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Desktop/DesktopModelTypes.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Desktop/InventoryManager.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Desktop/InventoryPath.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Desktop/MouseFinder.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Desktop/OcrStore.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Desktop/PlacementSpec.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Desktop/SessionWindowLocator.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Desktop/TilePickerView.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Desktop/WindowPreviewCard.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Desktop/WindowSelectionStore.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Input/KeyboardRemapConfig.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Input/MouseInputDeviceStore.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Input/MouseInputEventViewer.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/AppWindowShell.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/CommandMode/CommandModeState.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/CommandMode/CommandModeView.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/CommandMode/CommandModeWindow.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/CommandPalette/CommandPaletteView.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/CommandPalette/CommandPaletteWindow.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/HUD/CheatSheetHUD.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/HUD/HUDBottomBar.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/HUD/HUDController.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/HUD/HUDLeftBar.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/HUD/HUDMinimap.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/HUD/HUDRightBar.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/HUD/HUDState.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/HUD/HUDTopBar.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/HUD/LauncherHUD.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/HUD/LayerBezel.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/OmniSearch/OmniSearchState.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/OmniSearch/OmniSearchView.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/OmniSearch/OmniSearchWindow.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/OverlayPanelShell.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/ScreenMap/ScreenMapView.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Overlays/ScreenMap/ScreenMapWindowController.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Pi/PiAuthNextStepCard.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Pi/PiAuthPromptCard.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Pi/PiInstallCallout.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/System/DiagnosticLog.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/System/EventBus.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/System/ProcessModel.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/System/ProcessQuery.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/System/SystemTelemetryMonitor.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Voice/AdvisorLearningStore.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Voice/AgentSession.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Voice/HandsOffSession.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Voice/VoiceChatView.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Voice/VoxClient.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Workspace/Project.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Workspace/ProjectScanner.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Workspace/SessionLayerStore.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Workspace/SessionManager.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Workspace/Terminal/Terminal.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Workspace/Terminal/TerminalQuery.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Workspace/Terminal/TerminalSynthesizer.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Workspace/Tmux/TmuxModel.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Workspace/Tmux/TmuxQuery.swift +0 -0
- /package/{app → apps/mac}/Sources/Core/Workspace/WorkspaceManager.swift +0 -0
- /package/{app → apps/mac}/Sources/UI/ActionRow.swift +0 -0
- /package/{app → apps/mac}/Sources/UI/OrphanRow.swift +0 -0
- /package/{app → apps/mac}/Sources/UI/ProjectRow.swift +0 -0
- /package/{app → apps/mac}/Sources/UI/TabGroupRow.swift +0 -0
- /package/{app → apps/mac}/Sources/UI/Theme.swift +0 -0
- /package/{app → apps/mac}/Tests/StageDragTests.swift +0 -0
- /package/{app → apps/mac}/Tests/StageJoinTests.swift +0 -0
- /package/{app → apps/mac}/Tests/StageManagerTests.swift +0 -0
- /package/{app → apps/mac}/Tests/StageTileTests.swift +0 -0
|
@@ -14,7 +14,6 @@ struct MainView: View {
|
|
|
14
14
|
@StateObject private var inventory = InventoryManager.shared
|
|
15
15
|
@State private var searchText = ""
|
|
16
16
|
@State private var hasCheckedSetup = false
|
|
17
|
-
@State private var bannerDismissed = false
|
|
18
17
|
@State private var tmuxBannerDismissed = false
|
|
19
18
|
@ObservedObject private var tmuxModel = TmuxModel.shared
|
|
20
19
|
@State private var orphanSectionCollapsed = true
|
|
@@ -93,10 +92,13 @@ struct MainView: View {
|
|
|
93
92
|
permChecker.check()
|
|
94
93
|
DiagnosticLog.shared.finish(tPerm)
|
|
95
94
|
|
|
96
|
-
bannerDismissed = false
|
|
97
95
|
DiagnosticLog.shared.finish(tTotal)
|
|
98
96
|
}
|
|
99
97
|
|
|
98
|
+
private var visiblyMissingCapabilities: [Capability] {
|
|
99
|
+
Capability.allCases.filter { !$0.isGranted && !prefs.isCapabilityDismissed($0.rawValue) }
|
|
100
|
+
}
|
|
101
|
+
|
|
100
102
|
private var mainContent: some View {
|
|
101
103
|
VStack(spacing: 0) {
|
|
102
104
|
if layout == .popover {
|
|
@@ -104,23 +106,24 @@ struct MainView: View {
|
|
|
104
106
|
Text("Lattices")
|
|
105
107
|
.font(Typo.mono(14))
|
|
106
108
|
.foregroundColor(Palette.text)
|
|
109
|
+
buildChannelBadge
|
|
107
110
|
|
|
108
111
|
Spacer()
|
|
109
112
|
|
|
110
113
|
headerButton(icon: "house") {
|
|
111
|
-
|
|
114
|
+
MenuBarController.shared.dismissPopover()
|
|
112
115
|
ScreenMapWindowController.shared.showPage(.home)
|
|
113
116
|
}
|
|
114
117
|
headerButton(icon: "rectangle.3.group") {
|
|
115
|
-
|
|
118
|
+
MenuBarController.shared.dismissPopover()
|
|
116
119
|
ScreenMapWindowController.shared.showPage(.screenMap)
|
|
117
120
|
}
|
|
118
121
|
headerButton(icon: "magnifyingglass") {
|
|
119
|
-
|
|
122
|
+
MenuBarController.shared.dismissPopover()
|
|
120
123
|
ScreenMapWindowController.shared.showPage(.desktopInventory)
|
|
121
124
|
}
|
|
122
125
|
headerButton(icon: "command") {
|
|
123
|
-
|
|
126
|
+
MenuBarController.shared.dismissPopover()
|
|
124
127
|
CommandPaletteWindow.shared.toggle()
|
|
125
128
|
}
|
|
126
129
|
headerButton(icon: "arrow.clockwise") { scanner.scan(); inventory.refresh() }
|
|
@@ -157,8 +160,8 @@ struct MainView: View {
|
|
|
157
160
|
.padding(.bottom, 10)
|
|
158
161
|
}
|
|
159
162
|
|
|
160
|
-
// Permission banner
|
|
161
|
-
if !
|
|
163
|
+
// Permission banner — only when something is missing AND not snoozed
|
|
164
|
+
if !visiblyMissingCapabilities.isEmpty {
|
|
162
165
|
permissionBanner
|
|
163
166
|
}
|
|
164
167
|
|
|
@@ -401,12 +404,20 @@ struct MainView: View {
|
|
|
401
404
|
SettingsWindowController.shared.show()
|
|
402
405
|
}
|
|
403
406
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
407
|
+
if !permChecker.allGranted {
|
|
408
|
+
Button {
|
|
409
|
+
PermissionsAssistantWindowController.shared.show()
|
|
410
|
+
} label: {
|
|
411
|
+
HStack(spacing: 4) {
|
|
412
|
+
Circle()
|
|
413
|
+
.fill(Palette.detach)
|
|
414
|
+
.frame(width: 5, height: 5)
|
|
415
|
+
Text("Permissions")
|
|
416
|
+
.font(Typo.mono(9))
|
|
417
|
+
.foregroundColor(Palette.textMuted)
|
|
418
|
+
}
|
|
409
419
|
}
|
|
420
|
+
.buttonStyle(.plain)
|
|
410
421
|
}
|
|
411
422
|
|
|
412
423
|
Spacer()
|
|
@@ -502,33 +513,61 @@ struct MainView: View {
|
|
|
502
513
|
// MARK: - Permission banner
|
|
503
514
|
|
|
504
515
|
private var permissionBanner: some View {
|
|
505
|
-
|
|
516
|
+
let missing = visiblyMissingCapabilities
|
|
517
|
+
|
|
518
|
+
return VStack(alignment: .leading, spacing: 8) {
|
|
506
519
|
HStack {
|
|
507
|
-
Image(systemName: "
|
|
520
|
+
Image(systemName: "sparkles")
|
|
508
521
|
.font(.system(size: 10))
|
|
509
522
|
.foregroundColor(Palette.detach)
|
|
510
|
-
Text("
|
|
523
|
+
Text("OPTIONAL CAPABILITIES")
|
|
511
524
|
.font(Typo.monoBold(10))
|
|
512
525
|
.foregroundColor(Palette.detach)
|
|
513
526
|
Spacer()
|
|
514
|
-
Button {
|
|
515
|
-
|
|
516
|
-
|
|
527
|
+
Button {
|
|
528
|
+
for cap in missing { prefs.dismissCapability(cap.rawValue) }
|
|
529
|
+
} label: {
|
|
530
|
+
Text("Maybe later")
|
|
531
|
+
.font(Typo.mono(9))
|
|
517
532
|
.foregroundColor(Palette.textMuted)
|
|
518
533
|
}
|
|
519
534
|
.buttonStyle(.plain)
|
|
520
535
|
}
|
|
521
536
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
537
|
+
Text(bannerSummary(missing))
|
|
538
|
+
.font(Typo.mono(10))
|
|
539
|
+
.foregroundColor(Palette.text)
|
|
540
|
+
.fixedSize(horizontal: false, vertical: true)
|
|
541
|
+
|
|
542
|
+
HStack(spacing: 8) {
|
|
543
|
+
ForEach(missing) { cap in
|
|
544
|
+
capabilityChip(cap)
|
|
545
|
+
}
|
|
546
|
+
Spacer(minLength: 0)
|
|
527
547
|
}
|
|
528
548
|
|
|
529
|
-
|
|
530
|
-
.
|
|
531
|
-
|
|
549
|
+
Button {
|
|
550
|
+
PermissionsAssistantWindowController.shared.show(focus: missing.first)
|
|
551
|
+
} label: {
|
|
552
|
+
HStack(spacing: 6) {
|
|
553
|
+
Image(systemName: "slider.horizontal.3")
|
|
554
|
+
.font(.system(size: 10, weight: .semibold))
|
|
555
|
+
Text("Open Permissions Assistant")
|
|
556
|
+
.font(Typo.monoBold(10))
|
|
557
|
+
}
|
|
558
|
+
.foregroundColor(.white)
|
|
559
|
+
.padding(.horizontal, 10)
|
|
560
|
+
.padding(.vertical, 6)
|
|
561
|
+
.background(
|
|
562
|
+
RoundedRectangle(cornerRadius: 4)
|
|
563
|
+
.fill(Palette.detach.opacity(0.18))
|
|
564
|
+
.overlay(
|
|
565
|
+
RoundedRectangle(cornerRadius: 4)
|
|
566
|
+
.strokeBorder(Palette.detach.opacity(0.45), lineWidth: 0.5)
|
|
567
|
+
)
|
|
568
|
+
)
|
|
569
|
+
}
|
|
570
|
+
.buttonStyle(.plain)
|
|
532
571
|
}
|
|
533
572
|
.padding(12)
|
|
534
573
|
.background(
|
|
@@ -543,6 +582,56 @@ struct MainView: View {
|
|
|
543
582
|
.padding(.bottom, 10)
|
|
544
583
|
}
|
|
545
584
|
|
|
585
|
+
private func bannerSummary(_ missing: [Capability]) -> String {
|
|
586
|
+
switch missing.count {
|
|
587
|
+
case 0: return ""
|
|
588
|
+
case 1: return "\(missing[0].title) is off. Lattices works without it."
|
|
589
|
+
default: return "\(missing.count) capabilities are off. Turn on whichever you want; the rest of the app works without them."
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
private func capabilityChip(_ cap: Capability) -> some View {
|
|
594
|
+
Button {
|
|
595
|
+
PermissionsAssistantWindowController.shared.show(focus: cap)
|
|
596
|
+
} label: {
|
|
597
|
+
HStack(spacing: 5) {
|
|
598
|
+
Image(systemName: cap.iconName)
|
|
599
|
+
.font(.system(size: 9, weight: .semibold))
|
|
600
|
+
Text(cap.title)
|
|
601
|
+
.font(Typo.mono(9))
|
|
602
|
+
}
|
|
603
|
+
.foregroundColor(Palette.textDim)
|
|
604
|
+
.padding(.horizontal, 7)
|
|
605
|
+
.padding(.vertical, 3)
|
|
606
|
+
.background(
|
|
607
|
+
Capsule()
|
|
608
|
+
.fill(Palette.surface)
|
|
609
|
+
.overlay(
|
|
610
|
+
Capsule().strokeBorder(Palette.border, lineWidth: 0.5)
|
|
611
|
+
)
|
|
612
|
+
)
|
|
613
|
+
}
|
|
614
|
+
.buttonStyle(.plain)
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
private var buildChannelBadge: some View {
|
|
618
|
+
let tint = LatticesRuntime.isDevBuild ? Palette.detach : Palette.running
|
|
619
|
+
|
|
620
|
+
return Text(LatticesRuntime.buildChannelLabel)
|
|
621
|
+
.font(Typo.monoBold(9))
|
|
622
|
+
.foregroundColor(tint)
|
|
623
|
+
.padding(.horizontal, 6)
|
|
624
|
+
.padding(.vertical, 3)
|
|
625
|
+
.background(
|
|
626
|
+
Capsule()
|
|
627
|
+
.fill(tint.opacity(0.12))
|
|
628
|
+
.overlay(
|
|
629
|
+
Capsule()
|
|
630
|
+
.strokeBorder(tint.opacity(0.28), lineWidth: 0.5)
|
|
631
|
+
)
|
|
632
|
+
)
|
|
633
|
+
}
|
|
634
|
+
|
|
546
635
|
// MARK: - tmux banner
|
|
547
636
|
|
|
548
637
|
private var tmuxBanner: some View {
|
|
@@ -607,42 +696,6 @@ struct MainView: View {
|
|
|
607
696
|
.padding(.bottom, 10)
|
|
608
697
|
}
|
|
609
698
|
|
|
610
|
-
private func permissionRow(_ name: String, granted: Bool, open: @escaping () -> Void) -> some View {
|
|
611
|
-
Button(action: { if !granted { open() } }) {
|
|
612
|
-
HStack(spacing: 6) {
|
|
613
|
-
Image(systemName: granted ? "checkmark.circle.fill" : "circle")
|
|
614
|
-
.font(.system(size: 10))
|
|
615
|
-
.foregroundColor(granted ? Palette.running : Palette.detach)
|
|
616
|
-
Text(name)
|
|
617
|
-
.font(Typo.mono(10))
|
|
618
|
-
.foregroundColor(Palette.text)
|
|
619
|
-
Spacer()
|
|
620
|
-
if granted {
|
|
621
|
-
Text("granted")
|
|
622
|
-
.font(Typo.mono(9))
|
|
623
|
-
.foregroundColor(Palette.running)
|
|
624
|
-
} else {
|
|
625
|
-
HStack(spacing: 4) {
|
|
626
|
-
Text("not set")
|
|
627
|
-
.font(Typo.mono(9))
|
|
628
|
-
.foregroundColor(Palette.detach)
|
|
629
|
-
Image(systemName: "arrow.up.forward.square")
|
|
630
|
-
.font(.system(size: 9))
|
|
631
|
-
.foregroundColor(Palette.detach)
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
.padding(.vertical, 4)
|
|
636
|
-
.padding(.horizontal, 8)
|
|
637
|
-
.background(
|
|
638
|
-
RoundedRectangle(cornerRadius: 4)
|
|
639
|
-
.fill(granted ? Color.clear : Palette.detach.opacity(0.06))
|
|
640
|
-
)
|
|
641
|
-
}
|
|
642
|
-
.buttonStyle(.plain)
|
|
643
|
-
.disabled(granted)
|
|
644
|
-
}
|
|
645
|
-
|
|
646
699
|
// MARK: - Helpers
|
|
647
700
|
|
|
648
701
|
private func headerButton(icon: String, action: @escaping () -> Void) -> some View {
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import SwiftUI
|
|
3
|
+
|
|
4
|
+
final class MenuBarController: NSObject, NSPopoverDelegate {
|
|
5
|
+
static let shared = MenuBarController()
|
|
6
|
+
|
|
7
|
+
private var statusItem: NSStatusItem?
|
|
8
|
+
private var popover: NSPopover?
|
|
9
|
+
private var contextMenu: NSMenu?
|
|
10
|
+
|
|
11
|
+
var isPopoverShown: Bool {
|
|
12
|
+
popover?.isShown == true
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
private override init() {
|
|
16
|
+
super.init()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
func start() {
|
|
20
|
+
guard statusItem == nil else { return }
|
|
21
|
+
|
|
22
|
+
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
|
|
23
|
+
if let button = statusItem?.button {
|
|
24
|
+
button.image = Self.menuBarIcon
|
|
25
|
+
button.action = #selector(statusItemClicked(_:))
|
|
26
|
+
button.sendAction(on: [.leftMouseUp, .rightMouseUp])
|
|
27
|
+
button.target = self
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
contextMenu = buildContextMenu()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
func warmUpPopover() {
|
|
34
|
+
let popover = makePopover()
|
|
35
|
+
_ = popover.contentViewController?.view
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
func dismissPopover() {
|
|
39
|
+
popover?.performClose(nil)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private func showProjectsPopover() {
|
|
43
|
+
guard let button = statusItem?.button else { return }
|
|
44
|
+
let popover = makePopover()
|
|
45
|
+
popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
|
|
46
|
+
popover.contentViewController?.view.window?.makeKey()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@objc private func statusItemClicked(_ sender: Any?) {
|
|
50
|
+
guard let event = NSApp.currentEvent,
|
|
51
|
+
let button = statusItem?.button else { return }
|
|
52
|
+
|
|
53
|
+
if event.type == .rightMouseUp {
|
|
54
|
+
contextMenu?.popUp(positioning: nil, at: NSPoint(x: 0, y: button.bounds.height + 4), in: button)
|
|
55
|
+
} else if let shown = popover, shown.isShown {
|
|
56
|
+
shown.performClose(sender)
|
|
57
|
+
} else {
|
|
58
|
+
showProjectsPopover()
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private func makePopover() -> NSPopover {
|
|
63
|
+
if let popover { return popover }
|
|
64
|
+
let timed = DiagnosticLog.shared.startTimed("makePopover")
|
|
65
|
+
let popover = NSPopover()
|
|
66
|
+
popover.contentViewController = NSHostingController(rootView: MainView(scanner: ProjectScanner.shared))
|
|
67
|
+
popover.behavior = .transient
|
|
68
|
+
popover.contentSize = NSSize(width: 380, height: 300)
|
|
69
|
+
popover.appearance = NSAppearance(named: .darkAqua)
|
|
70
|
+
popover.delegate = self
|
|
71
|
+
self.popover = popover
|
|
72
|
+
DiagnosticLog.shared.finish(timed)
|
|
73
|
+
return popover
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
func popoverWillShow(_ notification: Notification) {
|
|
77
|
+
AppActivationCoordinator.shared.refresh()
|
|
78
|
+
NotificationCenter.default.post(name: .latticesPopoverWillShow, object: nil)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
func popoverDidClose(_ notification: Notification) {
|
|
82
|
+
AppActivationCoordinator.shared.refresh()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private func buildContextMenu() -> NSMenu {
|
|
86
|
+
let menu = NSMenu()
|
|
87
|
+
|
|
88
|
+
let actions: [(String, String, Selector)] = [
|
|
89
|
+
("Home", "", #selector(menuWorkspace)),
|
|
90
|
+
("Layout", "", #selector(menuLayout)),
|
|
91
|
+
("Search", "", #selector(menuSearch)),
|
|
92
|
+
("Command Palette", "⌘⇧M", #selector(menuCommandPalette)),
|
|
93
|
+
]
|
|
94
|
+
for (title, shortcut, action) in actions {
|
|
95
|
+
let item = NSMenuItem(title: title, action: action, keyEquivalent: "")
|
|
96
|
+
item.target = self
|
|
97
|
+
if !shortcut.isEmpty {
|
|
98
|
+
// Display-only; the actual hotkey is global.
|
|
99
|
+
}
|
|
100
|
+
menu.addItem(item)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
menu.addItem(.separator())
|
|
104
|
+
|
|
105
|
+
let cliActions: [(String, Selector)] = [
|
|
106
|
+
("Projects…", #selector(menuProjects)),
|
|
107
|
+
("Initialize Project in Terminal…", #selector(menuInitializeProject)),
|
|
108
|
+
("Launch Project in Terminal…", #selector(menuLaunchProject)),
|
|
109
|
+
]
|
|
110
|
+
for (title, action) in cliActions {
|
|
111
|
+
let item = NSMenuItem(title: title, action: action, keyEquivalent: "")
|
|
112
|
+
item.target = self
|
|
113
|
+
menu.addItem(item)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
menu.addItem(.separator())
|
|
117
|
+
|
|
118
|
+
let update = NSMenuItem(title: "Update Lattices…", action: #selector(menuUpdate), keyEquivalent: "")
|
|
119
|
+
update.target = self
|
|
120
|
+
menu.addItem(update)
|
|
121
|
+
|
|
122
|
+
menu.addItem(.separator())
|
|
123
|
+
|
|
124
|
+
let settings = NSMenuItem(title: "Help & Settings…", action: #selector(menuSettings), keyEquivalent: ",")
|
|
125
|
+
settings.target = self
|
|
126
|
+
menu.addItem(settings)
|
|
127
|
+
|
|
128
|
+
menu.addItem(.separator())
|
|
129
|
+
|
|
130
|
+
let quit = NSMenuItem(title: "Quit Lattices", action: #selector(menuQuit), keyEquivalent: "q")
|
|
131
|
+
quit.target = self
|
|
132
|
+
menu.addItem(quit)
|
|
133
|
+
|
|
134
|
+
return menu
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@objc private func menuCommandPalette() { CommandPaletteWindow.shared.toggle() }
|
|
138
|
+
@objc private func menuWorkspace() { ScreenMapWindowController.shared.showPage(.home) }
|
|
139
|
+
@objc private func menuLayout() { ScreenMapWindowController.shared.showPage(.screenMap) }
|
|
140
|
+
@objc private func menuSearch() { ScreenMapWindowController.shared.showPage(.desktopInventory) }
|
|
141
|
+
@objc private func menuProjects() { DispatchQueue.main.async { self.showProjectsPopover() } }
|
|
142
|
+
@objc private func menuInitializeProject() { CliActionLauncher.initializeProjectInTerminal() }
|
|
143
|
+
@objc private func menuLaunchProject() { CliActionLauncher.launchProjectInTerminal() }
|
|
144
|
+
@MainActor @objc private func menuUpdate() { AppUpdater.shared.promptForUpdate() }
|
|
145
|
+
@objc private func menuSettings() { SettingsWindowController.shared.show() }
|
|
146
|
+
@objc private func menuQuit() { NSApp.terminate(nil) }
|
|
147
|
+
|
|
148
|
+
private static let menuBarIcon: NSImage = {
|
|
149
|
+
let size: CGFloat = 18
|
|
150
|
+
let image = NSImage(size: NSSize(width: size, height: size), flipped: true) { _ in
|
|
151
|
+
let pad: CGFloat = 2
|
|
152
|
+
let gap: CGFloat = 1.5
|
|
153
|
+
let cellSize = (size - 2 * pad - 2 * gap) / 3
|
|
154
|
+
let solidCells: Set<Int> = [0, 3, 6, 7, 8]
|
|
155
|
+
|
|
156
|
+
for row in 0..<3 {
|
|
157
|
+
for column in 0..<3 {
|
|
158
|
+
let index = row * 3 + column
|
|
159
|
+
let x = pad + CGFloat(column) * (cellSize + gap)
|
|
160
|
+
let y = pad + CGFloat(row) * (cellSize + gap)
|
|
161
|
+
let rect = NSRect(x: x, y: y, width: cellSize, height: cellSize)
|
|
162
|
+
|
|
163
|
+
if solidCells.contains(index) {
|
|
164
|
+
NSColor.black.setFill()
|
|
165
|
+
} else {
|
|
166
|
+
NSColor.black.withAlphaComponent(0.25).setFill()
|
|
167
|
+
}
|
|
168
|
+
let path = NSBezierPath(roundedRect: rect, xRadius: 0.8, yRadius: 0.8)
|
|
169
|
+
path.fill()
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return true
|
|
173
|
+
}
|
|
174
|
+
image.isTemplate = true
|
|
175
|
+
return image
|
|
176
|
+
}()
|
|
177
|
+
}
|
|
@@ -4,8 +4,8 @@ import AppKit
|
|
|
4
4
|
// MARK: - Onboarding Flow
|
|
5
5
|
|
|
6
6
|
/// A step-by-step welcome screen shown on first launch.
|
|
7
|
-
///
|
|
8
|
-
///
|
|
7
|
+
/// Keeps setup quiet: permissions are introduced as optional capabilities
|
|
8
|
+
/// and requested later from the feature that needs them.
|
|
9
9
|
struct OnboardingView: View {
|
|
10
10
|
@ObservedObject private var permChecker = PermissionChecker.shared
|
|
11
11
|
@ObservedObject private var prefs = Preferences.shared
|
|
@@ -15,8 +15,7 @@ struct OnboardingView: View {
|
|
|
15
15
|
|
|
16
16
|
enum Step: Int, CaseIterable {
|
|
17
17
|
case welcome
|
|
18
|
-
case
|
|
19
|
-
case screenRecording
|
|
18
|
+
case capabilities
|
|
20
19
|
case projectRoot
|
|
21
20
|
case tmux
|
|
22
21
|
case done
|
|
@@ -39,8 +38,7 @@ struct OnboardingView: View {
|
|
|
39
38
|
Group {
|
|
40
39
|
switch step {
|
|
41
40
|
case .welcome: welcomeStep
|
|
42
|
-
case .
|
|
43
|
-
case .screenRecording: screenRecordingStep
|
|
41
|
+
case .capabilities: capabilitiesStep
|
|
44
42
|
case .projectRoot: projectRootStep
|
|
45
43
|
case .tmux: tmuxStep
|
|
46
44
|
case .done: doneStep
|
|
@@ -98,24 +96,37 @@ struct OnboardingView: View {
|
|
|
98
96
|
}
|
|
99
97
|
}
|
|
100
98
|
|
|
101
|
-
private var
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
granted: permChecker.accessibility,
|
|
107
|
-
action: { permChecker.requestAccessibility() }
|
|
108
|
-
)
|
|
109
|
-
}
|
|
99
|
+
private var capabilitiesStep: some View {
|
|
100
|
+
VStack(spacing: 16) {
|
|
101
|
+
Image(systemName: "slider.horizontal.3")
|
|
102
|
+
.font(.system(size: 28))
|
|
103
|
+
.foregroundColor(.white.opacity(0.7))
|
|
110
104
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
105
|
+
Text("Enable more when you need it")
|
|
106
|
+
.font(Typo.title(16))
|
|
107
|
+
.foregroundColor(Palette.text)
|
|
108
|
+
|
|
109
|
+
Text("Lattices launches projects without any extra permissions. Click a capability to set it up now in the Permissions Assistant — or skip and turn it on later.")
|
|
110
|
+
.font(Typo.body(12))
|
|
111
|
+
.foregroundColor(Palette.textDim)
|
|
112
|
+
.multilineTextAlignment(.center)
|
|
113
|
+
.lineSpacing(3)
|
|
114
|
+
|
|
115
|
+
VStack(alignment: .leading, spacing: 8) {
|
|
116
|
+
ForEach(Capability.allCases) { cap in
|
|
117
|
+
capabilityRow(cap)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
.padding(10)
|
|
121
|
+
.background(
|
|
122
|
+
RoundedRectangle(cornerRadius: 6)
|
|
123
|
+
.fill(Palette.surface)
|
|
124
|
+
.overlay(
|
|
125
|
+
RoundedRectangle(cornerRadius: 6)
|
|
126
|
+
.strokeBorder(Palette.border, lineWidth: 0.5)
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
}
|
|
119
130
|
}
|
|
120
131
|
|
|
121
132
|
private var projectRootStep: some View {
|
|
@@ -251,8 +262,7 @@ struct OnboardingView: View {
|
|
|
251
262
|
.foregroundColor(Palette.text)
|
|
252
263
|
|
|
253
264
|
VStack(alignment: .leading, spacing: 8) {
|
|
254
|
-
statusRow("
|
|
255
|
-
statusRow("Screen Recording", granted: permChecker.screenRecording)
|
|
265
|
+
statusRow("Permission prompts", granted: true, detail: "shown when needed")
|
|
256
266
|
statusRow("Project root", granted: !prefs.scanRoot.isEmpty,
|
|
257
267
|
detail: prefs.scanRoot.isEmpty ? "not set" : abbreviatePath(prefs.scanRoot))
|
|
258
268
|
statusRow("tmux", granted: tmux.isAvailable,
|
|
@@ -293,43 +303,47 @@ struct OnboardingView: View {
|
|
|
293
303
|
|
|
294
304
|
// MARK: - Shared helpers
|
|
295
305
|
|
|
296
|
-
private func
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
.foregroundColor(Palette.textDim)
|
|
309
|
-
.multilineTextAlignment(.center)
|
|
310
|
-
.lineSpacing(3)
|
|
311
|
-
|
|
312
|
-
if granted {
|
|
313
|
-
HStack(spacing: 6) {
|
|
314
|
-
Image(systemName: "checkmark.circle.fill")
|
|
315
|
-
.foregroundColor(Palette.running)
|
|
316
|
-
Text("Granted")
|
|
306
|
+
private func capabilityRow(_ cap: Capability) -> some View {
|
|
307
|
+
let granted = cap.isGranted
|
|
308
|
+
return Button {
|
|
309
|
+
PermissionsAssistantWindowController.shared.show(focus: cap)
|
|
310
|
+
} label: {
|
|
311
|
+
HStack(alignment: .top, spacing: 10) {
|
|
312
|
+
Image(systemName: cap.iconName)
|
|
313
|
+
.font(.system(size: 12, weight: .semibold))
|
|
314
|
+
.foregroundColor(granted ? Palette.running : Palette.text.opacity(0.85))
|
|
315
|
+
.frame(width: 18)
|
|
316
|
+
VStack(alignment: .leading, spacing: 3) {
|
|
317
|
+
Text(cap.title)
|
|
317
318
|
.font(Typo.monoBold(11))
|
|
318
|
-
.foregroundColor(Palette.
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
Text("Grant \(title)")
|
|
323
|
-
.angularButton(.white, filled: false)
|
|
319
|
+
.foregroundColor(Palette.text)
|
|
320
|
+
Text(cap.requirementLabel)
|
|
321
|
+
.font(Typo.mono(10))
|
|
322
|
+
.foregroundColor(Palette.textMuted)
|
|
324
323
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
.
|
|
329
|
-
.
|
|
330
|
-
.
|
|
324
|
+
Spacer(minLength: 0)
|
|
325
|
+
Text(granted ? "ON" : "Set up")
|
|
326
|
+
.font(Typo.monoBold(9))
|
|
327
|
+
.foregroundColor(granted ? Palette.running : Palette.textDim)
|
|
328
|
+
.padding(.horizontal, 6)
|
|
329
|
+
.padding(.vertical, 3)
|
|
330
|
+
.background(
|
|
331
|
+
Capsule().fill((granted ? Palette.running : Palette.borderLit).opacity(0.12))
|
|
332
|
+
)
|
|
331
333
|
}
|
|
334
|
+
.padding(.horizontal, 8)
|
|
335
|
+
.padding(.vertical, 6)
|
|
336
|
+
.contentShape(Rectangle())
|
|
337
|
+
.background(
|
|
338
|
+
RoundedRectangle(cornerRadius: 5)
|
|
339
|
+
.fill(Color.clear)
|
|
340
|
+
.overlay(
|
|
341
|
+
RoundedRectangle(cornerRadius: 5)
|
|
342
|
+
.strokeBorder(Palette.border.opacity(0.6), lineWidth: 0.5)
|
|
343
|
+
)
|
|
344
|
+
)
|
|
332
345
|
}
|
|
346
|
+
.buttonStyle(.plain)
|
|
333
347
|
}
|
|
334
348
|
|
|
335
349
|
private func statusRow(_ label: String, granted: Bool, detail: String? = nil) -> some View {
|
|
@@ -382,8 +396,6 @@ struct OnboardingView: View {
|
|
|
382
396
|
|
|
383
397
|
private var nextLabel: String {
|
|
384
398
|
switch step {
|
|
385
|
-
case .accessibility where !permChecker.accessibility: return "Continue anyway"
|
|
386
|
-
case .screenRecording where !permChecker.screenRecording: return "Continue anyway"
|
|
387
399
|
case .projectRoot where prefs.scanRoot.isEmpty: return "Skip for now"
|
|
388
400
|
case .tmux where !tmux.isAvailable: return "Skip"
|
|
389
401
|
default: return "Continue"
|