@lattices/cli 0.4.13 → 0.5.0

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.
Files changed (180) hide show
  1. package/README.md +5 -7
  2. package/apps/mac/Info.plist +2 -2
  3. package/apps/mac/Lattices.app/Contents/Info.plist +4 -12
  4. package/apps/mac/Lattices.app/Contents/MacOS/Lattices +0 -0
  5. package/bin/lattices-app.ts +110 -17
  6. package/bin/lattices-build +125 -0
  7. package/bin/lattices-dev +89 -16
  8. package/bin/lattices.ts +977 -16
  9. package/docs/agents.md +81 -4
  10. package/docs/ai-chat-ux-review.md +416 -0
  11. package/docs/api.md +135 -3
  12. package/docs/app.md +30 -8
  13. package/docs/config.md +4 -0
  14. package/docs/mouse-gestures.md +191 -63
  15. package/docs/proposals/LAT-004-interactive-overlay-actors.md +1 -1
  16. package/docs/proposals/LAT-005-action-runtime-product-spine.md +914 -0
  17. package/docs/proposals/LAT-006-mira-in-lattices.md +553 -0
  18. package/docs/reference/dewey.config.ts +2 -2
  19. package/docs/release.md +171 -0
  20. package/docs/repo-structure.md +4 -5
  21. package/docs/voice.md +11 -27
  22. package/package.json +9 -10
  23. package/apps/mac/Package.swift +0 -27
  24. package/apps/mac/Sources/AppShell/App.swift +0 -26
  25. package/apps/mac/Sources/AppShell/AppActivationCoordinator.swift +0 -27
  26. package/apps/mac/Sources/AppShell/AppDelegate.swift +0 -189
  27. package/apps/mac/Sources/AppShell/AppServicesBootstrap.swift +0 -25
  28. package/apps/mac/Sources/AppShell/AppShellView.swift +0 -171
  29. package/apps/mac/Sources/AppShell/AppUpdater.swift +0 -305
  30. package/apps/mac/Sources/AppShell/CliActionLauncher.swift +0 -50
  31. package/apps/mac/Sources/AppShell/HomeDashboardView.swift +0 -133
  32. package/apps/mac/Sources/AppShell/HotkeyBootstrap.swift +0 -87
  33. package/apps/mac/Sources/AppShell/KeyRecorderView.swift +0 -210
  34. package/apps/mac/Sources/AppShell/LatticesRuntime.swift +0 -104
  35. package/apps/mac/Sources/AppShell/MainView.swift +0 -847
  36. package/apps/mac/Sources/AppShell/MainWindow.swift +0 -83
  37. package/apps/mac/Sources/AppShell/MenuBarController.swift +0 -177
  38. package/apps/mac/Sources/AppShell/OnboardingView.swift +0 -483
  39. package/apps/mac/Sources/AppShell/PermissionsAssistantView.swift +0 -366
  40. package/apps/mac/Sources/AppShell/PermissionsAssistantWindow.swift +0 -70
  41. package/apps/mac/Sources/AppShell/Preferences.swift +0 -297
  42. package/apps/mac/Sources/AppShell/SettingsView.swift +0 -3163
  43. package/apps/mac/Sources/AppShell/SettingsWindow.swift +0 -34
  44. package/apps/mac/Sources/AppShell/WorkspaceInspectorPresenter.swift +0 -13
  45. package/apps/mac/Sources/Core/Actions/HotkeyManager.swift +0 -256
  46. package/apps/mac/Sources/Core/Actions/HotkeyStore.swift +0 -399
  47. package/apps/mac/Sources/Core/Actions/IntentEngine.swift +0 -988
  48. package/apps/mac/Sources/Core/Actions/IntentSchema.swift +0 -94
  49. package/apps/mac/Sources/Core/Actions/Intents/CreateLayerIntent.swift +0 -54
  50. package/apps/mac/Sources/Core/Actions/Intents/DistributeIntent.swift +0 -56
  51. package/apps/mac/Sources/Core/Actions/Intents/FocusIntent.swift +0 -69
  52. package/apps/mac/Sources/Core/Actions/Intents/HelpIntent.swift +0 -41
  53. package/apps/mac/Sources/Core/Actions/Intents/KillIntent.swift +0 -47
  54. package/apps/mac/Sources/Core/Actions/Intents/LatticeIntent.swift +0 -53
  55. package/apps/mac/Sources/Core/Actions/Intents/LaunchIntent.swift +0 -67
  56. package/apps/mac/Sources/Core/Actions/Intents/ListSessionsIntent.swift +0 -32
  57. package/apps/mac/Sources/Core/Actions/Intents/ListWindowsIntent.swift +0 -30
  58. package/apps/mac/Sources/Core/Actions/Intents/ScanIntent.swift +0 -52
  59. package/apps/mac/Sources/Core/Actions/Intents/SearchIntent.swift +0 -190
  60. package/apps/mac/Sources/Core/Actions/Intents/SwitchLayerIntent.swift +0 -50
  61. package/apps/mac/Sources/Core/Actions/Intents/TileIntent.swift +0 -61
  62. package/apps/mac/Sources/Core/Actions/PaletteCommand.swift +0 -439
  63. package/apps/mac/Sources/Core/Actions/VoiceIntentResolver.swift +0 -713
  64. package/apps/mac/Sources/Core/Companion/CompanionActivityLog.swift +0 -70
  65. package/apps/mac/Sources/Core/Companion/CompanionKeyboardController.swift +0 -141
  66. package/apps/mac/Sources/Core/Companion/LatticesCompanionBridgeServer.swift +0 -454
  67. package/apps/mac/Sources/Core/Companion/LatticesCompanionCockpit.swift +0 -555
  68. package/apps/mac/Sources/Core/Companion/LatticesCompanionSecurityCoordinator.swift +0 -629
  69. package/apps/mac/Sources/Core/Companion/LatticesCompanionTrackpadController.swift +0 -204
  70. package/apps/mac/Sources/Core/Companion/LatticesDeckHost.swift +0 -1463
  71. package/apps/mac/Sources/Core/Daemon/DaemonProtocol.swift +0 -114
  72. package/apps/mac/Sources/Core/Daemon/DaemonServer.swift +0 -427
  73. package/apps/mac/Sources/Core/Daemon/LatticesApi.swift +0 -2965
  74. package/apps/mac/Sources/Core/Desktop/AccessibilityTextExtractor.swift +0 -111
  75. package/apps/mac/Sources/Core/Desktop/AppTypeClassifier.swift +0 -106
  76. package/apps/mac/Sources/Core/Desktop/DesktopModel.swift +0 -331
  77. package/apps/mac/Sources/Core/Desktop/DesktopModelTypes.swift +0 -73
  78. package/apps/mac/Sources/Core/Desktop/InventoryManager.swift +0 -35
  79. package/apps/mac/Sources/Core/Desktop/InventoryPath.swift +0 -43
  80. package/apps/mac/Sources/Core/Desktop/MouseFinder.swift +0 -527
  81. package/apps/mac/Sources/Core/Desktop/OcrModel.swift +0 -467
  82. package/apps/mac/Sources/Core/Desktop/OcrStore.swift +0 -329
  83. package/apps/mac/Sources/Core/Desktop/PlacementSpec.swift +0 -195
  84. package/apps/mac/Sources/Core/Desktop/SessionWindowLocator.swift +0 -139
  85. package/apps/mac/Sources/Core/Desktop/TilePickerView.swift +0 -209
  86. package/apps/mac/Sources/Core/Desktop/WindowCapture.swift +0 -33
  87. package/apps/mac/Sources/Core/Desktop/WindowDragSnapController.swift +0 -429
  88. package/apps/mac/Sources/Core/Desktop/WindowPreviewCard.swift +0 -100
  89. package/apps/mac/Sources/Core/Desktop/WindowPreviewStore.swift +0 -112
  90. package/apps/mac/Sources/Core/Desktop/WindowSelectionStore.swift +0 -76
  91. package/apps/mac/Sources/Core/Desktop/WindowTiler.swift +0 -2222
  92. package/apps/mac/Sources/Core/Input/EventTapBreaker.swift +0 -124
  93. package/apps/mac/Sources/Core/Input/EventTapThread.swift +0 -54
  94. package/apps/mac/Sources/Core/Input/InputCaptureResetCenter.swift +0 -20
  95. package/apps/mac/Sources/Core/Input/KeyboardRemapConfig.swift +0 -69
  96. package/apps/mac/Sources/Core/Input/KeyboardRemapController.swift +0 -346
  97. package/apps/mac/Sources/Core/Input/KeyboardRemapStore.swift +0 -141
  98. package/apps/mac/Sources/Core/Input/MouseGestureConfig.swift +0 -499
  99. package/apps/mac/Sources/Core/Input/MouseGestureController.swift +0 -2271
  100. package/apps/mac/Sources/Core/Input/MouseInputDeviceStore.swift +0 -98
  101. package/apps/mac/Sources/Core/Input/MouseInputEventViewer.swift +0 -272
  102. package/apps/mac/Sources/Core/Input/MouseShortcutStore.swift +0 -170
  103. package/apps/mac/Sources/Core/Input/SecureEventInputMonitor.swift +0 -39
  104. package/apps/mac/Sources/Core/Input/ShapeRecognizer.swift +0 -624
  105. package/apps/mac/Sources/Core/Input/TapBudgetMeter.swift +0 -56
  106. package/apps/mac/Sources/Core/Overlays/AppWindowShell.swift +0 -63
  107. package/apps/mac/Sources/Core/Overlays/CommandMode/CommandModeState.swift +0 -1566
  108. package/apps/mac/Sources/Core/Overlays/CommandMode/CommandModeView.swift +0 -1927
  109. package/apps/mac/Sources/Core/Overlays/CommandMode/CommandModeWindow.swift +0 -196
  110. package/apps/mac/Sources/Core/Overlays/CommandPalette/CommandPaletteView.swift +0 -307
  111. package/apps/mac/Sources/Core/Overlays/CommandPalette/CommandPaletteWindow.swift +0 -67
  112. package/apps/mac/Sources/Core/Overlays/HUD/CheatSheetHUD.swift +0 -576
  113. package/apps/mac/Sources/Core/Overlays/HUD/HUDBottomBar.swift +0 -279
  114. package/apps/mac/Sources/Core/Overlays/HUD/HUDController.swift +0 -1158
  115. package/apps/mac/Sources/Core/Overlays/HUD/HUDLeftBar.swift +0 -849
  116. package/apps/mac/Sources/Core/Overlays/HUD/HUDMinimap.swift +0 -179
  117. package/apps/mac/Sources/Core/Overlays/HUD/HUDRightBar.swift +0 -596
  118. package/apps/mac/Sources/Core/Overlays/HUD/HUDState.swift +0 -367
  119. package/apps/mac/Sources/Core/Overlays/HUD/HUDTopBar.swift +0 -243
  120. package/apps/mac/Sources/Core/Overlays/HUD/LauncherHUD.swift +0 -334
  121. package/apps/mac/Sources/Core/Overlays/HUD/LayerBezel.swift +0 -203
  122. package/apps/mac/Sources/Core/Overlays/OmniSearch/OmniSearchState.swift +0 -280
  123. package/apps/mac/Sources/Core/Overlays/OmniSearch/OmniSearchView.swift +0 -422
  124. package/apps/mac/Sources/Core/Overlays/OmniSearch/OmniSearchWindow.swift +0 -94
  125. package/apps/mac/Sources/Core/Overlays/OverlayPanelShell.swift +0 -241
  126. package/apps/mac/Sources/Core/Overlays/ScreenMap/ScreenMapState.swift +0 -3135
  127. package/apps/mac/Sources/Core/Overlays/ScreenMap/ScreenMapView.swift +0 -3977
  128. package/apps/mac/Sources/Core/Overlays/ScreenMap/ScreenMapWindowController.swift +0 -119
  129. package/apps/mac/Sources/Core/Overlays/ScreenOverlayCanvasController.swift +0 -1217
  130. package/apps/mac/Sources/Core/Overlays/Voice/VoiceCommandWindow.swift +0 -1575
  131. package/apps/mac/Sources/Core/Pi/PiAuthNextStepCard.swift +0 -148
  132. package/apps/mac/Sources/Core/Pi/PiAuthPromptCard.swift +0 -90
  133. package/apps/mac/Sources/Core/Pi/PiChatDock.swift +0 -564
  134. package/apps/mac/Sources/Core/Pi/PiChatSession.swift +0 -1948
  135. package/apps/mac/Sources/Core/Pi/PiInstallCallout.swift +0 -86
  136. package/apps/mac/Sources/Core/Pi/PiProviderSetupCallout.swift +0 -99
  137. package/apps/mac/Sources/Core/Pi/PiWorkspaceView.swift +0 -510
  138. package/apps/mac/Sources/Core/System/Capability.swift +0 -79
  139. package/apps/mac/Sources/Core/System/DiagnosticLog.swift +0 -373
  140. package/apps/mac/Sources/Core/System/EventBus.swift +0 -31
  141. package/apps/mac/Sources/Core/System/PermissionChecker.swift +0 -224
  142. package/apps/mac/Sources/Core/System/ProcessModel.swift +0 -199
  143. package/apps/mac/Sources/Core/System/ProcessQuery.swift +0 -151
  144. package/apps/mac/Sources/Core/System/SystemTelemetryMonitor.swift +0 -273
  145. package/apps/mac/Sources/Core/Voice/AdvisorLearningStore.swift +0 -90
  146. package/apps/mac/Sources/Core/Voice/AgentSession.swift +0 -377
  147. package/apps/mac/Sources/Core/Voice/AudioProvider.swift +0 -555
  148. package/apps/mac/Sources/Core/Voice/HandsOffSession.swift +0 -839
  149. package/apps/mac/Sources/Core/Voice/VoiceChatView.swift +0 -192
  150. package/apps/mac/Sources/Core/Voice/VoxClient.swift +0 -454
  151. package/apps/mac/Sources/Core/Workspace/Project.swift +0 -28
  152. package/apps/mac/Sources/Core/Workspace/ProjectScanner.swift +0 -141
  153. package/apps/mac/Sources/Core/Workspace/SessionLayerStore.swift +0 -285
  154. package/apps/mac/Sources/Core/Workspace/SessionManager.swift +0 -75
  155. package/apps/mac/Sources/Core/Workspace/Terminal/Terminal.swift +0 -259
  156. package/apps/mac/Sources/Core/Workspace/Terminal/TerminalQuery.swift +0 -156
  157. package/apps/mac/Sources/Core/Workspace/Terminal/TerminalSynthesizer.swift +0 -200
  158. package/apps/mac/Sources/Core/Workspace/Tmux/TmuxModel.swift +0 -60
  159. package/apps/mac/Sources/Core/Workspace/Tmux/TmuxQuery.swift +0 -105
  160. package/apps/mac/Sources/Core/Workspace/WorkspaceManager.swift +0 -1027
  161. package/apps/mac/Sources/UI/ActionRow.swift +0 -78
  162. package/apps/mac/Sources/UI/OrphanRow.swift +0 -129
  163. package/apps/mac/Sources/UI/ProjectRow.swift +0 -368
  164. package/apps/mac/Sources/UI/TabGroupRow.swift +0 -178
  165. package/apps/mac/Sources/UI/Theme.swift +0 -164
  166. package/apps/mac/Tests/StageDragTests.swift +0 -333
  167. package/apps/mac/Tests/StageJoinTests.swift +0 -313
  168. package/apps/mac/Tests/StageManagerTests.swift +0 -280
  169. package/apps/mac/Tests/StageTileTests.swift +0 -353
  170. package/swift/Package.swift +0 -20
  171. package/swift/Sources/DeckKit/DeckAction.swift +0 -51
  172. package/swift/Sources/DeckKit/DeckBridgeSecurity.swift +0 -152
  173. package/swift/Sources/DeckKit/DeckCockpit.swift +0 -82
  174. package/swift/Sources/DeckKit/DeckHost.swift +0 -7
  175. package/swift/Sources/DeckKit/DeckManifest.swift +0 -145
  176. package/swift/Sources/DeckKit/DeckRuntimeSnapshot.swift +0 -533
  177. package/swift/Sources/DeckKit/DeckTrackpad.swift +0 -63
  178. package/swift/Sources/DeckKit/DeckValue.swift +0 -93
  179. package/swift/Sources/DeckKit/DeckVoiceError.swift +0 -88
  180. package/swift/Tests/DeckKitTests/DeckKitTests.swift +0 -286
