@lattices/cli 0.4.2 → 0.4.6

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 (146) hide show
  1. package/README.md +3 -0
  2. package/app/Info.plist +2 -2
  3. package/app/Lattices.app/Contents/Info.plist +2 -2
  4. package/app/Lattices.app/Contents/MacOS/Lattices +0 -0
  5. package/app/Package.swift +6 -0
  6. package/app/Sources/AppShell/App.swift +20 -0
  7. package/app/Sources/{AppDelegate.swift → AppShell/AppDelegate.swift} +94 -34
  8. package/app/Sources/{AppShellView.swift → AppShell/AppShellView.swift} +12 -1
  9. package/app/Sources/AppShell/AppUpdater.swift +92 -0
  10. package/app/Sources/AppShell/CliActionLauncher.swift +50 -0
  11. package/app/Sources/{HomeDashboardView.swift → AppShell/HomeDashboardView.swift} +18 -10
  12. package/app/Sources/AppShell/LatticesRuntime.swift +61 -0
  13. package/app/Sources/{MainView.swift → AppShell/MainView.swift} +351 -191
  14. package/app/Sources/{OnboardingView.swift → AppShell/OnboardingView.swift} +30 -16
  15. package/app/Sources/{Preferences.swift → AppShell/Preferences.swift} +78 -0
  16. package/app/Sources/{SettingsView.swift → AppShell/SettingsView.swift} +869 -152
  17. package/app/Sources/{HotkeyStore.swift → Core/Actions/HotkeyStore.swift} +9 -5
  18. package/app/Sources/{IntentEngine.swift → Core/Actions/IntentEngine.swift} +51 -27
  19. package/app/Sources/Core/Actions/IntentSchema.swift +94 -0
  20. package/app/Sources/{Intents → Core/Actions/Intents}/LatticeIntent.swift +0 -25
  21. package/app/Sources/{PaletteCommand.swift → Core/Actions/PaletteCommand.swift} +26 -6
  22. package/app/Sources/{VoiceIntentResolver.swift → Core/Actions/VoiceIntentResolver.swift} +46 -4
  23. package/app/Sources/Core/Companion/CompanionActivityLog.swift +70 -0
  24. package/app/Sources/Core/Companion/CompanionKeyboardController.swift +141 -0
  25. package/app/Sources/Core/Companion/LatticesCompanionBridgeServer.swift +438 -0
  26. package/app/Sources/Core/Companion/LatticesCompanionCockpit.swift +555 -0
  27. package/app/Sources/Core/Companion/LatticesCompanionSecurityCoordinator.swift +594 -0
  28. package/app/Sources/Core/Companion/LatticesCompanionTrackpadController.swift +204 -0
  29. package/app/Sources/Core/Companion/LatticesDeckHost.swift +1463 -0
  30. package/app/Sources/{LatticesApi.swift → Core/Daemon/LatticesApi.swift} +125 -4
  31. package/app/Sources/{AppTypeClassifier.swift → Core/Desktop/AppTypeClassifier.swift} +36 -0
  32. package/app/Sources/{DesktopModel.swift → Core/Desktop/DesktopModel.swift} +6 -8
  33. package/app/Sources/Core/Desktop/MouseFinder.swift +527 -0
  34. package/app/Sources/Core/Desktop/SessionWindowLocator.swift +139 -0
  35. package/app/Sources/Core/Desktop/WindowDragSnapController.swift +628 -0
  36. package/app/Sources/Core/Desktop/WindowPreviewCard.swift +100 -0
  37. package/app/Sources/Core/Desktop/WindowPreviewStore.swift +113 -0
  38. package/app/Sources/Core/Desktop/WindowSelectionStore.swift +76 -0
  39. package/app/Sources/{WindowTiler.swift → Core/Desktop/WindowTiler.swift} +351 -172
  40. package/app/Sources/Core/Input/MouseGestureConfig.swift +364 -0
  41. package/app/Sources/Core/Input/MouseGestureController.swift +1203 -0
  42. package/app/Sources/Core/Input/MouseInputDeviceStore.swift +98 -0
  43. package/app/Sources/Core/Input/MouseInputEventViewer.swift +272 -0
  44. package/app/Sources/Core/Input/MouseShortcutStore.swift +107 -0
  45. package/app/Sources/{CommandModeState.swift → Core/Overlays/CommandMode/CommandModeState.swift} +127 -24
  46. package/app/Sources/{CommandModeView.swift → Core/Overlays/CommandMode/CommandModeView.swift} +492 -79
  47. package/app/Sources/Core/Overlays/CommandPalette/CommandPaletteWindow.swift +67 -0
  48. package/app/Sources/{CheatSheetHUD.swift → Core/Overlays/HUD/CheatSheetHUD.swift} +1 -0
  49. package/app/Sources/{HUDRightBar.swift → Core/Overlays/HUD/HUDRightBar.swift} +23 -201
  50. package/app/Sources/{LauncherHUD.swift → Core/Overlays/HUD/LauncherHUD.swift} +12 -26
  51. package/app/Sources/{OmniSearchView.swift → Core/Overlays/OmniSearch/OmniSearchView.swift} +136 -2
  52. package/app/Sources/{OmniSearchWindow.swift → Core/Overlays/OmniSearch/OmniSearchWindow.swift} +21 -32
  53. package/app/Sources/Core/Overlays/OverlayPanelShell.swift +241 -0
  54. package/app/Sources/{ScreenMapState.swift → Core/Overlays/ScreenMap/ScreenMapState.swift} +116 -32
  55. package/app/Sources/{ScreenMapView.swift → Core/Overlays/ScreenMap/ScreenMapView.swift} +510 -524
  56. package/app/Sources/{ScreenMapWindowController.swift → Core/Overlays/ScreenMap/ScreenMapWindowController.swift} +12 -4
  57. package/app/Sources/{VoiceCommandWindow.swift → Core/Overlays/Voice/VoiceCommandWindow.swift} +46 -53
  58. package/app/Sources/Core/Pi/PiAuthNextStepCard.swift +148 -0
  59. package/app/Sources/Core/Pi/PiAuthPromptCard.swift +90 -0
  60. package/app/Sources/{PiChatDock.swift → Core/Pi/PiChatDock.swift} +137 -74
  61. package/app/Sources/{PiChatSession.swift → Core/Pi/PiChatSession.swift} +608 -108
  62. package/app/Sources/Core/Pi/PiInstallCallout.swift +86 -0
  63. package/app/Sources/Core/Pi/PiProviderSetupCallout.swift +99 -0
  64. package/app/Sources/{PiWorkspaceView.swift → Core/Pi/PiWorkspaceView.swift} +174 -77
  65. package/app/Sources/{PermissionChecker.swift → Core/System/PermissionChecker.swift} +76 -2
  66. package/app/Sources/Core/System/SystemTelemetryMonitor.swift +273 -0
  67. package/app/Sources/{HandsOffSession.swift → Core/Voice/HandsOffSession.swift} +15 -4
  68. package/app/Sources/{WorkspaceManager.swift → Core/Workspace/WorkspaceManager.swift} +288 -0
  69. package/bin/assistant-intelligence.ts +874 -0
  70. package/bin/handsoff-infer.ts +16 -209
  71. package/bin/handsoff-worker.ts +45 -258
  72. package/bin/lattices-app.ts +62 -0
  73. package/bin/lattices-dev +4 -0
  74. package/bin/lattices.ts +125 -14
  75. package/docs/agents.md +14 -0
  76. package/docs/api.md +55 -0
  77. package/docs/app.md +3 -0
  78. package/docs/companion-deck.md +180 -0
  79. package/docs/component-extraction-roadmap.md +392 -0
  80. package/docs/config.md +25 -0
  81. package/docs/tiling-reference.md +55 -0
  82. package/docs/voice-error-model.md +73 -0
  83. package/package.json +4 -1
  84. package/app/Sources/App.swift +0 -10
  85. package/app/Sources/CommandPaletteWindow.swift +0 -134
  86. package/app/Sources/MouseFinder.swift +0 -222
  87. /package/app/Sources/{KeyRecorderView.swift → AppShell/KeyRecorderView.swift} +0 -0
  88. /package/app/Sources/{MainWindow.swift → AppShell/MainWindow.swift} +0 -0
  89. /package/app/Sources/{SettingsWindow.swift → AppShell/SettingsWindow.swift} +0 -0
  90. /package/app/Sources/{HotkeyManager.swift → Core/Actions/HotkeyManager.swift} +0 -0
  91. /package/app/Sources/{Intents → Core/Actions/Intents}/CreateLayerIntent.swift +0 -0
  92. /package/app/Sources/{Intents → Core/Actions/Intents}/DistributeIntent.swift +0 -0
  93. /package/app/Sources/{Intents → Core/Actions/Intents}/FocusIntent.swift +0 -0
  94. /package/app/Sources/{Intents → Core/Actions/Intents}/HelpIntent.swift +0 -0
  95. /package/app/Sources/{Intents → Core/Actions/Intents}/KillIntent.swift +0 -0
  96. /package/app/Sources/{Intents → Core/Actions/Intents}/LaunchIntent.swift +0 -0
  97. /package/app/Sources/{Intents → Core/Actions/Intents}/ListSessionsIntent.swift +0 -0
  98. /package/app/Sources/{Intents → Core/Actions/Intents}/ListWindowsIntent.swift +0 -0
  99. /package/app/Sources/{Intents → Core/Actions/Intents}/ScanIntent.swift +0 -0
  100. /package/app/Sources/{Intents → Core/Actions/Intents}/SearchIntent.swift +0 -0
  101. /package/app/Sources/{Intents → Core/Actions/Intents}/SwitchLayerIntent.swift +0 -0
  102. /package/app/Sources/{Intents → Core/Actions/Intents}/TileIntent.swift +0 -0
  103. /package/app/Sources/{DaemonProtocol.swift → Core/Daemon/DaemonProtocol.swift} +0 -0
  104. /package/app/Sources/{DaemonServer.swift → Core/Daemon/DaemonServer.swift} +0 -0
  105. /package/app/Sources/{AccessibilityTextExtractor.swift → Core/Desktop/AccessibilityTextExtractor.swift} +0 -0
  106. /package/app/Sources/{DesktopModelTypes.swift → Core/Desktop/DesktopModelTypes.swift} +0 -0
  107. /package/app/Sources/{InventoryManager.swift → Core/Desktop/InventoryManager.swift} +0 -0
  108. /package/app/Sources/{InventoryPath.swift → Core/Desktop/InventoryPath.swift} +0 -0
  109. /package/app/Sources/{OcrModel.swift → Core/Desktop/OcrModel.swift} +0 -0
  110. /package/app/Sources/{OcrStore.swift → Core/Desktop/OcrStore.swift} +0 -0
  111. /package/app/Sources/{PlacementSpec.swift → Core/Desktop/PlacementSpec.swift} +0 -0
  112. /package/app/Sources/{TilePickerView.swift → Core/Desktop/TilePickerView.swift} +0 -0
  113. /package/app/Sources/{AppWindowShell.swift → Core/Overlays/AppWindowShell.swift} +0 -0
  114. /package/app/Sources/{CommandModeWindow.swift → Core/Overlays/CommandMode/CommandModeWindow.swift} +0 -0
  115. /package/app/Sources/{CommandPaletteView.swift → Core/Overlays/CommandPalette/CommandPaletteView.swift} +0 -0
  116. /package/app/Sources/{HUDBottomBar.swift → Core/Overlays/HUD/HUDBottomBar.swift} +0 -0
  117. /package/app/Sources/{HUDController.swift → Core/Overlays/HUD/HUDController.swift} +0 -0
  118. /package/app/Sources/{HUDLeftBar.swift → Core/Overlays/HUD/HUDLeftBar.swift} +0 -0
  119. /package/app/Sources/{HUDMinimap.swift → Core/Overlays/HUD/HUDMinimap.swift} +0 -0
  120. /package/app/Sources/{HUDState.swift → Core/Overlays/HUD/HUDState.swift} +0 -0
  121. /package/app/Sources/{HUDTopBar.swift → Core/Overlays/HUD/HUDTopBar.swift} +0 -0
  122. /package/app/Sources/{LayerBezel.swift → Core/Overlays/HUD/LayerBezel.swift} +0 -0
  123. /package/app/Sources/{OmniSearchState.swift → Core/Overlays/OmniSearch/OmniSearchState.swift} +0 -0
  124. /package/app/Sources/{DiagnosticLog.swift → Core/System/DiagnosticLog.swift} +0 -0
  125. /package/app/Sources/{EventBus.swift → Core/System/EventBus.swift} +0 -0
  126. /package/app/Sources/{ProcessModel.swift → Core/System/ProcessModel.swift} +0 -0
  127. /package/app/Sources/{ProcessQuery.swift → Core/System/ProcessQuery.swift} +0 -0
  128. /package/app/Sources/{AdvisorLearningStore.swift → Core/Voice/AdvisorLearningStore.swift} +0 -0
  129. /package/app/Sources/{AgentSession.swift → Core/Voice/AgentSession.swift} +0 -0
  130. /package/app/Sources/{AudioProvider.swift → Core/Voice/AudioProvider.swift} +0 -0
  131. /package/app/Sources/{VoiceChatView.swift → Core/Voice/VoiceChatView.swift} +0 -0
  132. /package/app/Sources/{VoxClient.swift → Core/Voice/VoxClient.swift} +0 -0
  133. /package/app/Sources/{Project.swift → Core/Workspace/Project.swift} +0 -0
  134. /package/app/Sources/{ProjectScanner.swift → Core/Workspace/ProjectScanner.swift} +0 -0
  135. /package/app/Sources/{SessionLayerStore.swift → Core/Workspace/SessionLayerStore.swift} +0 -0
  136. /package/app/Sources/{SessionManager.swift → Core/Workspace/SessionManager.swift} +0 -0
  137. /package/app/Sources/{Terminal.swift → Core/Workspace/Terminal/Terminal.swift} +0 -0
  138. /package/app/Sources/{TerminalQuery.swift → Core/Workspace/Terminal/TerminalQuery.swift} +0 -0
  139. /package/app/Sources/{TerminalSynthesizer.swift → Core/Workspace/Terminal/TerminalSynthesizer.swift} +0 -0
  140. /package/app/Sources/{TmuxModel.swift → Core/Workspace/Tmux/TmuxModel.swift} +0 -0
  141. /package/app/Sources/{TmuxQuery.swift → Core/Workspace/Tmux/TmuxQuery.swift} +0 -0
  142. /package/app/Sources/{ActionRow.swift → UI/ActionRow.swift} +0 -0
  143. /package/app/Sources/{OrphanRow.swift → UI/OrphanRow.swift} +0 -0
  144. /package/app/Sources/{ProjectRow.swift → UI/ProjectRow.swift} +0 -0
  145. /package/app/Sources/{TabGroupRow.swift → UI/TabGroupRow.swift} +0 -0
  146. /package/app/Sources/{Theme.swift → UI/Theme.swift} +0 -0
