@lattices/cli 0.4.2 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/app/Info.plist +2 -2
- package/app/Lattices.app/Contents/Info.plist +2 -2
- package/app/Lattices.app/Contents/MacOS/Lattices +0 -0
- package/app/Package.swift +6 -0
- package/app/Sources/AppShell/App.swift +20 -0
- package/app/Sources/{AppDelegate.swift → AppShell/AppDelegate.swift} +94 -34
- package/app/Sources/{AppShellView.swift → AppShell/AppShellView.swift} +12 -1
- package/app/Sources/AppShell/AppUpdater.swift +92 -0
- package/app/Sources/AppShell/CliActionLauncher.swift +50 -0
- package/app/Sources/{HomeDashboardView.swift → AppShell/HomeDashboardView.swift} +18 -10
- package/app/Sources/AppShell/LatticesRuntime.swift +61 -0
- package/app/Sources/{MainView.swift → AppShell/MainView.swift} +351 -191
- package/app/Sources/{OnboardingView.swift → AppShell/OnboardingView.swift} +30 -16
- package/app/Sources/{Preferences.swift → AppShell/Preferences.swift} +78 -0
- package/app/Sources/{SettingsView.swift → AppShell/SettingsView.swift} +869 -152
- package/app/Sources/{HotkeyStore.swift → Core/Actions/HotkeyStore.swift} +9 -5
- package/app/Sources/{IntentEngine.swift → Core/Actions/IntentEngine.swift} +51 -27
- package/app/Sources/Core/Actions/IntentSchema.swift +94 -0
- package/app/Sources/{Intents → Core/Actions/Intents}/LatticeIntent.swift +0 -25
- package/app/Sources/{PaletteCommand.swift → Core/Actions/PaletteCommand.swift} +26 -6
- package/app/Sources/{VoiceIntentResolver.swift → Core/Actions/VoiceIntentResolver.swift} +46 -4
- package/app/Sources/Core/Companion/CompanionActivityLog.swift +70 -0
- package/app/Sources/Core/Companion/CompanionKeyboardController.swift +141 -0
- package/app/Sources/Core/Companion/LatticesCompanionBridgeServer.swift +438 -0
- package/app/Sources/Core/Companion/LatticesCompanionCockpit.swift +555 -0
- package/app/Sources/Core/Companion/LatticesCompanionSecurityCoordinator.swift +594 -0
- package/app/Sources/Core/Companion/LatticesCompanionTrackpadController.swift +204 -0
- package/app/Sources/Core/Companion/LatticesDeckHost.swift +1463 -0
- package/app/Sources/{LatticesApi.swift → Core/Daemon/LatticesApi.swift} +125 -4
- package/app/Sources/{AppTypeClassifier.swift → Core/Desktop/AppTypeClassifier.swift} +36 -0
- package/app/Sources/{DesktopModel.swift → Core/Desktop/DesktopModel.swift} +6 -8
- package/app/Sources/Core/Desktop/MouseFinder.swift +527 -0
- package/app/Sources/Core/Desktop/SessionWindowLocator.swift +139 -0
- package/app/Sources/Core/Desktop/WindowDragSnapController.swift +628 -0
- package/app/Sources/Core/Desktop/WindowPreviewCard.swift +100 -0
- package/app/Sources/Core/Desktop/WindowPreviewStore.swift +113 -0
- package/app/Sources/Core/Desktop/WindowSelectionStore.swift +76 -0
- package/app/Sources/{WindowTiler.swift → Core/Desktop/WindowTiler.swift} +351 -172
- package/app/Sources/Core/Input/MouseGestureConfig.swift +364 -0
- package/app/Sources/Core/Input/MouseGestureController.swift +1203 -0
- package/app/Sources/Core/Input/MouseInputDeviceStore.swift +98 -0
- package/app/Sources/Core/Input/MouseInputEventViewer.swift +272 -0
- package/app/Sources/Core/Input/MouseShortcutStore.swift +107 -0
- package/app/Sources/{CommandModeState.swift → Core/Overlays/CommandMode/CommandModeState.swift} +127 -24
- package/app/Sources/{CommandModeView.swift → Core/Overlays/CommandMode/CommandModeView.swift} +492 -79
- package/app/Sources/Core/Overlays/CommandPalette/CommandPaletteWindow.swift +67 -0
- package/app/Sources/{CheatSheetHUD.swift → Core/Overlays/HUD/CheatSheetHUD.swift} +1 -0
- package/app/Sources/{HUDRightBar.swift → Core/Overlays/HUD/HUDRightBar.swift} +23 -201
- package/app/Sources/{LauncherHUD.swift → Core/Overlays/HUD/LauncherHUD.swift} +12 -26
- package/app/Sources/{OmniSearchView.swift → Core/Overlays/OmniSearch/OmniSearchView.swift} +136 -2
- package/app/Sources/{OmniSearchWindow.swift → Core/Overlays/OmniSearch/OmniSearchWindow.swift} +21 -32
- package/app/Sources/Core/Overlays/OverlayPanelShell.swift +241 -0
- package/app/Sources/{ScreenMapState.swift → Core/Overlays/ScreenMap/ScreenMapState.swift} +116 -32
- package/app/Sources/{ScreenMapView.swift → Core/Overlays/ScreenMap/ScreenMapView.swift} +510 -524
- package/app/Sources/{ScreenMapWindowController.swift → Core/Overlays/ScreenMap/ScreenMapWindowController.swift} +12 -4
- package/app/Sources/{VoiceCommandWindow.swift → Core/Overlays/Voice/VoiceCommandWindow.swift} +46 -53
- package/app/Sources/Core/Pi/PiAuthNextStepCard.swift +148 -0
- package/app/Sources/Core/Pi/PiAuthPromptCard.swift +90 -0
- package/app/Sources/{PiChatDock.swift → Core/Pi/PiChatDock.swift} +137 -74
- package/app/Sources/{PiChatSession.swift → Core/Pi/PiChatSession.swift} +608 -108
- package/app/Sources/Core/Pi/PiInstallCallout.swift +86 -0
- package/app/Sources/Core/Pi/PiProviderSetupCallout.swift +99 -0
- package/app/Sources/{PiWorkspaceView.swift → Core/Pi/PiWorkspaceView.swift} +174 -77
- package/app/Sources/{PermissionChecker.swift → Core/System/PermissionChecker.swift} +76 -2
- package/app/Sources/Core/System/SystemTelemetryMonitor.swift +273 -0
- package/app/Sources/{HandsOffSession.swift → Core/Voice/HandsOffSession.swift} +15 -4
- package/app/Sources/{WorkspaceManager.swift → Core/Workspace/WorkspaceManager.swift} +288 -0
- package/bin/assistant-intelligence.ts +874 -0
- package/bin/handsoff-infer.ts +16 -209
- package/bin/handsoff-worker.ts +45 -258
- package/bin/lattices-app.ts +62 -0
- package/bin/lattices-dev +4 -0
- package/bin/lattices.ts +125 -14
- package/docs/agents.md +14 -0
- package/docs/api.md +55 -0
- package/docs/app.md +3 -0
- package/docs/companion-deck.md +180 -0
- package/docs/component-extraction-roadmap.md +392 -0
- package/docs/config.md +25 -0
- package/docs/tiling-reference.md +55 -0
- package/docs/voice-error-model.md +73 -0
- package/package.json +4 -1
- package/app/Sources/App.swift +0 -10
- package/app/Sources/CommandPaletteWindow.swift +0 -134
- package/app/Sources/MouseFinder.swift +0 -222
- /package/app/Sources/{KeyRecorderView.swift → AppShell/KeyRecorderView.swift} +0 -0
- /package/app/Sources/{MainWindow.swift → AppShell/MainWindow.swift} +0 -0
- /package/app/Sources/{SettingsWindow.swift → AppShell/SettingsWindow.swift} +0 -0
- /package/app/Sources/{HotkeyManager.swift → Core/Actions/HotkeyManager.swift} +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/CreateLayerIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/DistributeIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/FocusIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/HelpIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/KillIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/LaunchIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/ListSessionsIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/ListWindowsIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/ScanIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/SearchIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/SwitchLayerIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/TileIntent.swift +0 -0
- /package/app/Sources/{DaemonProtocol.swift → Core/Daemon/DaemonProtocol.swift} +0 -0
- /package/app/Sources/{DaemonServer.swift → Core/Daemon/DaemonServer.swift} +0 -0
- /package/app/Sources/{AccessibilityTextExtractor.swift → Core/Desktop/AccessibilityTextExtractor.swift} +0 -0
- /package/app/Sources/{DesktopModelTypes.swift → Core/Desktop/DesktopModelTypes.swift} +0 -0
- /package/app/Sources/{InventoryManager.swift → Core/Desktop/InventoryManager.swift} +0 -0
- /package/app/Sources/{InventoryPath.swift → Core/Desktop/InventoryPath.swift} +0 -0
- /package/app/Sources/{OcrModel.swift → Core/Desktop/OcrModel.swift} +0 -0
- /package/app/Sources/{OcrStore.swift → Core/Desktop/OcrStore.swift} +0 -0
- /package/app/Sources/{PlacementSpec.swift → Core/Desktop/PlacementSpec.swift} +0 -0
- /package/app/Sources/{TilePickerView.swift → Core/Desktop/TilePickerView.swift} +0 -0
- /package/app/Sources/{AppWindowShell.swift → Core/Overlays/AppWindowShell.swift} +0 -0
- /package/app/Sources/{CommandModeWindow.swift → Core/Overlays/CommandMode/CommandModeWindow.swift} +0 -0
- /package/app/Sources/{CommandPaletteView.swift → Core/Overlays/CommandPalette/CommandPaletteView.swift} +0 -0
- /package/app/Sources/{HUDBottomBar.swift → Core/Overlays/HUD/HUDBottomBar.swift} +0 -0
- /package/app/Sources/{HUDController.swift → Core/Overlays/HUD/HUDController.swift} +0 -0
- /package/app/Sources/{HUDLeftBar.swift → Core/Overlays/HUD/HUDLeftBar.swift} +0 -0
- /package/app/Sources/{HUDMinimap.swift → Core/Overlays/HUD/HUDMinimap.swift} +0 -0
- /package/app/Sources/{HUDState.swift → Core/Overlays/HUD/HUDState.swift} +0 -0
- /package/app/Sources/{HUDTopBar.swift → Core/Overlays/HUD/HUDTopBar.swift} +0 -0
- /package/app/Sources/{LayerBezel.swift → Core/Overlays/HUD/LayerBezel.swift} +0 -0
- /package/app/Sources/{OmniSearchState.swift → Core/Overlays/OmniSearch/OmniSearchState.swift} +0 -0
- /package/app/Sources/{DiagnosticLog.swift → Core/System/DiagnosticLog.swift} +0 -0
- /package/app/Sources/{EventBus.swift → Core/System/EventBus.swift} +0 -0
- /package/app/Sources/{ProcessModel.swift → Core/System/ProcessModel.swift} +0 -0
- /package/app/Sources/{ProcessQuery.swift → Core/System/ProcessQuery.swift} +0 -0
- /package/app/Sources/{AdvisorLearningStore.swift → Core/Voice/AdvisorLearningStore.swift} +0 -0
- /package/app/Sources/{AgentSession.swift → Core/Voice/AgentSession.swift} +0 -0
- /package/app/Sources/{AudioProvider.swift → Core/Voice/AudioProvider.swift} +0 -0
- /package/app/Sources/{VoiceChatView.swift → Core/Voice/VoiceChatView.swift} +0 -0
- /package/app/Sources/{VoxClient.swift → Core/Voice/VoxClient.swift} +0 -0
- /package/app/Sources/{Project.swift → Core/Workspace/Project.swift} +0 -0
- /package/app/Sources/{ProjectScanner.swift → Core/Workspace/ProjectScanner.swift} +0 -0
- /package/app/Sources/{SessionLayerStore.swift → Core/Workspace/SessionLayerStore.swift} +0 -0
- /package/app/Sources/{SessionManager.swift → Core/Workspace/SessionManager.swift} +0 -0
- /package/app/Sources/{Terminal.swift → Core/Workspace/Terminal/Terminal.swift} +0 -0
- /package/app/Sources/{TerminalQuery.swift → Core/Workspace/Terminal/TerminalQuery.swift} +0 -0
- /package/app/Sources/{TerminalSynthesizer.swift → Core/Workspace/Terminal/TerminalSynthesizer.swift} +0 -0
- /package/app/Sources/{TmuxModel.swift → Core/Workspace/Tmux/TmuxModel.swift} +0 -0
- /package/app/Sources/{TmuxQuery.swift → Core/Workspace/Tmux/TmuxQuery.swift} +0 -0
- /package/app/Sources/{ActionRow.swift → UI/ActionRow.swift} +0 -0
- /package/app/Sources/{OrphanRow.swift → UI/OrphanRow.swift} +0 -0
- /package/app/Sources/{ProjectRow.swift → UI/ProjectRow.swift} +0 -0
- /package/app/Sources/{TabGroupRow.swift → UI/TabGroupRow.swift} +0 -0
- /package/app/Sources/{Theme.swift → UI/Theme.swift} +0 -0
package/app/Sources/{CommandModeState.swift → Core/Overlays/CommandMode/CommandModeState.swift}
RENAMED
|
@@ -91,11 +91,14 @@ final class CommandModeState: ObservableObject {
|
|
|
91
91
|
@Published var inventory = CommandModeInventory(activeLayer: nil, layerCount: 0, items: [])
|
|
92
92
|
@Published var chords: [Chord] = []
|
|
93
93
|
@Published var desktopSnapshot: DesktopInventorySnapshot?
|
|
94
|
-
@Published var selectedWindowIds: Set<UInt32> = []
|
|
94
|
+
@Published var selectedWindowIds: Set<UInt32> = [] {
|
|
95
|
+
didSet { syncSharedSelection() }
|
|
96
|
+
}
|
|
95
97
|
@Published var desktopMode: DesktopInventoryMode = .browsing
|
|
96
98
|
@Published var activePreset: FilterPreset? = nil
|
|
97
99
|
@Published var searchQuery: String = ""
|
|
98
100
|
@Published var isSearching: Bool = false
|
|
101
|
+
@Published var gridPreviewPlacement: PlacementSpec? = nil
|
|
99
102
|
|
|
100
103
|
// MARK: - Marquee Drag State
|
|
101
104
|
@Published var isDragging: Bool = false
|
|
@@ -140,6 +143,26 @@ final class CommandModeState: ObservableObject {
|
|
|
140
143
|
selectedWindowIds.first
|
|
141
144
|
}
|
|
142
145
|
|
|
146
|
+
var selectedWindowSummaryText: String {
|
|
147
|
+
let windows = flatWindowList.filter { selectedWindowIds.contains($0.id) }
|
|
148
|
+
let labels = windows.compactMap { $0.appName }.uniquePrefix(3)
|
|
149
|
+
guard !labels.isEmpty else { return "" }
|
|
150
|
+
if windows.count > labels.count {
|
|
151
|
+
return labels.joined(separator: " • ") + " +\(windows.count - labels.count)"
|
|
152
|
+
}
|
|
153
|
+
return labels.joined(separator: " • ")
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
var gridPreviewRegionLabel: String {
|
|
157
|
+
guard let placement = gridPreviewPlacement else { return "Full Screen" }
|
|
158
|
+
switch placement {
|
|
159
|
+
case .tile(let position):
|
|
160
|
+
return position.label
|
|
161
|
+
default:
|
|
162
|
+
return placement.wireValue
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
143
166
|
func isSelected(_ id: UInt32) -> Bool {
|
|
144
167
|
selectedWindowIds.contains(id)
|
|
145
168
|
}
|
|
@@ -344,6 +367,7 @@ final class CommandModeState: ObservableObject {
|
|
|
344
367
|
desktopSnapshot = buildDesktopInventory()
|
|
345
368
|
clearSelection()
|
|
346
369
|
desktopMode = .browsing
|
|
370
|
+
gridPreviewPlacement = nil
|
|
347
371
|
phase = .desktopInventory
|
|
348
372
|
// Don't call onPanelResize here — caller handles initial sizing
|
|
349
373
|
}
|
|
@@ -535,6 +559,7 @@ final class CommandModeState: ObservableObject {
|
|
|
535
559
|
if isSearching && selectedWindowIds.isEmpty { return false }
|
|
536
560
|
if isSearching { deactivateSearch() }
|
|
537
561
|
if !selectedWindowIds.isEmpty {
|
|
562
|
+
gridPreviewPlacement = nil
|
|
538
563
|
desktopMode = .gridPreview
|
|
539
564
|
}
|
|
540
565
|
return true
|
|
@@ -614,15 +639,54 @@ final class CommandModeState: ObservableObject {
|
|
|
614
639
|
|
|
615
640
|
private func handleGridPreviewKey(_ keyCode: UInt16) -> Bool {
|
|
616
641
|
switch keyCode {
|
|
617
|
-
case 53: // Escape —
|
|
618
|
-
|
|
642
|
+
case 53: // Escape — cancel preview, keep selection
|
|
643
|
+
gridPreviewPlacement = nil
|
|
644
|
+
desktopMode = .browsing
|
|
619
645
|
return true
|
|
620
646
|
|
|
621
647
|
case 36, 1: // Enter or s → apply the layout
|
|
622
|
-
showAndDistributeSelected()
|
|
648
|
+
showAndDistributeSelected(in: gridPreviewPlacement)
|
|
649
|
+
gridPreviewPlacement = nil
|
|
623
650
|
desktopMode = .browsing
|
|
624
651
|
return true
|
|
625
652
|
|
|
653
|
+
case 123:
|
|
654
|
+
gridPreviewPlacement = .tile(.left)
|
|
655
|
+
return true
|
|
656
|
+
case 124:
|
|
657
|
+
gridPreviewPlacement = .tile(.right)
|
|
658
|
+
return true
|
|
659
|
+
case 126:
|
|
660
|
+
gridPreviewPlacement = .tile(.top)
|
|
661
|
+
return true
|
|
662
|
+
case 125:
|
|
663
|
+
gridPreviewPlacement = .tile(.bottom)
|
|
664
|
+
return true
|
|
665
|
+
case 18:
|
|
666
|
+
gridPreviewPlacement = .tile(.topLeft)
|
|
667
|
+
return true
|
|
668
|
+
case 19:
|
|
669
|
+
gridPreviewPlacement = .tile(.topRight)
|
|
670
|
+
return true
|
|
671
|
+
case 20:
|
|
672
|
+
gridPreviewPlacement = .tile(.bottomLeft)
|
|
673
|
+
return true
|
|
674
|
+
case 21:
|
|
675
|
+
gridPreviewPlacement = .tile(.bottomRight)
|
|
676
|
+
return true
|
|
677
|
+
case 23:
|
|
678
|
+
gridPreviewPlacement = .tile(.leftThird)
|
|
679
|
+
return true
|
|
680
|
+
case 22:
|
|
681
|
+
gridPreviewPlacement = .tile(.centerThird)
|
|
682
|
+
return true
|
|
683
|
+
case 26:
|
|
684
|
+
gridPreviewPlacement = .tile(.rightThird)
|
|
685
|
+
return true
|
|
686
|
+
case 8:
|
|
687
|
+
gridPreviewPlacement = .tile(.center)
|
|
688
|
+
return true
|
|
689
|
+
|
|
626
690
|
default:
|
|
627
691
|
return true
|
|
628
692
|
}
|
|
@@ -795,20 +859,8 @@ final class CommandModeState: ObservableObject {
|
|
|
795
859
|
private func tileAllSelected(to position: TilePosition) {
|
|
796
860
|
let windows = flatWindowList.filter { selectedWindowIds.contains($0.id) }
|
|
797
861
|
guard !windows.isEmpty else { return }
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
if windows.count >= 2 && (position == .left || position == .right) {
|
|
801
|
-
distributeSelectedHorizontally()
|
|
802
|
-
return
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
DiagnosticLog.shared.info("Tile all \(windows.count): \(position.rawValue)")
|
|
806
|
-
for win in windows {
|
|
807
|
-
WindowTiler.tileWindowById(wid: win.id, pid: win.pid, to: position)
|
|
808
|
-
}
|
|
809
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in
|
|
810
|
-
self?.desktopSnapshot = self?.buildDesktopInventory()
|
|
811
|
-
}
|
|
862
|
+
DiagnosticLog.shared.info("Grid selected \(windows.count): \(position.rawValue)")
|
|
863
|
+
showAndDistributeSelected(in: .tile(position))
|
|
812
864
|
}
|
|
813
865
|
|
|
814
866
|
private func distributeSelectedHorizontally() {
|
|
@@ -849,28 +901,36 @@ final class CommandModeState: ObservableObject {
|
|
|
849
901
|
}
|
|
850
902
|
|
|
851
903
|
/// Show all selected windows AND distribute in smart grid — single batch operation
|
|
852
|
-
func showAndDistributeSelected() {
|
|
904
|
+
func showAndDistributeSelected(in placement: PlacementSpec? = nil) {
|
|
853
905
|
let windows = flatWindowList.filter { selectedWindowIds.contains($0.id) }
|
|
854
906
|
guard !windows.isEmpty else { return }
|
|
855
907
|
savePositions(for: windows)
|
|
856
|
-
WindowTiler.batchRaiseAndDistribute(
|
|
908
|
+
WindowTiler.batchRaiseAndDistribute(
|
|
909
|
+
windows: windows.map { (wid: $0.id, pid: $0.pid) },
|
|
910
|
+
region: placement?.fractions
|
|
911
|
+
)
|
|
857
912
|
let shape = WindowTiler.gridShape(for: windows.count)
|
|
858
913
|
let grid = shape.map(String.init).joined(separator: "+")
|
|
859
|
-
|
|
914
|
+
let region = placement.map { " in \(self.regionLabel(for: $0))" } ?? ""
|
|
915
|
+
flash("\(windows.count) windows\(region) [\(grid)]")
|
|
860
916
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in
|
|
861
917
|
self?.desktopSnapshot = self?.buildDesktopInventory()
|
|
862
918
|
}
|
|
863
919
|
}
|
|
864
920
|
|
|
865
921
|
/// Distribute selected in smart grid without raising
|
|
866
|
-
func distributeSelected() {
|
|
922
|
+
func distributeSelected(in placement: PlacementSpec? = nil) {
|
|
867
923
|
let windows = flatWindowList.filter { selectedWindowIds.contains($0.id) }
|
|
868
924
|
guard !windows.isEmpty else { return }
|
|
869
925
|
savePositions(for: windows)
|
|
870
|
-
WindowTiler.batchRaiseAndDistribute(
|
|
926
|
+
WindowTiler.batchRaiseAndDistribute(
|
|
927
|
+
windows: windows.map { (wid: $0.id, pid: $0.pid) },
|
|
928
|
+
region: placement?.fractions
|
|
929
|
+
)
|
|
871
930
|
let shape = WindowTiler.gridShape(for: windows.count)
|
|
872
931
|
let grid = shape.map(String.init).joined(separator: "+")
|
|
873
|
-
|
|
932
|
+
let region = placement.map { " in \(self.regionLabel(for: $0))" } ?? ""
|
|
933
|
+
flash("\(windows.count) windows\(region) [\(grid)]")
|
|
874
934
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in
|
|
875
935
|
self?.desktopSnapshot = self?.buildDesktopInventory()
|
|
876
936
|
}
|
|
@@ -960,6 +1020,36 @@ final class CommandModeState: ObservableObject {
|
|
|
960
1020
|
onDismiss?()
|
|
961
1021
|
}
|
|
962
1022
|
|
|
1023
|
+
private func syncSharedSelection() {
|
|
1024
|
+
guard !selectedWindowIds.isEmpty else {
|
|
1025
|
+
WindowSelectionStore.shared.clear(source: "desktop-inventory")
|
|
1026
|
+
return
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
let summaries = flatWindowList
|
|
1030
|
+
.filter { selectedWindowIds.contains($0.id) }
|
|
1031
|
+
.map {
|
|
1032
|
+
SelectedWindowSummary(
|
|
1033
|
+
wid: $0.id,
|
|
1034
|
+
app: $0.appName ?? "Window",
|
|
1035
|
+
title: $0.title,
|
|
1036
|
+
latticesSession: $0.latticesSession
|
|
1037
|
+
)
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
guard !summaries.isEmpty else { return }
|
|
1041
|
+
WindowSelectionStore.shared.setSelection(summaries, source: "desktop-inventory")
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
private func regionLabel(for placement: PlacementSpec) -> String {
|
|
1045
|
+
switch placement {
|
|
1046
|
+
case .tile(let position):
|
|
1047
|
+
return position.label
|
|
1048
|
+
default:
|
|
1049
|
+
return placement.wireValue
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
|
|
963
1053
|
// MARK: - Inventory Builder
|
|
964
1054
|
|
|
965
1055
|
private func buildInventory() -> CommandModeInventory {
|
|
@@ -1360,3 +1450,16 @@ final class CommandModeState: ObservableObject {
|
|
|
1360
1450
|
return chords
|
|
1361
1451
|
}
|
|
1362
1452
|
}
|
|
1453
|
+
|
|
1454
|
+
private extension Sequence where Element == String {
|
|
1455
|
+
func uniquePrefix(_ count: Int) -> [String] {
|
|
1456
|
+
var seen = Set<String>()
|
|
1457
|
+
var result: [String] = []
|
|
1458
|
+
for item in self where !seen.contains(item) {
|
|
1459
|
+
seen.insert(item)
|
|
1460
|
+
result.append(item)
|
|
1461
|
+
if result.count == count { break }
|
|
1462
|
+
}
|
|
1463
|
+
return result
|
|
1464
|
+
}
|
|
1465
|
+
}
|