@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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Arach Tchoupani
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
<picture>
|
|
2
|
-
<img alt="lattices" src="site/public/og.png" />
|
|
2
|
+
<img alt="lattices" src="apps/site/public/og.png" />
|
|
3
3
|
</picture>
|
|
4
4
|
|
|
5
5
|
# lattices
|
|
6
6
|
|
|
7
|
-
The agentic
|
|
7
|
+
The agentic workspace manager for macOS.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
Lattices turns your Mac workspace into a coherent API, so agents can see and
|
|
10
|
+
control windows, tmux sessions, screen text, and layouts. It also gives you
|
|
11
|
+
an assistant to control that workspace in plain language.
|
|
12
12
|
|
|
13
13
|
**[lattices.dev](https://lattices.dev)** · [Docs](https://lattices.dev/docs/overview) · [Download](https://github.com/arach/lattices/releases/latest)
|
|
14
14
|
|
|
@@ -42,25 +42,25 @@ The CLI and app work independently — use either or both.
|
|
|
42
42
|
git clone https://github.com/arach/lattices.git
|
|
43
43
|
cd lattices
|
|
44
44
|
|
|
45
|
-
# Build the menu bar app (requires Swift
|
|
46
|
-
cd
|
|
45
|
+
# Build the menu bar app (requires Swift 6.2 / Xcode 26+)
|
|
46
|
+
cd apps/mac && swift build -c release && cd ../..
|
|
47
47
|
|
|
48
48
|
# Install CLI dependencies
|
|
49
49
|
npm install
|
|
50
50
|
|
|
51
51
|
# Launch
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
bun bin/lattices-app.ts build # bundle the .app
|
|
53
|
+
bun bin/lattices-app.ts # launch it
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
To build a signed, notarized DMG for distribution:
|
|
57
57
|
|
|
58
58
|
```sh
|
|
59
59
|
# Requires a Developer ID certificate and notarytool keychain profile
|
|
60
|
-
./
|
|
60
|
+
./tools/release/build-dmg.sh
|
|
61
61
|
|
|
62
62
|
# Update v<package.json version> and upload the DMG to GitHub Releases
|
|
63
|
-
./
|
|
63
|
+
./tools/release/ship.sh
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
## Quick start
|
|
@@ -215,13 +215,13 @@ lattices help Show help
|
|
|
215
215
|
|
|
216
216
|
## Requirements
|
|
217
217
|
|
|
218
|
-
- macOS
|
|
218
|
+
- macOS 26.0+
|
|
219
219
|
- Node.js 18+
|
|
220
220
|
|
|
221
221
|
### Optional
|
|
222
222
|
|
|
223
223
|
- tmux for persistent terminal sessions (`brew install tmux`)
|
|
224
|
-
- Swift
|
|
224
|
+
- Swift 6.2 / Xcode 26+ to build the menu bar app from source
|
|
225
225
|
|
|
226
226
|
## Docs
|
|
227
227
|
|
|
@@ -26,9 +26,17 @@
|
|
|
26
26
|
</dict>
|
|
27
27
|
</array>
|
|
28
28
|
<key>CFBundleVersion</key>
|
|
29
|
-
<string>0.4.
|
|
29
|
+
<string>0.4.12</string>
|
|
30
30
|
<key>CFBundleShortVersionString</key>
|
|
31
|
-
<string>0.4.
|
|
31
|
+
<string>0.4.12</string>
|
|
32
|
+
<key>LatticesBuildChannel</key>
|
|
33
|
+
<string>dev</string>
|
|
34
|
+
<key>LatticesBuildTrack</key>
|
|
35
|
+
<string>latest</string>
|
|
36
|
+
<key>LatticesBuildRevision</key>
|
|
37
|
+
<string>b84f24f</string>
|
|
38
|
+
<key>LatticesBuildTimestamp</key>
|
|
39
|
+
<string>2026-05-04T16:17:39Z</string>
|
|
32
40
|
<key>LSMinimumSystemVersion</key>
|
|
33
41
|
<string>13.0</string>
|
|
34
42
|
<key>LSUIElement</key>
|
|
Binary file
|
|
@@ -5,7 +5,7 @@ let package = Package(
|
|
|
5
5
|
name: "Lattices",
|
|
6
6
|
platforms: [.macOS(.v13)],
|
|
7
7
|
dependencies: [
|
|
8
|
-
.package(path: "
|
|
8
|
+
.package(path: "../../swift")
|
|
9
9
|
],
|
|
10
10
|
targets: [
|
|
11
11
|
.executableTarget(
|
|
@@ -16,6 +16,7 @@ let package = Package(
|
|
|
16
16
|
path: "Sources",
|
|
17
17
|
resources: [
|
|
18
18
|
.copy("../Resources/tap.wav"),
|
|
19
|
+
.copy("../Resources/Pets"),
|
|
19
20
|
]
|
|
20
21
|
),
|
|
21
22
|
.testTarget(
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "assistant-spark",
|
|
3
|
+
"displayName": "Assistant Spark",
|
|
4
|
+
"description": "A small luminous assistant companion for Codex sessions.",
|
|
5
|
+
"spritesheetPath": "spritesheet.webp",
|
|
6
|
+
"states": {
|
|
7
|
+
"idle": {
|
|
8
|
+
"row": 0,
|
|
9
|
+
"frames": 6,
|
|
10
|
+
"frameWidth": 192,
|
|
11
|
+
"frameHeight": 208
|
|
12
|
+
},
|
|
13
|
+
"run_right": {
|
|
14
|
+
"row": 1,
|
|
15
|
+
"frames": 8,
|
|
16
|
+
"frameWidth": 192,
|
|
17
|
+
"frameHeight": 208
|
|
18
|
+
},
|
|
19
|
+
"run_left": {
|
|
20
|
+
"row": 2,
|
|
21
|
+
"frames": 8,
|
|
22
|
+
"frameWidth": 192,
|
|
23
|
+
"frameHeight": 208
|
|
24
|
+
},
|
|
25
|
+
"waving": {
|
|
26
|
+
"row": 3,
|
|
27
|
+
"frames": 4,
|
|
28
|
+
"frameWidth": 192,
|
|
29
|
+
"frameHeight": 208
|
|
30
|
+
},
|
|
31
|
+
"jumping": {
|
|
32
|
+
"row": 4,
|
|
33
|
+
"frames": 5,
|
|
34
|
+
"frameWidth": 192,
|
|
35
|
+
"frameHeight": 208
|
|
36
|
+
},
|
|
37
|
+
"failed": {
|
|
38
|
+
"row": 5,
|
|
39
|
+
"frames": 8,
|
|
40
|
+
"frameWidth": 192,
|
|
41
|
+
"frameHeight": 208
|
|
42
|
+
},
|
|
43
|
+
"waiting": {
|
|
44
|
+
"row": 6,
|
|
45
|
+
"frames": 6,
|
|
46
|
+
"frameWidth": 192,
|
|
47
|
+
"frameHeight": 208
|
|
48
|
+
},
|
|
49
|
+
"running": {
|
|
50
|
+
"row": 7,
|
|
51
|
+
"frames": 6,
|
|
52
|
+
"frameWidth": 192,
|
|
53
|
+
"frameHeight": 208
|
|
54
|
+
},
|
|
55
|
+
"review": {
|
|
56
|
+
"row": 8,
|
|
57
|
+
"frames": 6,
|
|
58
|
+
"frameWidth": 192,
|
|
59
|
+
"frameHeight": 208
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
|
|
3
|
+
final class AppActivationCoordinator {
|
|
4
|
+
static let shared = AppActivationCoordinator()
|
|
5
|
+
|
|
6
|
+
private var surfaceVisibilityProviders: [(id: String, isVisible: () -> Bool)] = []
|
|
7
|
+
|
|
8
|
+
private init() {}
|
|
9
|
+
|
|
10
|
+
func registerSurface(id: String, isVisible: @escaping () -> Bool) {
|
|
11
|
+
guard !surfaceVisibilityProviders.contains(where: { $0.id == id }) else { return }
|
|
12
|
+
surfaceVisibilityProviders.append((id: id, isVisible: isVisible))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
func refresh() {
|
|
16
|
+
let hasVisibleWindow = surfaceVisibilityProviders.contains { provider in
|
|
17
|
+
provider.isVisible()
|
|
18
|
+
}
|
|
19
|
+
let desired: NSApplication.ActivationPolicy = hasVisibleWindow ? .regular : .accessory
|
|
20
|
+
if NSApp.activationPolicy() != desired {
|
|
21
|
+
NSApp.setActivationPolicy(desired)
|
|
22
|
+
if desired == .regular {
|
|
23
|
+
NSApp.activate(ignoringOtherApps: true)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import Carbon
|
|
3
|
+
|
|
4
|
+
extension Notification.Name {
|
|
5
|
+
static let latticesPopoverWillShow = Notification.Name("latticesPopoverWillShow")
|
|
6
|
+
static let latticesShowGeneralSettings = Notification.Name("latticesShowGeneralSettings")
|
|
7
|
+
static let latticesShowAssistantSettings = Notification.Name("latticesShowAssistantSettings")
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
class AppDelegate: NSObject, NSApplicationDelegate {
|
|
11
|
+
private var notificationObservers: [NSObjectProtocol] = []
|
|
12
|
+
|
|
13
|
+
static func updateActivationPolicy() {
|
|
14
|
+
AppActivationCoordinator.shared.refresh()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
func applicationDidFinishLaunching(_ notification: Notification) {
|
|
18
|
+
NSApp.setActivationPolicy(.accessory)
|
|
19
|
+
NSApp.appearance = NSAppearance(named: .darkAqua)
|
|
20
|
+
registerDeepLinkHandler()
|
|
21
|
+
installSystemInputBoundaryObservers()
|
|
22
|
+
|
|
23
|
+
MenuBarController.shared.start()
|
|
24
|
+
registerVisibleSurfaces()
|
|
25
|
+
HotkeyBootstrap.registerHotkeys()
|
|
26
|
+
|
|
27
|
+
DispatchQueue.main.async { HUDController.shared.warmUp() }
|
|
28
|
+
DispatchQueue.main.async { MenuBarController.shared.warmUpPopover() }
|
|
29
|
+
DispatchQueue.main.async { ScreenOverlayCanvasController.shared.warmUp() }
|
|
30
|
+
|
|
31
|
+
WindowDragSnapController.shared.start()
|
|
32
|
+
MouseGestureController.shared.start()
|
|
33
|
+
KeyboardRemapController.shared.start()
|
|
34
|
+
SecureEventInputMonitor.shared.start()
|
|
35
|
+
|
|
36
|
+
if !OnboardingWindowController.shared.showIfNeeded() {
|
|
37
|
+
PermissionChecker.shared.check()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
AppServicesBootstrap.start()
|
|
41
|
+
|
|
42
|
+
Task {
|
|
43
|
+
await AppUpdater.shared.checkIfNeeded()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// --diagnostics flag: auto-open diagnostics panel on launch
|
|
47
|
+
if CommandLine.arguments.contains("--diagnostics") {
|
|
48
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
|
49
|
+
DiagnosticWindow.shared.show()
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// --screen-map flag: auto-open layout on launch
|
|
54
|
+
if CommandLine.arguments.contains("--screen-map") {
|
|
55
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
|
56
|
+
ScreenMapWindowController.shared.showPage(.screenMap)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Explicit preview entry point for development/demo flows. This still
|
|
61
|
+
// requires a launch argument; the assistant never opens automatically.
|
|
62
|
+
if CommandLine.arguments.contains("--permissions-assistant") {
|
|
63
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
|
64
|
+
PermissionsAssistantWindowController.shared.show()
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
func applicationWillTerminate(_ notification: Notification) {
|
|
70
|
+
removeSystemInputBoundaryObservers()
|
|
71
|
+
SecureEventInputMonitor.shared.stop()
|
|
72
|
+
KeyboardRemapController.shared.stop()
|
|
73
|
+
AppServicesBootstrap.stop()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
func applicationDidBecomeActive(_ notification: Notification) {
|
|
77
|
+
resetInputCapture(reason: "app became active")
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
func applicationWillResignActive(_ notification: Notification) {
|
|
81
|
+
resetInputCapture(reason: "app resigned active")
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private func registerVisibleSurfaces() {
|
|
85
|
+
let coordinator = AppActivationCoordinator.shared
|
|
86
|
+
coordinator.registerSurface(id: "menuBarPopover") { MenuBarController.shared.isPopoverShown }
|
|
87
|
+
coordinator.registerSurface(id: "commandMode") { CommandModeWindow.shared.isVisible }
|
|
88
|
+
coordinator.registerSurface(id: "commandPalette") { CommandPaletteWindow.shared.isVisible }
|
|
89
|
+
coordinator.registerSurface(id: "mainWindow") { MainWindow.shared.isVisible }
|
|
90
|
+
coordinator.registerSurface(id: "screenMap") { ScreenMapWindowController.shared.isVisible }
|
|
91
|
+
coordinator.registerSurface(id: "omniSearch") { OmniSearchWindow.shared.isVisible }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private func installSystemInputBoundaryObservers() {
|
|
95
|
+
let center = NSWorkspace.shared.notificationCenter
|
|
96
|
+
notificationObservers.append(center.addObserver(
|
|
97
|
+
forName: NSWorkspace.willSleepNotification,
|
|
98
|
+
object: nil,
|
|
99
|
+
queue: .main
|
|
100
|
+
) { [weak self] _ in
|
|
101
|
+
self?.resetInputCapture(reason: "system will sleep")
|
|
102
|
+
})
|
|
103
|
+
notificationObservers.append(center.addObserver(
|
|
104
|
+
forName: NSWorkspace.didWakeNotification,
|
|
105
|
+
object: nil,
|
|
106
|
+
queue: .main
|
|
107
|
+
) { [weak self] _ in
|
|
108
|
+
self?.resetInputCapture(reason: "system did wake")
|
|
109
|
+
})
|
|
110
|
+
notificationObservers.append(center.addObserver(
|
|
111
|
+
forName: NSWorkspace.screensDidWakeNotification,
|
|
112
|
+
object: nil,
|
|
113
|
+
queue: .main
|
|
114
|
+
) { [weak self] _ in
|
|
115
|
+
self?.resetInputCapture(reason: "screens did wake")
|
|
116
|
+
})
|
|
117
|
+
notificationObservers.append(center.addObserver(
|
|
118
|
+
forName: NSWorkspace.screensDidSleepNotification,
|
|
119
|
+
object: nil,
|
|
120
|
+
queue: .main
|
|
121
|
+
) { [weak self] _ in
|
|
122
|
+
self?.resetInputCapture(reason: "screens did sleep")
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private func removeSystemInputBoundaryObservers() {
|
|
127
|
+
let center = NSWorkspace.shared.notificationCenter
|
|
128
|
+
for observer in notificationObservers {
|
|
129
|
+
center.removeObserver(observer)
|
|
130
|
+
}
|
|
131
|
+
notificationObservers.removeAll()
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private func resetInputCapture(reason: String) {
|
|
135
|
+
InputCaptureResetCenter.reset(reason: reason)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// MARK: - Deep Links
|
|
139
|
+
|
|
140
|
+
private func registerDeepLinkHandler() {
|
|
141
|
+
NSAppleEventManager.shared().setEventHandler(
|
|
142
|
+
self,
|
|
143
|
+
andSelector: #selector(handleGetURLEvent(_:withReplyEvent:)),
|
|
144
|
+
forEventClass: AEEventClass(kInternetEventClass),
|
|
145
|
+
andEventID: AEEventID(kAEGetURL)
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
@objc private func handleGetURLEvent(
|
|
150
|
+
_ event: NSAppleEventDescriptor,
|
|
151
|
+
withReplyEvent replyEvent: NSAppleEventDescriptor
|
|
152
|
+
) {
|
|
153
|
+
guard
|
|
154
|
+
let value = event.paramDescriptor(forKeyword: keyDirectObject)?.stringValue,
|
|
155
|
+
let url = URL(string: value)
|
|
156
|
+
else {
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
handleDeepLink(url)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private func handleDeepLink(_ url: URL) {
|
|
163
|
+
guard url.scheme?.localizedCaseInsensitiveCompare("lattices") == .orderedSame else {
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
let host = url.host?.lowercased()
|
|
168
|
+
let action = url.pathComponents
|
|
169
|
+
.first { $0 != "/" }?
|
|
170
|
+
.lowercased()
|
|
171
|
+
|
|
172
|
+
guard host == "companion" else {
|
|
173
|
+
SettingsWindowController.shared.show()
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
switch action {
|
|
178
|
+
case "enable", "start":
|
|
179
|
+
Preferences.shared.companionBridgeEnabled = true
|
|
180
|
+
SettingsWindowController.shared.showCompanion()
|
|
181
|
+
case "disable", "stop":
|
|
182
|
+
Preferences.shared.companionBridgeEnabled = false
|
|
183
|
+
SettingsWindowController.shared.showCompanion()
|
|
184
|
+
default:
|
|
185
|
+
SettingsWindowController.shared.showCompanion()
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
enum AppServicesBootstrap {
|
|
2
|
+
static func start() {
|
|
3
|
+
let diagnosticLog = DiagnosticLog.shared
|
|
4
|
+
let timedBoot = diagnosticLog.startTimed("Daemon services boot")
|
|
5
|
+
OcrStore.shared.open()
|
|
6
|
+
DesktopModel.shared.start()
|
|
7
|
+
OcrModel.shared.start()
|
|
8
|
+
TmuxModel.shared.start()
|
|
9
|
+
ProcessModel.shared.start()
|
|
10
|
+
LatticesApi.setup()
|
|
11
|
+
DaemonServer.shared.start()
|
|
12
|
+
if Preferences.shared.companionBridgeEnabled {
|
|
13
|
+
LatticesCompanionBridgeServer.shared.start()
|
|
14
|
+
} else {
|
|
15
|
+
diagnosticLog.info("CompanionBridge: disabled by preference")
|
|
16
|
+
}
|
|
17
|
+
AgentPool.shared.start()
|
|
18
|
+
diagnosticLog.finish(timedBoot)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static func stop() {
|
|
22
|
+
LatticesCompanionBridgeServer.shared.stop()
|
|
23
|
+
DaemonServer.shared.stop()
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -14,9 +14,9 @@ enum AppPage: String, CaseIterable {
|
|
|
14
14
|
var label: String {
|
|
15
15
|
switch self {
|
|
16
16
|
case .home: return "Home"
|
|
17
|
-
case .screenMap: return "
|
|
17
|
+
case .screenMap: return "Layout"
|
|
18
18
|
case .desktopInventory: return "Desktop Inventory"
|
|
19
|
-
case .pi: return "
|
|
19
|
+
case .pi: return "Assistant"
|
|
20
20
|
case .settings: return "Settings"
|
|
21
21
|
case .companionSettings:return "Settings"
|
|
22
22
|
case .docs: return "Docs"
|
|
@@ -28,7 +28,7 @@ enum AppPage: String, CaseIterable {
|
|
|
28
28
|
case .home: return "house"
|
|
29
29
|
case .screenMap: return "rectangle.3.group"
|
|
30
30
|
case .desktopInventory: return "macwindow.on.rectangle"
|
|
31
|
-
case .pi: return "
|
|
31
|
+
case .pi: return "bubble.left.and.bubble.right"
|
|
32
32
|
case .settings: return "gearshape"
|
|
33
33
|
case .companionSettings:return "ipad.and.iphone"
|
|
34
34
|
case .docs: return "book"
|
|
@@ -63,6 +63,21 @@ struct AppShellView: View {
|
|
|
63
63
|
}
|
|
64
64
|
.onChange(of: windowController.activePage) { page in
|
|
65
65
|
syncPageState(page)
|
|
66
|
+
clearRelevantDismissals(for: page)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/// Entering a feature page clears its capability snooze — the user is
|
|
71
|
+
/// telling us they want this to work, so the banner can resurface.
|
|
72
|
+
private func clearRelevantDismissals(for page: AppPage) {
|
|
73
|
+
let prefs = Preferences.shared
|
|
74
|
+
switch page {
|
|
75
|
+
case .screenMap:
|
|
76
|
+
prefs.clearDismissal(Capability.windowControl.rawValue)
|
|
77
|
+
case .desktopInventory:
|
|
78
|
+
prefs.clearDismissal(Capability.screenSearch.rawValue)
|
|
79
|
+
default:
|
|
80
|
+
break
|
|
66
81
|
}
|
|
67
82
|
}
|
|
68
83
|
|
|
@@ -31,6 +31,7 @@ final class AppUpdater: ObservableObject {
|
|
|
31
31
|
private init() {}
|
|
32
32
|
|
|
33
33
|
var currentVersion: String { LatticesRuntime.appVersion }
|
|
34
|
+
var currentDisplayVersion: String { LatticesRuntime.appDisplayVersion }
|
|
34
35
|
|
|
35
36
|
var canUpdate: Bool {
|
|
36
37
|
LatticesRuntime.bunPath != nil && LatticesRuntime.appHelperScriptPath != nil
|
|
@@ -124,7 +125,7 @@ final class AppUpdater: ObservableObject {
|
|
|
124
125
|
} else {
|
|
125
126
|
presentAlert(
|
|
126
127
|
title: "Lattices Is Up to Date",
|
|
127
|
-
message: "You’re running \(
|
|
128
|
+
message: "You’re running \(currentDisplayVersion), which is the latest available build for this install."
|
|
128
129
|
)
|
|
129
130
|
}
|
|
130
131
|
}
|
|
@@ -140,7 +141,7 @@ final class AppUpdater: ObservableObject {
|
|
|
140
141
|
if let update = availableUpdate {
|
|
141
142
|
alert.messageText = "Update Lattices?"
|
|
142
143
|
alert.informativeText = """
|
|
143
|
-
Current version: \(
|
|
144
|
+
Current version: \(currentDisplayVersion)
|
|
144
145
|
New version: \(update.version)
|
|
145
146
|
|
|
146
147
|
Lattices will download the signed release, quit briefly, replace the app, and relaunch when the update is ready.
|
|
@@ -148,7 +149,7 @@ final class AppUpdater: ObservableObject {
|
|
|
148
149
|
} else {
|
|
149
150
|
alert.messageText = "Check and update Lattices?"
|
|
150
151
|
alert.informativeText = """
|
|
151
|
-
Current version: \(
|
|
152
|
+
Current version: \(currentDisplayVersion)
|
|
152
153
|
New version: latest published release
|
|
153
154
|
|
|
154
155
|
Lattices will download the signed release, quit briefly, replace the app, and relaunch when the update is ready.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
|
|
3
|
+
enum HotkeyBootstrap {
|
|
4
|
+
static func registerHotkeys() {
|
|
5
|
+
let scanner = ProjectScanner.shared
|
|
6
|
+
CommandPaletteWindow.shared.configure(scanner: scanner)
|
|
7
|
+
|
|
8
|
+
let store = HotkeyStore.shared
|
|
9
|
+
store.register(action: .palette) { CommandPaletteWindow.shared.toggle() }
|
|
10
|
+
store.register(action: .unifiedWindow) { ScreenMapWindowController.shared.toggle() }
|
|
11
|
+
store.register(action: .bezel) { WorkspaceInspectorPresenter.show() }
|
|
12
|
+
store.register(action: .cheatSheet) { SettingsWindowController.shared.show() }
|
|
13
|
+
store.register(action: .desktopInventory) {
|
|
14
|
+
DiagnosticLog.shared.info("Hotkey: desktopInventory triggered")
|
|
15
|
+
ScreenMapWindowController.shared.showPage(.desktopInventory)
|
|
16
|
+
}
|
|
17
|
+
store.register(action: .voiceCommand) {
|
|
18
|
+
DiagnosticLog.shared.info("Hotkey: voiceCommand triggered")
|
|
19
|
+
VoiceCommandWindow.shared.toggle()
|
|
20
|
+
}
|
|
21
|
+
store.register(action: .handsOff) {
|
|
22
|
+
DiagnosticLog.shared.info("Hotkey: handsOff triggered")
|
|
23
|
+
HandsOffSession.shared.toggle()
|
|
24
|
+
if HandsOffSession.shared.state != .idle {
|
|
25
|
+
HUDController.shared.showVoiceBar()
|
|
26
|
+
} else {
|
|
27
|
+
HUDController.shared.hideVoiceBar()
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
store.register(action: .hud) { HUDController.shared.toggle() }
|
|
31
|
+
store.register(action: .mouseFinder) { MouseFinder.shared.find() }
|
|
32
|
+
store.register(action: .overlayActors) { ScreenOverlayCanvasController.shared.toggleAgentActorsVisibility() }
|
|
33
|
+
store.register(action: .omniSearch) { OmniSearchWindow.shared.toggle() }
|
|
34
|
+
|
|
35
|
+
registerLayerHotkeys(store: store)
|
|
36
|
+
registerTilingHotkeys(store: store)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private static func registerLayerHotkeys(store: HotkeyStore) {
|
|
40
|
+
store.register(action: .layerNext) { SessionLayerStore.shared.cycleNext() }
|
|
41
|
+
store.register(action: .layerPrev) { SessionLayerStore.shared.cyclePrev() }
|
|
42
|
+
store.register(action: .layerTag) { SessionLayerStore.shared.tagFrontmostWindow() }
|
|
43
|
+
|
|
44
|
+
let workspace = WorkspaceManager.shared
|
|
45
|
+
let configLayerCount = (workspace.config?.layers ?? []).count
|
|
46
|
+
let maxLayers = max(configLayerCount, 9)
|
|
47
|
+
for (index, action) in HotkeyAction.layerActions.prefix(maxLayers).enumerated() {
|
|
48
|
+
store.register(action: action) {
|
|
49
|
+
let session = SessionLayerStore.shared
|
|
50
|
+
if !session.layers.isEmpty && index < session.layers.count {
|
|
51
|
+
session.switchTo(index: index)
|
|
52
|
+
} else {
|
|
53
|
+
workspace.focusLayer(index: index)
|
|
54
|
+
}
|
|
55
|
+
EventBus.shared.post(.layerSwitched(index: index))
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private static func registerTilingHotkeys(store: HotkeyStore) {
|
|
61
|
+
let tileMap: [(HotkeyAction, TilePosition)] = [
|
|
62
|
+
(.tileLeft, .left), (.tileRight, .right),
|
|
63
|
+
(.tileMaximize, .maximize), (.tileCenter, .center),
|
|
64
|
+
(.tileTopLeft, .topLeft), (.tileTopRight, .topRight),
|
|
65
|
+
(.tileBottomLeft, .bottomLeft), (.tileBottomRight, .bottomRight),
|
|
66
|
+
(.tileTop, .top), (.tileBottom, .bottom),
|
|
67
|
+
(.tileLeftThird, .leftThird), (.tileCenterThird, .centerThird),
|
|
68
|
+
(.tileRightThird, .rightThird),
|
|
69
|
+
]
|
|
70
|
+
for (action, position) in tileMap {
|
|
71
|
+
store.register(action: action) {
|
|
72
|
+
WindowTiler.tileFrontmostViaAX(to: position)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
store.register(action: .tileDistribute) {
|
|
76
|
+
WindowTiler.distributeVisible(reactivateLattices: false)
|
|
77
|
+
}
|
|
78
|
+
store.register(action: .tileTypeGrid) {
|
|
79
|
+
WindowTiler.distributeVisibleByFrontmostType(reactivateLattices: false)
|
|
80
|
+
}
|
|
81
|
+
store.register(action: .tileOrganize) {
|
|
82
|
+
let appName = DesktopModel.shared.frontmostWindow()?.app
|
|
83
|
+
?? NSWorkspace.shared.frontmostApplication?.localizedName
|
|
84
|
+
CommandModeWindow.shared.show(launchMode: .organize(appName: appName))
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -55,7 +55,50 @@ enum LatticesRuntime {
|
|
|
55
55
|
?? "unknown"
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
static var appDisplayVersion: String {
|
|
59
|
+
let base = appVersion == "unknown" ? "unknown" : "v\(appVersion)"
|
|
60
|
+
guard isDevBuild else { return base }
|
|
61
|
+
|
|
62
|
+
let track = buildTrack ?? "latest"
|
|
63
|
+
return "\(base)-dev.\(track)"
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static var buildChannel: String {
|
|
67
|
+
let raw = Bundle.main.infoDictionary?["LatticesBuildChannel"] as? String
|
|
68
|
+
return raw?.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() ?? "release"
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static var buildTrack: String? {
|
|
72
|
+
normalizedInfoValue("LatticesBuildTrack")
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static var buildRevision: String? {
|
|
76
|
+
normalizedInfoValue("LatticesBuildRevision")
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
static var buildTimestamp: String? {
|
|
80
|
+
normalizedInfoValue("LatticesBuildTimestamp")
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static var isDevBuild: Bool {
|
|
84
|
+
buildChannel == "dev"
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
static var buildStatusLabel: String {
|
|
88
|
+
isDevBuild ? "Latest local dev build" : "Signed release build"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static var buildChannelLabel: String {
|
|
92
|
+
isDevBuild ? "DEV" : "RELEASE"
|
|
93
|
+
}
|
|
94
|
+
|
|
58
95
|
private static func hasAppHelper(in root: String) -> Bool {
|
|
59
96
|
FileManager.default.fileExists(atPath: root + "/bin/lattices-app.ts")
|
|
60
97
|
}
|
|
98
|
+
|
|
99
|
+
private static func normalizedInfoValue(_ key: String) -> String? {
|
|
100
|
+
guard let raw = Bundle.main.infoDictionary?[key] as? String else { return nil }
|
|
101
|
+
let value = raw.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
102
|
+
return value.isEmpty || value == "unknown" ? nil : value
|
|
103
|
+
}
|
|
61
104
|
}
|