@@ -16,7 +16,7 @@ struct PiChatDock: View {
16
16
  VStack(spacing: 0) {
17
17
  topHandle
18
18
 
19
- if session.isAuthPanelVisible {
19
+ if session.hasPiBinary && session.isAuthPanelVisible {
20
20
  Rectangle()
21
21
  .fill(Palette.border)
22
22
  .frame(height: 0.5)
@@ -34,7 +34,23 @@ struct PiChatDock: View {
34
34
  .fill(Palette.border)
35
35
  .frame(height: 0.5)
36
36
 
37
- composer
37
+ if session.hasPiBinary && !session.needsProviderSetup {
38
+ composer
39
+ } else if session.needsProviderSetup {
40
+ if session.isAuthPanelVisible {
41
+ setupLockedBar
42
+ } else {
43
+ PiProviderSetupCallout(session: session, compact: true)
44
+ .padding(.horizontal, 10)
45
+ .padding(.vertical, 10)
46
+ .background(Color.black.opacity(0.62))
47
+ }
48
+ } else {
49
+ PiInstallCallout(session: session, compact: true)
50
+ .padding(.horizontal, 10)
51
+ .padding(.vertical, 10)
52
+ .background(Color.black.opacity(0.62))
53
+ }
38
54
 
39
55
  Rectangle()
40
56
  .fill(Palette.border)
@@ -59,8 +75,11 @@ struct PiChatDock: View {
59
75
  .strokeBorder(Palette.border, lineWidth: 0.5)
60
76
  )
61
77
  .onAppear {
62
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
63
- composerFocused = true
78
+ session.prepareForDisplay()
79
+ if session.hasPiBinary && !session.needsProviderSetup {
80
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
81
+ composerFocused = true
82
+ }
64
83
  }
65
84
  }
66
85
  }
@@ -113,99 +132,113 @@ struct PiChatDock: View {
113
132
 
114
133
  private var authPanel: some View {
115
134
  VStack(alignment: .leading, spacing: 10) {
116
- HStack(spacing: 8) {
117
- Text("provider")
118
- .font(Typo.geistMonoBold(9))
119
- .foregroundColor(Palette.textMuted)
135
+ if session.isAuthenticating {
136
+ VStack(alignment: .leading, spacing: 4) {
137
+ Text("Finish Setup")
138
+ .font(Typo.geistMonoBold(10))
139
+ .foregroundColor(Palette.text)
120
140
 
121
- Picker("Provider", selection: $session.authProviderID) {
122
- ForEach(session.providerOptions) { provider in
123
- Text(provider.name).tag(provider.id)
124
- }
141
+ Text("Ignore the rest for a second and just do the next step below.")
142
+ .font(Typo.mono(9))
143
+ .foregroundColor(Palette.textDim)
144
+ .fixedSize(horizontal: false, vertical: true)
125
145
  }
126
- .labelsHidden()
127
- .pickerStyle(.menu)
128
- .font(Typo.mono(10))
129
-
130
- Spacer()
131
-
132
- capsuleLabel(
133
- session.currentProvider.authMode == .oauth ? "OAUTH" : "TOKEN",
134
- tint: session.currentProvider.authMode == .oauth ? Palette.detach : Palette.running
135
- )
136
- }
137
146
 
138
- Text(session.currentProvider.helpText)
139
- .font(Typo.mono(10))
140
- .foregroundColor(Palette.textDim)
141
- .fixedSize(horizontal: false, vertical: true)
142
-
143
- if session.currentProvider.authMode == .apiKey {
144
147
  HStack(spacing: 8) {
145
- SecureField(session.currentProvider.tokenPlaceholder, text: $session.authToken)
146
- .textFieldStyle(.plain)
147
- .font(Typo.mono(11))
148
- .foregroundColor(Palette.text)
149
- .focused($authFieldFocused)
150
- .onSubmit {
151
- session.saveSelectedToken()
152
- }
148
+ capsuleLabel(session.currentProvider.name.uppercased(), tint: Palette.text)
149
+ capsuleLabel("IN PROGRESS", tint: Palette.detach)
150
+ Spacer()
151
+ }
153
152
 
154
- footerButton("save") {
155
- session.saveSelectedToken()
156
- }
153
+ if let prompt = session.pendingAuthPrompt {
154
+ PiAuthPromptCard(session: session, prompt: prompt, compact: true, focus: $authFieldFocused)
155
+ } else {
156
+ PiAuthNextStepCard(session: session, compact: true)
157
+ }
158
+ } else {
159
+ if session.needsProviderSetup {
160
+ VStack(alignment: .leading, spacing: 4) {
161
+ Text("Set Up Your AI")
162
+ .font(Typo.geistMonoBold(10))
163
+ .foregroundColor(Palette.text)
157
164
 
158
- if session.hasSelectedCredential {
159
- footerButton("clear") {
160
- session.removeSelectedCredential()
161
- }
165
+ Text("Choose a provider, connect it once, and the chat box unlocks automatically.")
166
+ .font(Typo.mono(9))
167
+ .foregroundColor(Palette.textDim)
168
+ .fixedSize(horizontal: false, vertical: true)
162
169
  }
163
170
  }
164
- .padding(.horizontal, 10)
165
- .padding(.vertical, 8)
166
- .background(authCardBackground(tint: Palette.running))
167
- } else {
171
+
168
172
  HStack(spacing: 8) {
169
- footerButton(session.isAuthenticating ? "cancel" : "login") {
170
- if session.isAuthenticating {
171
- session.cancelAuthFlow()
172
- } else {
173
- session.startSelectedAuthFlow()
174
- }
175
- }
173
+ Text(session.needsProviderSetup ? "choose provider" : "provider")
174
+ .font(Typo.geistMonoBold(9))
175
+ .foregroundColor(Palette.textMuted)
176
176
 
177
- if session.hasSelectedCredential {
178
- footerButton("clear") {
179
- session.removeSelectedCredential()
177
+ Picker("Provider", selection: $session.authProviderID) {
178
+ ForEach(session.providerOptions) { provider in
179
+ Text(provider.name).tag(provider.id)
180
180
  }
181
181
  }
182
+ .labelsHidden()
183
+ .pickerStyle(.menu)
184
+ .font(Typo.mono(10))
185
+
186
+ Spacer()
187
+
188
+ capsuleLabel(
189
+ session.currentProvider.authMode == .oauth ? "OAUTH" : "TOKEN",
190
+ tint: session.currentProvider.authMode == .oauth ? Palette.detach : Palette.running
191
+ )
182
192
  }
183
- .padding(.horizontal, 10)
184
- .padding(.vertical, 8)
185
- .background(authCardBackground(tint: session.isAuthenticating ? Palette.detach : Palette.running))
186
193
 
187
- if let prompt = session.pendingAuthPrompt {
194
+ Text(session.currentProvider.helpText)
195
+ .font(Typo.mono(10))
196
+ .foregroundColor(Palette.textDim)
197
+ .fixedSize(horizontal: false, vertical: true)
198
+
199
+ if session.currentProvider.authMode == .apiKey {
188
200
  HStack(spacing: 8) {
189
- TextField(prompt.placeholder ?? prompt.message, text: $session.authPromptInput)
201
+ SecureField(session.currentProvider.tokenPlaceholder, text: $session.authToken)
190
202
  .textFieldStyle(.plain)
191
203
  .font(Typo.mono(11))
192
204
  .foregroundColor(Palette.text)
193
205
  .focused($authFieldFocused)
194
206
  .onSubmit {
195
- session.submitAuthPrompt()
207
+ session.saveSelectedToken()
196
208
  }
197
209
 
198
- footerButton("continue") {
199
- session.submitAuthPrompt()
210
+ footerButton("save key") {
211
+ session.saveSelectedToken()
212
+ }
213
+
214
+ if session.hasSelectedCredential {
215
+ footerButton("clear") {
216
+ session.removeSelectedCredential()
217
+ }
218
+ }
219
+ }
220
+ .padding(.horizontal, 10)
221
+ .padding(.vertical, 8)
222
+ .background(authCardBackground(tint: Palette.running))
223
+ } else {
224
+ HStack(spacing: 8) {
225
+ footerButton("connect") {
226
+ session.startSelectedAuthFlow()
227
+ }
228
+
229
+ if session.hasSelectedCredential {
230
+ footerButton("clear") {
231
+ session.removeSelectedCredential()
232
+ }
200
233
  }
201
234
  }
202
235
  .padding(.horizontal, 10)
203
236
  .padding(.vertical, 8)
204
- .background(authCardBackground(tint: Palette.detach))
237
+ .background(authCardBackground(tint: Palette.running))
205
238
  }
206
239
  }
207
240
 
208
- if let notice = session.authNoticeText, !notice.isEmpty {
241
+ if !session.isAuthenticating, let notice = session.authNoticeText, !notice.isEmpty {
209
242
  Text(notice)
210
243
  .font(Typo.mono(9))
211
244
  .foregroundColor(Palette.textDim)
@@ -362,8 +395,10 @@ struct PiChatDock: View {
362
395
 
363
396
  Spacer()
364
397
 
365
- footerButton(session.isAuthPanelVisible ? "auth -" : "auth +") {
366
- session.toggleAuthPanel()
398
+ if session.hasPiBinary && !session.needsProviderSetup {
399
+ footerButton(session.isAuthPanelVisible ? "auth -" : "auth +") {
400
+ session.toggleAuthPanel()
401
+ }
367
402
  }
368
403
 
369
404
  footerButton("reset") {
@@ -376,8 +411,8 @@ struct PiChatDock: View {
376
411
  }
377
412
 
378
413
  private var footerStatusText: String {
379
- if session.statusText == "idle" {
380
- return session.currentProvider.name
414
+ if session.statusText == "idle" || session.needsProviderSetup || session.isAuthenticating || !session.hasPiBinary {
415
+ return session.setupStatusSummary
381
416
  }
382
417
  return "\(session.currentProvider.name) · \(session.statusText)"
383
418
  }
@@ -390,6 +425,32 @@ struct PiChatDock: View {
390
425
  }
391
426
  }
392
427
 
428
+ private var setupLockedBar: some View {
429
+ HStack(alignment: .center, spacing: 8) {
430
+ Circle()
431
+ .fill(Palette.detach)
432
+ .frame(width: 6, height: 6)
433
+
434
+ VStack(alignment: .leading, spacing: 2) {
435
+ Text("SETUP IN PROGRESS")
436
+ .font(Typo.geistMonoBold(9))
437
+ .foregroundColor(Palette.text)
438
+
439
+ Text(session.isAuthenticating
440
+ ? "Stay with the setup panel above for now. The chat box unlocks as soon as you finish that step."
441
+ : "Finish the setup panel above to unlock the chat box.")
442
+ .font(Typo.mono(9))
443
+ .foregroundColor(Palette.textDim)
444
+ .fixedSize(horizontal: false, vertical: true)
445
+ }
446
+
447
+ Spacer()
448
+ }
449
+ .padding(.horizontal, 10)
450
+ .padding(.vertical, 10)
451
+ .background(Color.black.opacity(0.62))
452
+ }
453
+
393
454
  private func roleLabel(for role: PiChatMessage.Role) -> String {
394
455
  switch role {
395
456
  case .system: return "system"
@@ -426,11 +487,11 @@ struct PiChatDock: View {
426
487
  )
427
488
  }
428
489
 
429
- private func footerButton(_ label: String, tint: Color = Palette.textMuted, action: @escaping () -> Void) -> some View {
490
+ private func footerButton(_ label: String, tint: Color = Palette.textMuted, disabled: Bool = false, action: @escaping () -> Void) -> some View {
430
491
  Button(label, action: action)
431
492
  .buttonStyle(.plain)
432
493
  .font(Typo.geistMonoBold(9))
433
- .foregroundColor(tint)
494
+ .foregroundColor(disabled ? Palette.textMuted : tint)
434
495
  .padding(.horizontal, 8)
435
496
  .padding(.vertical, 5)
436
497
  .background(
@@ -441,6 +502,8 @@ struct PiChatDock: View {
441
502
  .strokeBorder(Palette.border, lineWidth: 0.5)
442
503
  )
443
504
  )
505
+ .opacity(disabled ? 0.65 : 1)
506
+ .disabled(disabled)
444
507
  }
445
508
 
446
509
  private func authCardBackground(tint: Color) -> some View {