@@ -1,483 +0,0 @@
1
- import SwiftUI
2
- import AppKit
3
-
4
- // MARK: - Onboarding Flow
5
-
6
- /// A step-by-step welcome screen shown on first launch.
7
- /// Keeps setup quiet: permissions are introduced as optional capabilities
8
- /// and requested later from the feature that needs them.
9
- struct OnboardingView: View {
10
- @ObservedObject private var permChecker = PermissionChecker.shared
11
- @ObservedObject private var prefs = Preferences.shared
12
- @ObservedObject private var tmux = TmuxModel.shared
13
- @State private var step: Step = .welcome
14
- var onComplete: () -> Void
15
-
16
- enum Step: Int, CaseIterable {
17
- case welcome
18
- case capabilities
19
- case projectRoot
20
- case tmux
21
- case done
22
- }
23
-
24
- var body: some View {
25
- VStack(spacing: 0) {
26
- // Progress dots
27
- HStack(spacing: 6) {
28
- ForEach(Step.allCases, id: \.rawValue) { s in
29
- Circle()
30
- .fill(s.rawValue <= step.rawValue ? Color.white : Color.white.opacity(0.20))
31
- .frame(width: 6, height: 6)
32
- }
33
- }
34
- .padding(.top, 28)
35
- .padding(.bottom, 24)
36
-
37
- // Step content
38
- Group {
39
- switch step {
40
- case .welcome: welcomeStep
41
- case .capabilities: capabilitiesStep
42
- case .projectRoot: projectRootStep
43
- case .tmux: tmuxStep
44
- case .done: doneStep
45
- }
46
- }
47
- .frame(maxWidth: .infinity, maxHeight: .infinity)
48
- .padding(.horizontal, 40)
49
-
50
- Spacer(minLength: 0)
51
-
52
- // Navigation
53
- HStack {
54
- if step != .welcome {
55
- Button("Back") { withAnimation(.easeInOut(duration: 0.2)) { goBack() } }
56
- .buttonStyle(.plain)
57
- .font(Typo.mono(11))
58
- .foregroundColor(Palette.textMuted)
59
- }
60
- Spacer()
61
- if step == .done {
62
- Button(action: { onComplete() }) {
63
- Text("Get started")
64
- .angularButton(Palette.running)
65
- }
66
- .buttonStyle(.plain)
67
- } else {
68
- Button(action: { withAnimation(.easeInOut(duration: 0.2)) { advance() } }) {
69
- Text(nextLabel)
70
- .angularButton(.white)
71
- }
72
- .buttonStyle(.plain)
73
- }
74
- }
75
- .padding(.horizontal, 40)
76
- .padding(.bottom, 28)
77
- }
78
- .frame(width: 480, height: 470)
79
- .background(Palette.bg)
80
- .preferredColorScheme(.dark)
81
- }
82
-
83
- // MARK: - Steps
84
-
85
- private var welcomeStep: some View {
86
- VStack(spacing: 16) {
87
- latticesIcon
88
- Text("Welcome to Lattices")
89
- .font(Typo.title(18))
90
- .foregroundColor(Palette.text)
91
- Text("Workspace control plane for macOS.\nLet's get you set up in under a minute.")
92
- .font(Typo.body(12))
93
- .foregroundColor(Palette.textDim)
94
- .multilineTextAlignment(.center)
95
- .lineSpacing(3)
96
- }
97
- }
98
-
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))
104
-
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
- }
130
- }
131
-
132
- private var projectRootStep: some View {
133
- VStack(spacing: 16) {
134
- Image(systemName: "folder.fill")
135
- .font(.system(size: 28))
136
- .foregroundColor(.white.opacity(0.7))
137
-
138
- Text("Project directory")
139
- .font(Typo.title(16))
140
- .foregroundColor(Palette.text)
141
-
142
- Text("Where do your projects live? Lattices scans this folder to find workspaces.")
143
- .font(Typo.body(12))
144
- .foregroundColor(Palette.textDim)
145
- .multilineTextAlignment(.center)
146
- .lineSpacing(3)
147
-
148
- HStack(spacing: 8) {
149
- Text(prefs.scanRoot.isEmpty ? "Not set" : abbreviatePath(prefs.scanRoot))
150
- .font(Typo.mono(11))
151
- .foregroundColor(prefs.scanRoot.isEmpty ? Palette.textMuted : Palette.text)
152
- .lineLimit(1)
153
- .truncationMode(.middle)
154
- .frame(maxWidth: .infinity, alignment: .leading)
155
- .padding(.horizontal, 10)
156
- .padding(.vertical, 8)
157
- .background(
158
- RoundedRectangle(cornerRadius: 5)
159
- .fill(Palette.surface)
160
- .overlay(
161
- RoundedRectangle(cornerRadius: 5)
162
- .strokeBorder(Palette.border, lineWidth: 0.5)
163
- )
164
- )
165
-
166
- Button("Browse") {
167
- let panel = NSOpenPanel()
168
- panel.canChooseFiles = false
169
- panel.canChooseDirectories = true
170
- panel.allowsMultipleSelection = false
171
- panel.directoryURL = URL(fileURLWithPath: prefs.scanRoot.isEmpty ? NSHomeDirectory() : prefs.scanRoot)
172
- if panel.runModal() == .OK, let url = panel.url {
173
- prefs.scanRoot = url.path
174
- }
175
- }
176
- .buttonStyle(.plain)
177
- .font(Typo.monoBold(10))
178
- .foregroundColor(.white)
179
- .padding(.horizontal, 10)
180
- .padding(.vertical, 6)
181
- .background(
182
- RoundedRectangle(cornerRadius: 5)
183
- .fill(Palette.surface)
184
- .overlay(
185
- RoundedRectangle(cornerRadius: 5)
186
- .strokeBorder(Palette.borderLit, lineWidth: 0.5)
187
- )
188
- )
189
- }
190
-
191
- if !prefs.scanRoot.isEmpty {
192
- HStack(spacing: 4) {
193
- Image(systemName: "checkmark.circle.fill")
194
- .font(.system(size: 10))
195
- .foregroundColor(Palette.running)
196
- Text(abbreviatePath(prefs.scanRoot))
197
- .font(Typo.mono(10))
198
- .foregroundColor(Palette.running)
199
- }
200
- }
201
- }
202
- }
203
-
204
- private var tmuxStep: some View {
205
- VStack(spacing: 16) {
206
- Image(systemName: "terminal.fill")
207
- .font(.system(size: 28))
208
- .foregroundColor(.white.opacity(0.7))
209
-
210
- Text("Terminal sessions")
211
- .font(Typo.title(16))
212
- .foregroundColor(Palette.text)
213
-
214
- if tmux.isAvailable {
215
- HStack(spacing: 6) {
216
- Image(systemName: "checkmark.circle.fill")
217
- .foregroundColor(Palette.running)
218
- Text("tmux is installed")
219
- .font(Typo.mono(12))
220
- .foregroundColor(Palette.running)
221
- }
222
- Text("Lattices can manage tmux sessions, pane layouts, and terminal workspaces for you.")
223
- .font(Typo.body(12))
224
- .foregroundColor(Palette.textDim)
225
- .multilineTextAlignment(.center)
226
- .lineSpacing(3)
227
- } else {
228
- Text("tmux is optional but recommended. It enables managed terminal sessions with persistent pane layouts.")
229
- .font(Typo.body(12))
230
- .foregroundColor(Palette.textDim)
231
- .multilineTextAlignment(.center)
232
- .lineSpacing(3)
233
-
234
- Button(action: CliActionLauncher.installTmuxInTerminal) {
235
- HStack(spacing: 6) {
236
- Image(systemName: "arrow.down.circle")
237
- .font(.system(size: 11))
238
- Text("Install tmux in Terminal")
239
- .font(Typo.monoBold(11))
240
- }
241
- .angularButton(.white, filled: false)
242
- }
243
- .buttonStyle(.plain)
244
-
245
- Text("You can always install it later. Window tiling, search, and OCR work without tmux.")
246
- .font(Typo.mono(10))
247
- .foregroundColor(Palette.textMuted)
248
- .multilineTextAlignment(.center)
249
- .lineSpacing(2)
250
- }
251
- }
252
- }
253
-
254
- private var doneStep: some View {
255
- VStack(spacing: 16) {
256
- Image(systemName: "checkmark.circle.fill")
257
- .font(.system(size: 36))
258
- .foregroundColor(Palette.running)
259
-
260
- Text("You're all set")
261
- .font(Typo.title(18))
262
- .foregroundColor(Palette.text)
263
-
264
- VStack(alignment: .leading, spacing: 8) {
265
- statusRow("Permission prompts", granted: true, detail: "shown when needed")
266
- statusRow("Project root", granted: !prefs.scanRoot.isEmpty,
267
- detail: prefs.scanRoot.isEmpty ? "not set" : abbreviatePath(prefs.scanRoot))
268
- statusRow("tmux", granted: tmux.isAvailable,
269
- detail: tmux.isAvailable ? "installed" : "skipped")
270
- }
271
- .padding(16)
272
- .background(
273
- RoundedRectangle(cornerRadius: 6)
274
- .fill(Palette.surface)
275
- .overlay(
276
- RoundedRectangle(cornerRadius: 6)
277
- .strokeBorder(Palette.border, lineWidth: 0.5)
278
- )
279
- )
280
-
281
- VStack(spacing: 10) {
282
- Text("Pick a repo and let the CLI do the setup in your terminal.")
283
- .font(Typo.mono(10))
284
- .foregroundColor(Palette.textMuted)
285
- .multilineTextAlignment(.center)
286
-
287
- HStack(spacing: 10) {
288
- Button(action: CliActionLauncher.initializeProjectInTerminal) {
289
- Text("Initialize Project")
290
- .angularButton(Palette.running)
291
- }
292
- .buttonStyle(.plain)
293
-
294
- Button(action: CliActionLauncher.launchProjectInTerminal) {
295
- Text("Launch Project")
296
- .angularButton(.white, filled: false)
297
- }
298
- .buttonStyle(.plain)
299
- }
300
- }
301
- }
302
- }
303
-
304
- // MARK: - Shared helpers
305
-
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)
318
- .font(Typo.monoBold(11))
319
- .foregroundColor(Palette.text)
320
- Text(cap.requirementLabel)
321
- .font(Typo.mono(10))
322
- .foregroundColor(Palette.textMuted)
323
- }
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
- )
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
- )
345
- }
346
- .buttonStyle(.plain)
347
- }
348
-
349
- private func statusRow(_ label: String, granted: Bool, detail: String? = nil) -> some View {
350
- HStack(spacing: 8) {
351
- Image(systemName: granted ? "checkmark.circle.fill" : "circle")
352
- .font(.system(size: 11))
353
- .foregroundColor(granted ? Palette.running : Palette.detach)
354
- Text(label)
355
- .font(Typo.mono(11))
356
- .foregroundColor(Palette.text)
357
- Spacer()
358
- if let detail {
359
- Text(detail)
360
- .font(Typo.mono(10))
361
- .foregroundColor(granted ? Palette.textDim : Palette.detach)
362
- } else {
363
- Text(granted ? "granted" : "not set")
364
- .font(Typo.mono(10))
365
- .foregroundColor(granted ? Palette.running : Palette.detach)
366
- }
367
- }
368
- }
369
-
370
- private var latticesIcon: some View {
371
- // 3x3 grid — L-shape pattern
372
- let cells = [true, false, false, true, false, false, true, true, true]
373
- let size: CGFloat = 40
374
- let pad: CGFloat = 4
375
- let gap: CGFloat = 2.5
376
- let cell = (size - 2 * pad - 2 * gap) / 3
377
- return Canvas { context, _ in
378
- for (i, bright) in cells.enumerated() {
379
- let row = i / 3
380
- let col = i % 3
381
- let rect = CGRect(
382
- x: pad + CGFloat(col) * (cell + gap),
383
- y: pad + CGFloat(row) * (cell + gap),
384
- width: cell, height: cell
385
- )
386
- context.fill(
387
- RoundedRectangle(cornerRadius: 2).path(in: rect),
388
- with: .color(bright ? .white : .white.opacity(0.18))
389
- )
390
- }
391
- }
392
- .frame(width: size, height: size)
393
- }
394
-
395
- // MARK: - Navigation
396
-
397
- private var nextLabel: String {
398
- switch step {
399
- case .projectRoot where prefs.scanRoot.isEmpty: return "Skip for now"
400
- case .tmux where !tmux.isAvailable: return "Skip"
401
- default: return "Continue"
402
- }
403
- }
404
-
405
- private func advance() {
406
- guard let next = Step(rawValue: step.rawValue + 1) else { return }
407
- step = next
408
- }
409
-
410
- private func goBack() {
411
- guard let prev = Step(rawValue: step.rawValue - 1) else { return }
412
- step = prev
413
- }
414
-
415
- private func abbreviatePath(_ path: String) -> String {
416
- let home = NSHomeDirectory()
417
- if path.hasPrefix(home) {
418
- return "~" + path.dropFirst(home.count)
419
- }
420
- return path
421
- }
422
- }
423
-
424
- // MARK: - Window Controller
425
-
426
- final class OnboardingWindowController {
427
- static let shared = OnboardingWindowController()
428
-
429
- private var window: NSWindow?
430
- private static let completedKey = "onboarding.completed"
431
-
432
- var hasCompleted: Bool {
433
- UserDefaults.standard.bool(forKey: Self.completedKey)
434
- }
435
-
436
- /// Show the onboarding window if not yet completed.
437
- /// Returns true if onboarding was shown.
438
- @discardableResult
439
- func showIfNeeded() -> Bool {
440
- guard !hasCompleted else { return false }
441
- show()
442
- return true
443
- }
444
-
445
- func show() {
446
- if let w = window {
447
- w.makeKeyAndOrderFront(nil)
448
- NSApp.activate(ignoringOtherApps: true)
449
- return
450
- }
451
-
452
- let view = OnboardingView {
453
- self.complete()
454
- }
455
-
456
- let w = AppWindowShell.makeWindow(
457
- config: .init(
458
- title: "Welcome to Lattices",
459
- titleVisible: false,
460
- initialSize: NSSize(width: 480, height: 470),
461
- minSize: NSSize(width: 480, height: 470),
462
- maxSize: NSSize(width: 480, height: 470),
463
- miniaturizable: false
464
- ),
465
- rootView: view
466
- )
467
- w.styleMask.remove(.resizable)
468
- AppWindowShell.positionCentered(w)
469
- AppWindowShell.present(w)
470
- self.window = w
471
- }
472
-
473
- private func complete() {
474
- UserDefaults.standard.set(true, forKey: Self.completedKey)
475
- window?.orderOut(nil)
476
- window = nil
477
- AppDelegate.updateActivationPolicy()
478
- }
479
-
480
- func reset() {
481
- UserDefaults.standard.removeObject(forKey: Self.completedKey)
482
- }
483
- }