@lattices/cli 0.4.9 → 0.4.11
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/DesktopModel.swift +1 -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 +2259 -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 +46 -27
- package/{app → apps/mac}/Sources/Core/Overlays/ScreenMap/ScreenMapView.swift +580 -162
- package/apps/mac/Sources/Core/Overlays/ScreenOverlayCanvasController.swift +1240 -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/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/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
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Repository Structure
|
|
2
|
+
|
|
3
|
+
Lattices is a small project with several real product surfaces. The root should
|
|
4
|
+
make those surfaces obvious.
|
|
5
|
+
|
|
6
|
+
This document is the current maintainer-facing map and the proposed direction
|
|
7
|
+
for keeping file structure as architecture.
|
|
8
|
+
|
|
9
|
+
## Current Top-Level Areas
|
|
10
|
+
|
|
11
|
+
| Path | Role |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| `apps/mac/` | Native macOS menu bar app. Swift/AppKit/SwiftUI package. |
|
|
14
|
+
| `bin/` | Published TypeScript CLI and app helper entry points. |
|
|
15
|
+
| `swift/` | Shared Swift package code used by the app. |
|
|
16
|
+
| `apps/ios/` | iOS companion app experiments and local build state. |
|
|
17
|
+
| `apps/site/` | Public marketing site and blog content. |
|
|
18
|
+
| `apps/docs-site/` | Astro documentation site. |
|
|
19
|
+
| `docs/` | Markdown docs and engineering proposals. |
|
|
20
|
+
| `tools/agents/skills/` | Agent skill pack for driving Lattices. |
|
|
21
|
+
| `assets/` | Shared release/app assets. |
|
|
22
|
+
| `tools/release/` | Maintainer scripts for building and shipping. |
|
|
23
|
+
| `tests/` | CLI, daemon, and evaluation tests. |
|
|
24
|
+
|
|
25
|
+
## Problem
|
|
26
|
+
|
|
27
|
+
The root currently mixes categories:
|
|
28
|
+
|
|
29
|
+
- shipped product surfaces: `apps/mac/`, `bin/`, `swift/`
|
|
30
|
+
- websites: `apps/site/`, `apps/docs-site/`
|
|
31
|
+
- companion experiments: `apps/ios/`
|
|
32
|
+
- generated or release output: `dist/`
|
|
33
|
+
- maintainer and agent affordances: `docs/`, `tools/`, `tests/`
|
|
34
|
+
|
|
35
|
+
That makes the project feel larger than it is. It also makes it harder to see
|
|
36
|
+
which directories are architecture and which are support material.
|
|
37
|
+
|
|
38
|
+
## Target Shape
|
|
39
|
+
|
|
40
|
+
Do not reorganize everything at once. The target is:
|
|
41
|
+
|
|
42
|
+
```text
|
|
43
|
+
apps/
|
|
44
|
+
mac/ # macOS menu bar app
|
|
45
|
+
ios/ # iOS companion app
|
|
46
|
+
site/ # marketing site and blog content
|
|
47
|
+
docs-site/ # documentation site
|
|
48
|
+
|
|
49
|
+
packages/
|
|
50
|
+
cli/ # current bin/ plus TypeScript package surface
|
|
51
|
+
swift/ # current swift/
|
|
52
|
+
|
|
53
|
+
docs/
|
|
54
|
+
proposals/
|
|
55
|
+
|
|
56
|
+
tools/
|
|
57
|
+
release/ # release/build scripts
|
|
58
|
+
agents/skills/ # agent skill pack
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
This is intentionally similar to the `apps/` and `packages/` split used by
|
|
62
|
+
small monorepos such as Flue, but adapted for Lattices' macOS app plus CLI
|
|
63
|
+
shape.
|
|
64
|
+
|
|
65
|
+
## Migration Rules
|
|
66
|
+
|
|
67
|
+
- Move one category at a time.
|
|
68
|
+
- Keep published npm entry points stable.
|
|
69
|
+
- Keep app bundle and release scripts working after each move.
|
|
70
|
+
- Update docs and agent instructions in the same PR as any move.
|
|
71
|
+
- Avoid renames that only satisfy aesthetics without reducing ambiguity.
|
|
72
|
+
- Keep generated output ignored and out of the architectural map.
|
|
73
|
+
|
|
74
|
+
## Near-Term Cleanup
|
|
75
|
+
|
|
76
|
+
Good first moves:
|
|
77
|
+
|
|
78
|
+
1. Treat `dist/` as generated output only.
|
|
79
|
+
2. Move blog content closer to the site that owns it, or explicitly document it
|
|
80
|
+
as shared content.
|
|
81
|
+
3. Decide whether the iOS companion remains in this repo or moves to its own
|
|
82
|
+
repo once the companion work becomes active again.
|
|
83
|
+
4. Split `docs/proposals/` for numbered engineering docs such as `LAT-001`.
|
|
84
|
+
5. Decide whether `bin/` remains the package root for the CLI or becomes
|
|
85
|
+
`packages/cli/src/` before adding more exported modules.
|
|
86
|
+
|
|
87
|
+
## What Stays Boring
|
|
88
|
+
|
|
89
|
+
Root files should be few and intentional:
|
|
90
|
+
|
|
91
|
+
- `README.md`
|
|
92
|
+
- `AGENTS.md`
|
|
93
|
+
- `package.json`
|
|
94
|
+
- lockfile
|
|
95
|
+
- TypeScript config
|
|
96
|
+
- license/security/contribution docs
|
|
97
|
+
- release/install affordances
|
|
98
|
+
|
|
99
|
+
Everything else should either be a product surface, a package, docs, content,
|
|
100
|
+
or tooling.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Goal + anchors
|
|
4
4
|
|
|
5
|
-
Use one error vocabulary for Mac voice capture/execution and iPad relay/status. The canonical protocol says Lattices borrows Vox for capture and never owns the mic directly (`docs/voice-command-protocol.md:5-7`), but the shared runtime already has a cross-platform `DeckVoiceState` slot (`swift/Sources/DeckKit/DeckRuntimeSnapshot.swift:49-68`). Current Mac code exposes local strings (`VoxError`, `executionResult`) instead of structured errors (`
|
|
5
|
+
Use one error vocabulary for Mac voice capture/execution and iPad relay/status. The canonical protocol says Lattices borrows Vox for capture and never owns the mic directly (`docs/voice-command-protocol.md:5-7`), but the shared runtime already has a cross-platform `DeckVoiceState` slot (`swift/Sources/DeckKit/DeckRuntimeSnapshot.swift:49-68`). Current Mac code exposes local strings (`VoxError`, `executionResult`) instead of structured errors (`apps/mac/Sources/VoxClient.swift:43-59`, `apps/mac/Sources/AudioProvider.swift:343-349`); iPad has only a generic `errorMessage` (`apps/ios/Sources/DeckStore.swift:18`). Normalize at DeckKit, then let each surface render the same object.
|
|
6
6
|
|
|
7
7
|
## Error structure
|
|
8
8
|
|
|
@@ -48,21 +48,21 @@ Copy examples: `Mic in use by Vox — finish memo first`, `No target window`, `V
|
|
|
48
48
|
|
|
49
49
|
## Presentation patterns
|
|
50
50
|
|
|
51
|
-
**Mac VoiceCommandWindow.** Keep the three-column cockpit. The top mic bar already owns live state (`connecting...`, `processing...`; `
|
|
51
|
+
**Mac VoiceCommandWindow.** Keep the three-column cockpit. The top mic bar already owns live state (`connecting...`, `processing...`; `apps/mac/Sources/VoiceCommandWindow.swift:692-719`); render the active error as a compact red/amber status chip there. The center column uses `commandSection` cards (`apps/mac/Sources/VoiceCommandWindow.swift:1287-1304`): show a single `blocked`/`needs action` card only when the user can do something. The footer already has key chips (`apps/mac/Sources/VoiceCommandWindow.swift:1308-1348`); replace the generic command list with contextual remediation: `⌥ Retry`, `Return Open Vox`, `⌘, Permissions`. Logs stay in the right rail, using existing level colors (`apps/mac/Sources/VoiceCommandWindow.swift:1112-1150`).
|
|
52
52
|
|
|
53
|
-
**Mac HUD.** `HUDTopBar.voiceStatus` already has dot, label, transcript, response (`
|
|
53
|
+
**Mac HUD.** `HUDTopBar.voiceStatus` already has dot, label, transcript, response (`apps/mac/Sources/HUDTopBar.swift:134-198`). Add severity tint: green idle/listening, amber connecting/recoverable, red blocked. For active voice errors, HUD shows a one-line banner in the top bar; no sheet.
|
|
54
54
|
|
|
55
|
-
**iPad Home.** Add `HomeVoiceOverlay` as the full voice modal for active relay: title row `VOICE`, phase, transcript, Mac owner, and one remediation button. The bottom bar already has dense status slots and `hold·space` (`
|
|
55
|
+
**iPad Home.** Add `HomeVoiceOverlay` as the full voice modal for active relay: title row `VOICE`, phase, transcript, Mac owner, and one remediation button. The bottom bar already has dense status slots and `hold·space` (`apps/ios/Sources/Home/HomeBottomBar.swift:58-68`, `apps/ios/Sources/Home/HomeBottomBar.swift:129-148`); render idle/recoverable errors inline there (`voice · reconnecting`, `voice · Vox offline`). Use a deck overlay banner only when an issued iPad action failed. Use sheets only for permissions/pairing because they need human action. This follows the chrome rule: do not remove noisy UI; replace it with state that answers “what am I controlling, who is listening, what failed?” (`/Users/arach/.claude/projects/-Users-arach-dev-lattices/memory/feedback_chrome_design.md:11-13`).
|
|
56
56
|
|
|
57
57
|
## Unhappy-path prescriptions
|
|
58
58
|
|
|
59
|
-
**Launch Vox on demand.** Spec flow is detect installed/not running, open Vox, show `Starting Vox...`, wait up to 10s, retry `startDictation`, then fail with manual-open copy (`docs/voice-command-protocol.md:73-89`). Current Mac waits 2s after `connect()` (`
|
|
59
|
+
**Launch Vox on demand.** Spec flow is detect installed/not running, open Vox, show `Starting Vox...`, wait up to 10s, retry `startDictation`, then fail with manual-open copy (`docs/voice-command-protocol.md:73-89`). Current Mac waits 2s after `connect()` (`apps/mac/Sources/VoiceCommandWindow.swift:290-313`); design target is `vox_not_running` → `vox_loading` → retry → either clear error or `vox_unreachable` with `openVox`.
|
|
60
60
|
|
|
61
61
|
**Mic busy.** Preserve owner attribution from protocol (`docs/voice-command-protocol.md:127-135`). `mic_busy(owner: "Vox")` is warning, recoverable, retry hint `userAction`; message: `Mic in use by Vox — finish memo first`. If owner is unknown: `Mic busy — wait for current recording`.
|
|
62
62
|
|
|
63
63
|
**Connection recovery.** If idle, reconnect silently and write log only. If active, show red `Connection lost`; do not auto-retry captured audio because Vox cancels dropped sockets (`docs/voice-command-protocol.md:174-188`). iPad shows `Mac voice link lost` if bridge lost, not `network` unless the iPad transport failed.
|
|
64
64
|
|
|
65
|
-
**JSONL.** Add `~/.lattices/voice.jsonl` beside `lattices.log` (current log path is `~/.lattices/lattices.log`; `
|
|
65
|
+
**JSONL.** Add `~/.lattices/voice.jsonl` beside `lattices.log` (current log path is `~/.lattices/lattices.log`; `apps/mac/Sources/DiagnosticLog.swift:40-59`). Shape:
|
|
66
66
|
|
|
67
67
|
```json
|
|
68
68
|
{"ts":"2026-04-27T14:03:11.120Z","platform":"mac","sessionId":"...","phase":"listening","event":"error","error":{"code":"mic_busy","severity":"warning","recoverable":true,"source":"vox","owner":"Vox","message":"Mic in use by Vox — finish memo first"},"transcript":null,"intent":null,"durationMs":820}
|
|
@@ -70,4 +70,4 @@ Copy examples: `Mic in use by Vox — finish memo first`, `No target window`, `V
|
|
|
70
70
|
|
|
71
71
|
## Cross-platform conventions
|
|
72
72
|
|
|
73
|
-
Tone: terse cockpit, no apologies. Prefer noun-state-action: `Vox offline — starting`, `No target — pick window`, `Access denied — enable Accessibility`. Tint maps to existing palettes: Mac `Palette.detach` amber and `Palette.kill` red (`
|
|
73
|
+
Tone: terse cockpit, no apologies. Prefer noun-state-action: `Vox offline — starting`, `No target — pick window`, `Access denied — enable Accessibility`. Tint maps to existing palettes: Mac `Palette.detach` amber and `Palette.kill` red (`apps/mac/Sources/Theme.swift:19-23`); iPad `LatsPalette.amber/red` (`apps/ios/Sources/LatsDeckScreen.swift:19-25`). Icons: `mic.fill` live, `mic.slash` denied, `waveform.badge.exclamationmark` transcription, `bolt.trianglebadge.exclamationmark` execution, `wifi.exclamationmark` connection, `scope` target. Ownership: Mac owns Vox, mic, Accessibility, intent execution, and JSONL. iPad owns relay/bridge/network presentation and never claims direct mic capture.
|
package/docs/voice.md
CHANGED
|
@@ -134,6 +134,24 @@ reaches 75% of the model's limit, the session auto-resets.
|
|
|
134
134
|
|
|
135
135
|
Context usage and session cost are shown in the AI corner header.
|
|
136
136
|
|
|
137
|
+
## Hands-off inference
|
|
138
|
+
|
|
139
|
+
Hands-off voice uses the shared inference wrapper in `bin/infer.ts`.
|
|
140
|
+
By default it chooses the lowest-latency configured provider and, when
|
|
141
|
+
Groq credentials are present, uses `groq/llama-3.1-8b-instant`.
|
|
142
|
+
|
|
143
|
+
Credentials are read from process env, `.env.local`, `.env`,
|
|
144
|
+
`~/.lattices/inference.json`, then `~/.config/speakeasy/settings.json`.
|
|
145
|
+
For Groq, either `GROQ_API_KEY` or the common typo `GROK_API_KEY` works
|
|
146
|
+
when the key has Groq's `gsk_` prefix.
|
|
147
|
+
|
|
148
|
+
Override the voice engine if needed:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
LATTICES_VOICE_PROVIDER=groq
|
|
152
|
+
LATTICES_VOICE_MODEL=llama-3.1-8b-instant
|
|
153
|
+
```
|
|
154
|
+
|
|
137
155
|
## Configuration
|
|
138
156
|
|
|
139
157
|
Open **Settings > AI** to configure:
|
package/package.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lattices/cli",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.4.11",
|
|
4
|
+
"description": "The agentic workspace manager for macOS — turn your desktop into a coherent API",
|
|
5
|
+
"packageManager": "bun@1.3.11",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=18"
|
|
8
|
+
},
|
|
5
9
|
"bin": {
|
|
6
10
|
"lattices": "./bin/lattices.ts",
|
|
7
11
|
"lattices-app": "./bin/lattices-app.ts"
|
|
@@ -27,10 +31,13 @@
|
|
|
27
31
|
"./project-twin": "./bin/project-twin.ts"
|
|
28
32
|
},
|
|
29
33
|
"scripts": {
|
|
30
|
-
"dev": "bun --cwd docs-site dev",
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
+
"dev": "bun --cwd apps/docs-site dev",
|
|
35
|
+
"check": "bun run check:types && bun run check:app",
|
|
36
|
+
"check:types": "tsc --noEmit",
|
|
37
|
+
"check:app": "env CLANG_MODULE_CACHE_PATH=/tmp/lattices-clang-cache SWIFTPM_TESTS_MODULECACHE=/tmp/lattices-swiftpm-cache swift build --package-path apps/mac",
|
|
38
|
+
"test:e2e": "node --experimental-strip-types --test tests/e2e-daemon.test.mjs",
|
|
39
|
+
"test:e2e:voice": "node --experimental-strip-types tests/eval-voice.js",
|
|
40
|
+
"typecheck": "bun run check:types",
|
|
34
41
|
"build:app-bundle": "bash ./bin/lattices-dev build",
|
|
35
42
|
"prepack": "bash ./bin/lattices-dev build"
|
|
36
43
|
},
|
|
@@ -38,13 +45,16 @@
|
|
|
38
45
|
"os": ["darwin"],
|
|
39
46
|
"files": [
|
|
40
47
|
"bin",
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
+
"apps/mac/Info.plist",
|
|
49
|
+
"apps/mac/Lattices.app",
|
|
50
|
+
"apps/mac/Lattices.entitlements",
|
|
51
|
+
"apps/mac/Package.swift",
|
|
52
|
+
"apps/mac/Resources",
|
|
53
|
+
"apps/mac/Sources",
|
|
54
|
+
"apps/mac/Tests",
|
|
55
|
+
"swift/Package.swift",
|
|
56
|
+
"swift/Sources",
|
|
57
|
+
"swift/Tests",
|
|
48
58
|
"assets/AppIcon.icns",
|
|
49
59
|
"docs"
|
|
50
60
|
],
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// swift-tools-version: 5.9
|
|
2
|
+
import PackageDescription
|
|
3
|
+
|
|
4
|
+
let package = Package(
|
|
5
|
+
name: "DeckKit",
|
|
6
|
+
platforms: [
|
|
7
|
+
.macOS(.v13),
|
|
8
|
+
.iOS(.v17)
|
|
9
|
+
],
|
|
10
|
+
products: [
|
|
11
|
+
.library(name: "DeckKit", targets: ["DeckKit"])
|
|
12
|
+
],
|
|
13
|
+
targets: [
|
|
14
|
+
.target(name: "DeckKit"),
|
|
15
|
+
.testTarget(
|
|
16
|
+
name: "DeckKitTests",
|
|
17
|
+
dependencies: ["DeckKit"]
|
|
18
|
+
)
|
|
19
|
+
]
|
|
20
|
+
)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
public struct DeckActionRequest: Codable, Equatable, Sendable {
|
|
4
|
+
public var pageID: String?
|
|
5
|
+
public var actionID: String
|
|
6
|
+
public var payload: [String: DeckValue]
|
|
7
|
+
|
|
8
|
+
public init(
|
|
9
|
+
pageID: String? = nil,
|
|
10
|
+
actionID: String,
|
|
11
|
+
payload: [String: DeckValue] = [:]
|
|
12
|
+
) {
|
|
13
|
+
self.pageID = pageID
|
|
14
|
+
self.actionID = actionID
|
|
15
|
+
self.payload = payload
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public struct DeckActionResult: Codable, Equatable, Sendable {
|
|
20
|
+
public var ok: Bool
|
|
21
|
+
public var summary: String
|
|
22
|
+
public var detail: String?
|
|
23
|
+
public var runtimeSnapshot: DeckRuntimeSnapshot?
|
|
24
|
+
public var suggestedActions: [DeckSuggestedAction]
|
|
25
|
+
|
|
26
|
+
public init(
|
|
27
|
+
ok: Bool,
|
|
28
|
+
summary: String,
|
|
29
|
+
detail: String? = nil,
|
|
30
|
+
runtimeSnapshot: DeckRuntimeSnapshot? = nil,
|
|
31
|
+
suggestedActions: [DeckSuggestedAction] = []
|
|
32
|
+
) {
|
|
33
|
+
self.ok = ok
|
|
34
|
+
self.summary = summary
|
|
35
|
+
self.detail = detail
|
|
36
|
+
self.runtimeSnapshot = runtimeSnapshot
|
|
37
|
+
self.suggestedActions = suggestedActions
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public struct DeckSuggestedAction: Codable, Equatable, Identifiable, Sendable {
|
|
42
|
+
public var id: String
|
|
43
|
+
public var title: String
|
|
44
|
+
public var iconSystemName: String?
|
|
45
|
+
|
|
46
|
+
public init(id: String, title: String, iconSystemName: String? = nil) {
|
|
47
|
+
self.id = id
|
|
48
|
+
self.title = title
|
|
49
|
+
self.iconSystemName = iconSystemName
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
public struct DeckPairingRequest: Codable, Equatable, Sendable {
|
|
4
|
+
public var deviceID: String
|
|
5
|
+
public var deviceName: String
|
|
6
|
+
public var devicePublicKey: String
|
|
7
|
+
public var platform: String
|
|
8
|
+
public var appVersion: String?
|
|
9
|
+
public var requestedCapabilities: [String]
|
|
10
|
+
|
|
11
|
+
public init(
|
|
12
|
+
deviceID: String,
|
|
13
|
+
deviceName: String,
|
|
14
|
+
devicePublicKey: String,
|
|
15
|
+
platform: String,
|
|
16
|
+
appVersion: String? = nil,
|
|
17
|
+
requestedCapabilities: [String] = DeckBridgeCapability.defaultCompanionCapabilities
|
|
18
|
+
) {
|
|
19
|
+
self.deviceID = deviceID
|
|
20
|
+
self.deviceName = deviceName
|
|
21
|
+
self.devicePublicKey = devicePublicKey
|
|
22
|
+
self.platform = platform
|
|
23
|
+
self.appVersion = appVersion
|
|
24
|
+
self.requestedCapabilities = requestedCapabilities
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private enum CodingKeys: String, CodingKey {
|
|
28
|
+
case deviceID
|
|
29
|
+
case deviceName
|
|
30
|
+
case devicePublicKey
|
|
31
|
+
case platform
|
|
32
|
+
case appVersion
|
|
33
|
+
case requestedCapabilities
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public init(from decoder: Decoder) throws {
|
|
37
|
+
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
38
|
+
deviceID = try container.decode(String.self, forKey: .deviceID)
|
|
39
|
+
deviceName = try container.decode(String.self, forKey: .deviceName)
|
|
40
|
+
devicePublicKey = try container.decode(String.self, forKey: .devicePublicKey)
|
|
41
|
+
platform = try container.decode(String.self, forKey: .platform)
|
|
42
|
+
appVersion = try container.decodeIfPresent(String.self, forKey: .appVersion)
|
|
43
|
+
requestedCapabilities = try container.decodeIfPresent([String].self, forKey: .requestedCapabilities)
|
|
44
|
+
?? DeckBridgeCapability.defaultCompanionCapabilities
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public struct DeckPairingResponse: Codable, Equatable, Sendable {
|
|
49
|
+
public var disposition: DeckPairingDisposition
|
|
50
|
+
public var bridgeName: String
|
|
51
|
+
public var bridgePublicKey: String
|
|
52
|
+
public var bridgeFingerprint: String
|
|
53
|
+
public var requestSigningRequired: Bool
|
|
54
|
+
public var payloadEncryptionRequired: Bool
|
|
55
|
+
public var grantedCapabilities: [String]
|
|
56
|
+
public var detail: String?
|
|
57
|
+
|
|
58
|
+
public init(
|
|
59
|
+
disposition: DeckPairingDisposition,
|
|
60
|
+
bridgeName: String,
|
|
61
|
+
bridgePublicKey: String,
|
|
62
|
+
bridgeFingerprint: String,
|
|
63
|
+
requestSigningRequired: Bool,
|
|
64
|
+
payloadEncryptionRequired: Bool,
|
|
65
|
+
grantedCapabilities: [String] = DeckBridgeCapability.defaultCompanionCapabilities,
|
|
66
|
+
detail: String? = nil
|
|
67
|
+
) {
|
|
68
|
+
self.disposition = disposition
|
|
69
|
+
self.bridgeName = bridgeName
|
|
70
|
+
self.bridgePublicKey = bridgePublicKey
|
|
71
|
+
self.bridgeFingerprint = bridgeFingerprint
|
|
72
|
+
self.requestSigningRequired = requestSigningRequired
|
|
73
|
+
self.payloadEncryptionRequired = payloadEncryptionRequired
|
|
74
|
+
self.grantedCapabilities = grantedCapabilities
|
|
75
|
+
self.detail = detail
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private enum CodingKeys: String, CodingKey {
|
|
79
|
+
case disposition
|
|
80
|
+
case bridgeName
|
|
81
|
+
case bridgePublicKey
|
|
82
|
+
case bridgeFingerprint
|
|
83
|
+
case requestSigningRequired
|
|
84
|
+
case payloadEncryptionRequired
|
|
85
|
+
case grantedCapabilities
|
|
86
|
+
case detail
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public init(from decoder: Decoder) throws {
|
|
90
|
+
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
91
|
+
disposition = try container.decode(DeckPairingDisposition.self, forKey: .disposition)
|
|
92
|
+
bridgeName = try container.decode(String.self, forKey: .bridgeName)
|
|
93
|
+
bridgePublicKey = try container.decode(String.self, forKey: .bridgePublicKey)
|
|
94
|
+
bridgeFingerprint = try container.decode(String.self, forKey: .bridgeFingerprint)
|
|
95
|
+
requestSigningRequired = try container.decode(Bool.self, forKey: .requestSigningRequired)
|
|
96
|
+
payloadEncryptionRequired = try container.decode(Bool.self, forKey: .payloadEncryptionRequired)
|
|
97
|
+
grantedCapabilities = try container.decodeIfPresent([String].self, forKey: .grantedCapabilities)
|
|
98
|
+
?? DeckBridgeCapability.defaultCompanionCapabilities
|
|
99
|
+
detail = try container.decodeIfPresent(String.self, forKey: .detail)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public enum DeckPairingDisposition: String, Codable, CaseIterable, Sendable {
|
|
104
|
+
case approved
|
|
105
|
+
case alreadyTrusted
|
|
106
|
+
case denied
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public struct DeckEncryptedEnvelope: Codable, Equatable, Sendable {
|
|
110
|
+
public var sealedBox: String
|
|
111
|
+
|
|
112
|
+
public init(sealedBox: String) {
|
|
113
|
+
self.sealedBox = sealedBox
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public struct DeckTrustedDeviceSummary: Codable, Equatable, Identifiable, Sendable {
|
|
118
|
+
public var id: String
|
|
119
|
+
public var name: String
|
|
120
|
+
public var fingerprint: String
|
|
121
|
+
public var capabilities: [String]
|
|
122
|
+
public var pairedAt: Date
|
|
123
|
+
public var lastSeenAt: Date
|
|
124
|
+
|
|
125
|
+
public init(
|
|
126
|
+
id: String,
|
|
127
|
+
name: String,
|
|
128
|
+
fingerprint: String,
|
|
129
|
+
capabilities: [String] = [],
|
|
130
|
+
pairedAt: Date,
|
|
131
|
+
lastSeenAt: Date
|
|
132
|
+
) {
|
|
133
|
+
self.id = id
|
|
134
|
+
self.name = name
|
|
135
|
+
self.fingerprint = fingerprint
|
|
136
|
+
self.capabilities = capabilities
|
|
137
|
+
self.pairedAt = pairedAt
|
|
138
|
+
self.lastSeenAt = lastSeenAt
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
public enum DeckBridgeCapability {
|
|
143
|
+
public static let deckRead = "deck.read"
|
|
144
|
+
public static let deckPerform = "deck.perform"
|
|
145
|
+
public static let inputTrackpad = "input.trackpad"
|
|
146
|
+
|
|
147
|
+
public static let defaultCompanionCapabilities = [
|
|
148
|
+
deckRead,
|
|
149
|
+
deckPerform,
|
|
150
|
+
inputTrackpad,
|
|
151
|
+
]
|
|
152
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
public struct DeckCockpitState: Codable, Equatable, Sendable {
|
|
4
|
+
public var title: String?
|
|
5
|
+
public var detail: String?
|
|
6
|
+
public var pages: [DeckCockpitPage]
|
|
7
|
+
|
|
8
|
+
public init(
|
|
9
|
+
title: String? = nil,
|
|
10
|
+
detail: String? = nil,
|
|
11
|
+
pages: [DeckCockpitPage]
|
|
12
|
+
) {
|
|
13
|
+
self.title = title
|
|
14
|
+
self.detail = detail
|
|
15
|
+
self.pages = pages
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public struct DeckCockpitPage: Codable, Equatable, Identifiable, Sendable {
|
|
20
|
+
public var id: String
|
|
21
|
+
public var title: String
|
|
22
|
+
public var subtitle: String?
|
|
23
|
+
public var columns: Int
|
|
24
|
+
public var tiles: [DeckCockpitTile]
|
|
25
|
+
|
|
26
|
+
public init(
|
|
27
|
+
id: String,
|
|
28
|
+
title: String,
|
|
29
|
+
subtitle: String? = nil,
|
|
30
|
+
columns: Int = 4,
|
|
31
|
+
tiles: [DeckCockpitTile]
|
|
32
|
+
) {
|
|
33
|
+
self.id = id
|
|
34
|
+
self.title = title
|
|
35
|
+
self.subtitle = subtitle
|
|
36
|
+
self.columns = columns
|
|
37
|
+
self.tiles = tiles
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public struct DeckCockpitTile: Codable, Equatable, Identifiable, Sendable {
|
|
42
|
+
public var id: String
|
|
43
|
+
public var shortcutID: String
|
|
44
|
+
public var title: String
|
|
45
|
+
public var subtitle: String?
|
|
46
|
+
public var iconSystemName: String
|
|
47
|
+
public var accentToken: String?
|
|
48
|
+
public var deckID: String?
|
|
49
|
+
public var categoryTint: String?
|
|
50
|
+
public var actionID: String?
|
|
51
|
+
public var payload: [String: DeckValue]
|
|
52
|
+
public var isEnabled: Bool
|
|
53
|
+
public var isActive: Bool
|
|
54
|
+
|
|
55
|
+
public init(
|
|
56
|
+
id: String,
|
|
57
|
+
shortcutID: String,
|
|
58
|
+
title: String,
|
|
59
|
+
subtitle: String? = nil,
|
|
60
|
+
iconSystemName: String,
|
|
61
|
+
accentToken: String? = nil,
|
|
62
|
+
deckID: String? = nil,
|
|
63
|
+
categoryTint: String? = nil,
|
|
64
|
+
actionID: String? = nil,
|
|
65
|
+
payload: [String: DeckValue] = [:],
|
|
66
|
+
isEnabled: Bool = true,
|
|
67
|
+
isActive: Bool = false
|
|
68
|
+
) {
|
|
69
|
+
self.id = id
|
|
70
|
+
self.shortcutID = shortcutID
|
|
71
|
+
self.title = title
|
|
72
|
+
self.subtitle = subtitle
|
|
73
|
+
self.iconSystemName = iconSystemName
|
|
74
|
+
self.accentToken = accentToken
|
|
75
|
+
self.deckID = deckID
|
|
76
|
+
self.categoryTint = categoryTint
|
|
77
|
+
self.actionID = actionID
|
|
78
|
+
self.payload = payload
|
|
79
|
+
self.isEnabled = isEnabled
|
|
80
|
+
self.isActive = isActive
|
|
81
|
+
}
|
|
82
|
+
}
|