@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
|
@@ -18,7 +18,7 @@ struct PiProviderSetupCallout: View {
|
|
|
18
18
|
|
|
19
19
|
Text(session.isAuthenticating
|
|
20
20
|
? "Finish the setup above. As soon as that one step is done, the chat box unlocks."
|
|
21
|
-
: "
|
|
21
|
+
: "Chat is optional. Connect \(session.currentProvider.name) when you want the in-app assistant.")
|
|
22
22
|
.font(Typo.mono(compact ? 10 : 11))
|
|
23
23
|
.foregroundColor(Palette.textDim)
|
|
24
24
|
.fixedSize(horizontal: false, vertical: true)
|
|
@@ -32,18 +32,18 @@ struct PiProviderSetupCallout: View {
|
|
|
32
32
|
PiAuthNextStepCard(session: session, compact: compact)
|
|
33
33
|
} else {
|
|
34
34
|
Text(session.currentProvider.authMode == .oauth
|
|
35
|
-
? "
|
|
36
|
-
: "
|
|
35
|
+
? "This opens the provider flow in your browser."
|
|
36
|
+
: "Open setup when you are ready to paste and save a key.")
|
|
37
37
|
.font(Typo.mono(compact ? 9 : 10))
|
|
38
38
|
.foregroundColor(Palette.textMuted)
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
if session.currentProvider.authMode == .oauth && !session.isAuthenticating {
|
|
42
42
|
primaryActionButton(
|
|
43
|
-
"
|
|
43
|
+
"SET UP \(session.currentProvider.name.uppercased())",
|
|
44
44
|
tint: Palette.running
|
|
45
45
|
) {
|
|
46
|
-
session.
|
|
46
|
+
session.toggleAuthPanel()
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
}
|
|
@@ -19,14 +19,6 @@ struct PiWorkspaceView: View {
|
|
|
19
19
|
.fill(Palette.border)
|
|
20
20
|
.frame(height: 0.5)
|
|
21
21
|
|
|
22
|
-
if session.hasPiBinary && session.isAuthPanelVisible {
|
|
23
|
-
authPanel
|
|
24
|
-
|
|
25
|
-
Rectangle()
|
|
26
|
-
.fill(Palette.border)
|
|
27
|
-
.frame(height: 0.5)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
22
|
transcript
|
|
31
23
|
|
|
32
24
|
Rectangle()
|
|
@@ -36,14 +28,7 @@ struct PiWorkspaceView: View {
|
|
|
36
28
|
if session.hasPiBinary && !session.needsProviderSetup {
|
|
37
29
|
composer
|
|
38
30
|
} else if session.needsProviderSetup {
|
|
39
|
-
|
|
40
|
-
setupLockedPanel
|
|
41
|
-
} else {
|
|
42
|
-
PiProviderSetupCallout(session: session, compact: false)
|
|
43
|
-
.padding(.horizontal, 16)
|
|
44
|
-
.padding(.vertical, 14)
|
|
45
|
-
.background(Palette.surface.opacity(0.22))
|
|
46
|
-
}
|
|
31
|
+
providerSettingsPrompt
|
|
47
32
|
} else {
|
|
48
33
|
PiInstallCallout(session: session, compact: false)
|
|
49
34
|
.padding(.horizontal, 16)
|
|
@@ -70,7 +55,7 @@ struct PiWorkspaceView: View {
|
|
|
70
55
|
.fill(session.hasPiBinary ? Palette.running : Palette.kill)
|
|
71
56
|
.frame(width: 7, height: 7)
|
|
72
57
|
|
|
73
|
-
Text("WORKSPACE
|
|
58
|
+
Text("WORKSPACE ASSISTANT")
|
|
74
59
|
.font(Typo.geistMonoBold(11))
|
|
75
60
|
.foregroundColor(Palette.text)
|
|
76
61
|
|
|
@@ -89,8 +74,8 @@ struct PiWorkspaceView: View {
|
|
|
89
74
|
? session.authStepDescription
|
|
90
75
|
: (session.needsProviderSetup
|
|
91
76
|
? "Next step: connect a provider to unlock chat."
|
|
92
|
-
: "Full conversation surface for longer prompts,
|
|
93
|
-
: "Install
|
|
77
|
+
: "Full conversation surface for settings, longer prompts, planning, debugging, and second opinions."))
|
|
78
|
+
: "Settings chat is ready here. Install the provider runtime to unlock longer prompts and provider-backed chat.")
|
|
94
79
|
.font(Typo.mono(10))
|
|
95
80
|
.foregroundColor(Palette.textDim)
|
|
96
81
|
}
|
|
@@ -98,13 +83,7 @@ struct PiWorkspaceView: View {
|
|
|
98
83
|
Spacer()
|
|
99
84
|
|
|
100
85
|
HStack(spacing: 6) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if session.hasPiBinary && !session.needsProviderSetup {
|
|
104
|
-
actionChip(session.isAuthPanelVisible ? "AUTH -" : "AUTH +") {
|
|
105
|
-
session.toggleAuthPanel()
|
|
106
|
-
}
|
|
107
|
-
}
|
|
86
|
+
settingsGearButton
|
|
108
87
|
|
|
109
88
|
if session.hasConversationHistory {
|
|
110
89
|
actionChip("RESET") {
|
|
@@ -117,6 +96,51 @@ struct PiWorkspaceView: View {
|
|
|
117
96
|
.padding(.vertical, 14)
|
|
118
97
|
}
|
|
119
98
|
|
|
99
|
+
private var settingsGearButton: some View {
|
|
100
|
+
Button {
|
|
101
|
+
SettingsWindowController.shared.showAssistant()
|
|
102
|
+
} label: {
|
|
103
|
+
Image(systemName: "gearshape")
|
|
104
|
+
.font(.system(size: 11, weight: .semibold))
|
|
105
|
+
.foregroundColor(Palette.textMuted)
|
|
106
|
+
.frame(width: 26, height: 24)
|
|
107
|
+
.background(
|
|
108
|
+
Capsule()
|
|
109
|
+
.fill(Color.white.opacity(0.03))
|
|
110
|
+
.overlay(Capsule().strokeBorder(Palette.border, lineWidth: 0.5))
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
.buttonStyle(.plain)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private var providerSettingsPrompt: some View {
|
|
117
|
+
HStack(alignment: .center, spacing: 10) {
|
|
118
|
+
Circle()
|
|
119
|
+
.fill(Palette.detach)
|
|
120
|
+
.frame(width: 7, height: 7)
|
|
121
|
+
|
|
122
|
+
VStack(alignment: .leading, spacing: 3) {
|
|
123
|
+
Text("CONNECT A PROVIDER")
|
|
124
|
+
.font(Typo.geistMonoBold(10))
|
|
125
|
+
.foregroundColor(Palette.text)
|
|
126
|
+
|
|
127
|
+
Text("Choose OpenAI, Groq, OpenRouter, or MiniMax in Settings to unlock provider-backed chat.")
|
|
128
|
+
.font(Typo.mono(10))
|
|
129
|
+
.foregroundColor(Palette.textDim)
|
|
130
|
+
.fixedSize(horizontal: false, vertical: true)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
Spacer()
|
|
134
|
+
|
|
135
|
+
actionChip("SETTINGS", tint: Palette.running) {
|
|
136
|
+
SettingsWindowController.shared.showAssistant()
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
.padding(.horizontal, 16)
|
|
140
|
+
.padding(.vertical, 14)
|
|
141
|
+
.background(Palette.surface.opacity(0.22))
|
|
142
|
+
}
|
|
143
|
+
|
|
120
144
|
private var authPanel: some View {
|
|
121
145
|
VStack(alignment: .leading, spacing: 12) {
|
|
122
146
|
if session.isAuthenticating {
|
|
@@ -184,29 +208,54 @@ struct PiWorkspaceView: View {
|
|
|
184
208
|
.fixedSize(horizontal: false, vertical: true)
|
|
185
209
|
|
|
186
210
|
if session.currentProvider.authMode == .apiKey {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
.focused($authFieldFocused)
|
|
193
|
-
.onSubmit {
|
|
194
|
-
session.saveSelectedToken()
|
|
195
|
-
}
|
|
211
|
+
if session.hasSelectedCredential && !session.isEditingStoredCredential {
|
|
212
|
+
HStack(spacing: 8) {
|
|
213
|
+
Circle()
|
|
214
|
+
.fill(Palette.running)
|
|
215
|
+
.frame(width: 6, height: 6)
|
|
196
216
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
217
|
+
Text("\(session.currentProvider.name) credential saved")
|
|
218
|
+
.font(Typo.mono(10))
|
|
219
|
+
.foregroundColor(Palette.textDim)
|
|
220
|
+
|
|
221
|
+
Spacer()
|
|
222
|
+
|
|
223
|
+
actionChip("REPLACE") {
|
|
224
|
+
session.beginReplacingSelectedCredential()
|
|
225
|
+
}
|
|
200
226
|
|
|
201
|
-
if session.hasSelectedCredential {
|
|
202
227
|
actionChip("CLEAR") {
|
|
203
228
|
session.removeSelectedCredential()
|
|
204
229
|
}
|
|
205
230
|
}
|
|
231
|
+
.padding(.horizontal, 12)
|
|
232
|
+
.padding(.vertical, 10)
|
|
233
|
+
.background(authCardBackground(tint: Palette.running))
|
|
234
|
+
} else {
|
|
235
|
+
HStack(spacing: 8) {
|
|
236
|
+
SecureField(session.currentProvider.tokenPlaceholder, text: $session.authToken)
|
|
237
|
+
.textFieldStyle(.plain)
|
|
238
|
+
.font(Typo.mono(11))
|
|
239
|
+
.foregroundColor(Palette.text)
|
|
240
|
+
.focused($authFieldFocused)
|
|
241
|
+
.onSubmit {
|
|
242
|
+
session.saveSelectedToken()
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
actionChip("SAVE KEY") {
|
|
246
|
+
session.saveSelectedToken()
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if session.hasSelectedCredential {
|
|
250
|
+
actionChip("CANCEL") {
|
|
251
|
+
session.cancelReplacingSelectedCredential()
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
.padding(.horizontal, 12)
|
|
256
|
+
.padding(.vertical, 10)
|
|
257
|
+
.background(authCardBackground(tint: Palette.running))
|
|
206
258
|
}
|
|
207
|
-
.padding(.horizontal, 12)
|
|
208
|
-
.padding(.vertical, 10)
|
|
209
|
-
.background(authCardBackground(tint: Palette.running))
|
|
210
259
|
} else {
|
|
211
260
|
HStack(spacing: 8) {
|
|
212
261
|
actionChip("CONNECT") {
|
|
@@ -343,7 +392,7 @@ struct PiWorkspaceView: View {
|
|
|
343
392
|
.font(Typo.geistMonoBold(12))
|
|
344
393
|
.foregroundColor(Palette.running)
|
|
345
394
|
|
|
346
|
-
TextField("Ask
|
|
395
|
+
TextField("Ask about settings or planning...", text: $session.draft, axis: .vertical)
|
|
347
396
|
.textFieldStyle(.plain)
|
|
348
397
|
.font(Typo.mono(12))
|
|
349
398
|
.foregroundColor(Palette.text)
|
|
@@ -395,7 +444,7 @@ struct PiWorkspaceView: View {
|
|
|
395
444
|
switch role {
|
|
396
445
|
case .system: return "system"
|
|
397
446
|
case .user: return "you"
|
|
398
|
-
case .assistant: return "
|
|
447
|
+
case .assistant: return "assistant"
|
|
399
448
|
}
|
|
400
449
|
}
|
|
401
450
|
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import Foundation
|
|
3
|
+
|
|
4
|
+
/// An OS permission Lattices can ask for. The Permissions Assistant introduces
|
|
5
|
+
/// these and only requests the underlying grant when the user explicitly opts
|
|
6
|
+
/// in. Product configuration like Pi/provider auth is intentionally excluded —
|
|
7
|
+
/// that lives in the Chat/Pi surface, not here.
|
|
8
|
+
enum Capability: String, CaseIterable, Identifiable {
|
|
9
|
+
case windowControl // macOS Accessibility — tiling, focus, snap
|
|
10
|
+
case screenSearch // macOS Screen Recording — OCR / on-screen text search
|
|
11
|
+
|
|
12
|
+
var id: String { rawValue }
|
|
13
|
+
|
|
14
|
+
var title: String {
|
|
15
|
+
switch self {
|
|
16
|
+
case .windowControl: return "Tiling & focus"
|
|
17
|
+
case .screenSearch: return "Screen text search"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
var iconName: String {
|
|
22
|
+
switch self {
|
|
23
|
+
case .windowControl: return "rectangle.3.group"
|
|
24
|
+
case .screenSearch: return "text.viewfinder"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
var requirementLabel: String {
|
|
29
|
+
switch self {
|
|
30
|
+
case .windowControl: return "Accessibility"
|
|
31
|
+
case .screenSearch: return "Screen & System Audio Recording"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
var pitch: String {
|
|
36
|
+
switch self {
|
|
37
|
+
case .windowControl:
|
|
38
|
+
return "Move, resize, snap, and arrange windows from the menu bar, command palette, and gestures."
|
|
39
|
+
case .screenSearch:
|
|
40
|
+
return "Index on-screen text with OCR so the omni search can jump to any window by what it shows."
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
var why: String {
|
|
45
|
+
switch self {
|
|
46
|
+
case .windowControl:
|
|
47
|
+
return "macOS Accessibility lets Lattices read window titles and move or resize windows. No keystrokes are recorded."
|
|
48
|
+
case .screenSearch:
|
|
49
|
+
return "Screen Recording lets Lattices read pixels to OCR what is on-screen. Captures stay on this Mac."
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// Optional one-liner shown when the capability is on, summarising current behavior.
|
|
54
|
+
var whenGrantedDetail: String {
|
|
55
|
+
switch self {
|
|
56
|
+
case .windowControl: return "Lattices can move and tile windows."
|
|
57
|
+
case .screenSearch: return "OCR can index visible windows for omni search."
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// Live status — read directly from the system, never cached.
|
|
62
|
+
var isGranted: Bool {
|
|
63
|
+
switch self {
|
|
64
|
+
case .windowControl: return PermissionChecker.shared.accessibility
|
|
65
|
+
case .screenSearch: return PermissionChecker.shared.screenRecording
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/// All capabilities that are not yet granted.
|
|
70
|
+
static var missing: [Capability] {
|
|
71
|
+
Capability.allCases.filter { !$0.isGranted }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// Capabilities that are missing AND have not been dismissed-for-now.
|
|
75
|
+
static var visiblyMissing: [Capability] {
|
|
76
|
+
let dismissed = Preferences.shared.dismissedCapabilities
|
|
77
|
+
return missing.filter { !dismissed.contains($0.rawValue) }
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -14,12 +14,20 @@ final class PermissionChecker: ObservableObject {
|
|
|
14
14
|
|
|
15
15
|
var allGranted: Bool { accessibility && screenRecording }
|
|
16
16
|
|
|
17
|
+
var isSimulatingMissingPermissions: Bool {
|
|
18
|
+
CommandLine.arguments.contains("--simulate-missing-permissions")
|
|
19
|
+
|| UserDefaults.standard.bool(forKey: "permissions.simulateMissing")
|
|
20
|
+
}
|
|
21
|
+
|
|
17
22
|
/// Check current permission state without prompting.
|
|
18
|
-
func check() {
|
|
23
|
+
func check(pollIfMissing: Bool = false) {
|
|
19
24
|
let diag = DiagnosticLog.shared
|
|
20
25
|
|
|
21
|
-
let
|
|
22
|
-
let
|
|
26
|
+
let realAX = AXIsProcessTrusted()
|
|
27
|
+
let realSR = CGPreflightScreenCaptureAccess()
|
|
28
|
+
let simulating = isSimulatingMissingPermissions
|
|
29
|
+
let ax = simulating ? false : realAX
|
|
30
|
+
let sr = simulating ? false : realSR
|
|
23
31
|
|
|
24
32
|
// First check: log identity info only
|
|
25
33
|
if !hasLoggedInitial {
|
|
@@ -29,8 +37,11 @@ final class PermissionChecker: ObservableObject {
|
|
|
29
37
|
let pid = ProcessInfo.processInfo.processIdentifier
|
|
30
38
|
diag.info("PermissionChecker: bundleId=\(bundleId) pid=\(pid)")
|
|
31
39
|
diag.info("PermissionChecker: exec=\(execPath)")
|
|
32
|
-
diag.info("AXIsProcessTrusted() → \(
|
|
33
|
-
diag.info("CGPreflightScreenCaptureAccess() → \(
|
|
40
|
+
diag.info("AXIsProcessTrusted() → \(realAX)")
|
|
41
|
+
diag.info("CGPreflightScreenCaptureAccess() → \(realSR)")
|
|
42
|
+
if simulating {
|
|
43
|
+
diag.warn("PermissionChecker: simulating missing permissions for UX preview")
|
|
44
|
+
}
|
|
34
45
|
}
|
|
35
46
|
|
|
36
47
|
// Log on state changes
|
|
@@ -41,11 +52,11 @@ final class PermissionChecker: ObservableObject {
|
|
|
41
52
|
accessibility = ax
|
|
42
53
|
screenRecording = sr
|
|
43
54
|
|
|
44
|
-
//
|
|
45
|
-
//
|
|
55
|
+
// Only poll after an intentional permission request. A passive launch-time
|
|
56
|
+
// check should not keep nudging macOS privacy state in the background.
|
|
46
57
|
if allGranted {
|
|
47
58
|
stopPolling()
|
|
48
|
-
} else {
|
|
59
|
+
} else if pollIfMissing {
|
|
49
60
|
startPolling()
|
|
50
61
|
}
|
|
51
62
|
}
|
|
@@ -54,6 +65,11 @@ final class PermissionChecker: ObservableObject {
|
|
|
54
65
|
/// which adds lattices to the Accessibility list and asks the user to toggle it on.
|
|
55
66
|
func requestAccessibility() {
|
|
56
67
|
let diag = DiagnosticLog.shared
|
|
68
|
+
if isSimulatingMissingPermissions {
|
|
69
|
+
diag.warn("requestAccessibility: skipped because missing-permission simulation is enabled")
|
|
70
|
+
accessibility = false
|
|
71
|
+
return
|
|
72
|
+
}
|
|
57
73
|
let beforeCheck = AXIsProcessTrusted()
|
|
58
74
|
diag.info("requestAccessibility: before=\(beforeCheck), prompting…")
|
|
59
75
|
let opts = [kAXTrustedCheckOptionPrompt.takeUnretainedValue(): true] as CFDictionary
|
|
@@ -71,6 +87,11 @@ final class PermissionChecker: ObservableObject {
|
|
|
71
87
|
/// which adds lattices to the Screen Recording list. The user toggles it on in Settings.
|
|
72
88
|
func requestScreenRecording() {
|
|
73
89
|
let diag = DiagnosticLog.shared
|
|
90
|
+
if isSimulatingMissingPermissions {
|
|
91
|
+
diag.warn("requestScreenRecording: skipped because missing-permission simulation is enabled")
|
|
92
|
+
screenRecording = false
|
|
93
|
+
return
|
|
94
|
+
}
|
|
74
95
|
let beforeCheck = CGPreflightScreenCaptureAccess()
|
|
75
96
|
diag.info("requestScreenRecording: before=\(beforeCheck), probing…")
|
|
76
97
|
|
|
@@ -131,6 +152,20 @@ final class PermissionChecker: ObservableObject {
|
|
|
131
152
|
}
|
|
132
153
|
}
|
|
133
154
|
|
|
155
|
+
/// Opens System Settings → Privacy & Security → Automation.
|
|
156
|
+
func openAutomationSettings() {
|
|
157
|
+
if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Automation") {
|
|
158
|
+
NSWorkspace.shared.open(url)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/// Opens System Settings → Privacy & Security → Input Monitoring.
|
|
163
|
+
func openInputMonitoringSettings() {
|
|
164
|
+
if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_ListenEvent") {
|
|
165
|
+
NSWorkspace.shared.open(url)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
134
169
|
@available(macOS 15.0, *)
|
|
135
170
|
private func probeScreenCaptureShareableContent() async -> String {
|
|
136
171
|
await withCheckedContinuation { continuation in
|