@lattices/cli 0.4.5 → 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/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/Sources/{AppDelegate.swift → AppShell/AppDelegate.swift} +4 -0
- package/app/Sources/{AppShellView.swift → AppShell/AppShellView.swift} +10 -1
- package/app/Sources/{HotkeyStore.swift → Core/Actions/HotkeyStore.swift} +2 -1
- package/app/Sources/{IntentEngine.swift → Core/Actions/IntentEngine.swift} +44 -26
- package/app/Sources/Core/Actions/IntentSchema.swift +94 -0
- package/app/Sources/{Intents → Core/Actions/Intents}/LatticeIntent.swift +0 -25
- package/app/Sources/{VoiceIntentResolver.swift → Core/Actions/VoiceIntentResolver.swift} +46 -4
- package/app/Sources/{DesktopModel.swift → Core/Desktop/DesktopModel.swift} +2 -8
- package/app/Sources/Core/Desktop/SessionWindowLocator.swift +139 -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} +24 -108
- package/app/Sources/{CommandModeState.swift → Core/Overlays/CommandMode/CommandModeState.swift} +127 -24
- package/app/Sources/{CommandModeView.swift → Core/Overlays/CommandMode/CommandModeView.swift} +488 -55
- package/app/Sources/Core/Overlays/CommandPalette/CommandPaletteWindow.swift +67 -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/Core/Overlays/OmniSearch/OmniSearchWindow.swift +94 -0
- package/app/Sources/Core/Overlays/OverlayPanelShell.swift +241 -0
- package/app/Sources/{ScreenMapState.swift → Core/Overlays/ScreenMap/ScreenMapState.swift} +25 -1
- package/app/Sources/{VoiceCommandWindow.swift → Core/Overlays/Voice/VoiceCommandWindow.swift} +46 -74
- package/docs/component-extraction-roadmap.md +392 -0
- package/package.json +3 -1
- package/app/Sources/CommandPaletteWindow.swift +0 -134
- package/app/Sources/OmniSearchWindow.swift +0 -165
- /package/app/Sources/{App.swift → AppShell/App.swift} +0 -0
- /package/app/Sources/{AppUpdater.swift → AppShell/AppUpdater.swift} +0 -0
- /package/app/Sources/{CliActionLauncher.swift → AppShell/CliActionLauncher.swift} +0 -0
- /package/app/Sources/{HomeDashboardView.swift → AppShell/HomeDashboardView.swift} +0 -0
- /package/app/Sources/{KeyRecorderView.swift → AppShell/KeyRecorderView.swift} +0 -0
- /package/app/Sources/{LatticesRuntime.swift → AppShell/LatticesRuntime.swift} +0 -0
- /package/app/Sources/{MainView.swift → AppShell/MainView.swift} +0 -0
- /package/app/Sources/{MainWindow.swift → AppShell/MainWindow.swift} +0 -0
- /package/app/Sources/{OnboardingView.swift → AppShell/OnboardingView.swift} +0 -0
- /package/app/Sources/{Preferences.swift → AppShell/Preferences.swift} +0 -0
- /package/app/Sources/{SettingsView.swift → AppShell/SettingsView.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/{PaletteCommand.swift → Core/Actions/PaletteCommand.swift} +0 -0
- /package/app/Sources/{CompanionActivityLog.swift → Core/Companion/CompanionActivityLog.swift} +0 -0
- /package/app/Sources/{CompanionKeyboardController.swift → Core/Companion/CompanionKeyboardController.swift} +0 -0
- /package/app/Sources/{LatticesCompanionBridgeServer.swift → Core/Companion/LatticesCompanionBridgeServer.swift} +0 -0
- /package/app/Sources/{LatticesCompanionCockpit.swift → Core/Companion/LatticesCompanionCockpit.swift} +0 -0
- /package/app/Sources/{LatticesCompanionSecurityCoordinator.swift → Core/Companion/LatticesCompanionSecurityCoordinator.swift} +0 -0
- /package/app/Sources/{LatticesCompanionTrackpadController.swift → Core/Companion/LatticesCompanionTrackpadController.swift} +0 -0
- /package/app/Sources/{LatticesDeckHost.swift → Core/Companion/LatticesDeckHost.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/{LatticesApi.swift → Core/Daemon/LatticesApi.swift} +0 -0
- /package/app/Sources/{AccessibilityTextExtractor.swift → Core/Desktop/AccessibilityTextExtractor.swift} +0 -0
- /package/app/Sources/{AppTypeClassifier.swift → Core/Desktop/AppTypeClassifier.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/{MouseFinder.swift → Core/Desktop/MouseFinder.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/{WindowDragSnapController.swift → Core/Desktop/WindowDragSnapController.swift} +0 -0
- /package/app/Sources/{MouseGestureConfig.swift → Core/Input/MouseGestureConfig.swift} +0 -0
- /package/app/Sources/{MouseGestureController.swift → Core/Input/MouseGestureController.swift} +0 -0
- /package/app/Sources/{MouseInputDeviceStore.swift → Core/Input/MouseInputDeviceStore.swift} +0 -0
- /package/app/Sources/{MouseInputEventViewer.swift → Core/Input/MouseInputEventViewer.swift} +0 -0
- /package/app/Sources/{MouseShortcutStore.swift → Core/Input/MouseShortcutStore.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/{CheatSheetHUD.swift → Core/Overlays/HUD/CheatSheetHUD.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/{OmniSearchView.swift → Core/Overlays/OmniSearch/OmniSearchView.swift} +0 -0
- /package/app/Sources/{ScreenMapView.swift → Core/Overlays/ScreenMap/ScreenMapView.swift} +0 -0
- /package/app/Sources/{ScreenMapWindowController.swift → Core/Overlays/ScreenMap/ScreenMapWindowController.swift} +0 -0
- /package/app/Sources/{PiAuthNextStepCard.swift → Core/Pi/PiAuthNextStepCard.swift} +0 -0
- /package/app/Sources/{PiAuthPromptCard.swift → Core/Pi/PiAuthPromptCard.swift} +0 -0
- /package/app/Sources/{PiChatDock.swift → Core/Pi/PiChatDock.swift} +0 -0
- /package/app/Sources/{PiChatSession.swift → Core/Pi/PiChatSession.swift} +0 -0
- /package/app/Sources/{PiInstallCallout.swift → Core/Pi/PiInstallCallout.swift} +0 -0
- /package/app/Sources/{PiProviderSetupCallout.swift → Core/Pi/PiProviderSetupCallout.swift} +0 -0
- /package/app/Sources/{PiWorkspaceView.swift → Core/Pi/PiWorkspaceView.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/{PermissionChecker.swift → Core/System/PermissionChecker.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/{SystemTelemetryMonitor.swift → Core/System/SystemTelemetryMonitor.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/{HandsOffSession.swift → Core/Voice/HandsOffSession.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/{WorkspaceManager.swift → Core/Workspace/WorkspaceManager.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
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import SwiftUI
|
|
3
|
+
|
|
4
|
+
final class CommandPaletteWindow {
|
|
5
|
+
static let shared = CommandPaletteWindow()
|
|
6
|
+
|
|
7
|
+
private var panel: NSPanel?
|
|
8
|
+
private var scanner: ProjectScanner?
|
|
9
|
+
|
|
10
|
+
func configure(scanner: ProjectScanner) {
|
|
11
|
+
self.scanner = scanner
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
var isVisible: Bool { panel?.isVisible ?? false }
|
|
15
|
+
|
|
16
|
+
func toggle() {
|
|
17
|
+
if let p = panel, p.isVisible {
|
|
18
|
+
dismiss()
|
|
19
|
+
} else {
|
|
20
|
+
show()
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
func show() {
|
|
25
|
+
// Always rebuild for fresh command state
|
|
26
|
+
dismiss()
|
|
27
|
+
|
|
28
|
+
guard let scanner = scanner else { return }
|
|
29
|
+
|
|
30
|
+
// Ensure projects are up to date (full scan if list is empty,
|
|
31
|
+
// e.g. palette opened via hotkey before main popover appeared)
|
|
32
|
+
if scanner.projects.isEmpty {
|
|
33
|
+
scanner.scan()
|
|
34
|
+
} else {
|
|
35
|
+
scanner.refreshStatus()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let commands = CommandBuilder.build(scanner: scanner)
|
|
39
|
+
let view = CommandPaletteView(commands: commands) { [weak self] in
|
|
40
|
+
self?.dismiss()
|
|
41
|
+
}
|
|
42
|
+
.preferredColorScheme(.dark)
|
|
43
|
+
|
|
44
|
+
let panel = OverlayPanelShell.makePanel(
|
|
45
|
+
config: .init(
|
|
46
|
+
size: NSSize(width: 540, height: 440),
|
|
47
|
+
styleMask: [.nonactivatingPanel],
|
|
48
|
+
background: .material(.popover),
|
|
49
|
+
cornerRadius: 14,
|
|
50
|
+
hidesOnDeactivate: true,
|
|
51
|
+
isMovableByWindowBackground: true
|
|
52
|
+
),
|
|
53
|
+
rootView: view
|
|
54
|
+
)
|
|
55
|
+
OverlayPanelShell.position(panel, placement: .centered(yOffsetRatio: 0.1))
|
|
56
|
+
OverlayPanelShell.present(panel)
|
|
57
|
+
|
|
58
|
+
self.panel = panel
|
|
59
|
+
AppDelegate.updateActivationPolicy()
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
func dismiss() {
|
|
63
|
+
panel?.orderOut(nil)
|
|
64
|
+
panel = nil
|
|
65
|
+
AppDelegate.updateActivationPolicy()
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -168,34 +168,11 @@ struct HUDRightBar: View {
|
|
|
168
168
|
.foregroundColor(Palette.textDim)
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
.strokeBorder(Palette.border, lineWidth: 0.5)
|
|
177
|
-
)
|
|
178
|
-
|
|
179
|
-
if let image = previewModel.image(for: window.wid) {
|
|
180
|
-
Image(nsImage: image)
|
|
181
|
-
.resizable()
|
|
182
|
-
.aspectRatio(contentMode: .fit)
|
|
183
|
-
.clipShape(RoundedRectangle(cornerRadius: 8))
|
|
184
|
-
.padding(8)
|
|
185
|
-
} else if previewModel.isLoading(window.wid) {
|
|
186
|
-
previewPlaceholder(
|
|
187
|
-
icon: "photo",
|
|
188
|
-
title: "Capturing preview",
|
|
189
|
-
subtitle: window.app
|
|
190
|
-
)
|
|
191
|
-
} else {
|
|
192
|
-
previewPlaceholder(
|
|
193
|
-
icon: "eye.slash",
|
|
194
|
-
title: "Preview unavailable",
|
|
195
|
-
subtitle: window.app
|
|
196
|
-
)
|
|
197
|
-
}
|
|
198
|
-
}
|
|
171
|
+
WindowPreviewCard(
|
|
172
|
+
image: previewModel.image(for: window.wid),
|
|
173
|
+
isLoading: previewModel.isLoading(window.wid),
|
|
174
|
+
appName: window.app
|
|
175
|
+
)
|
|
199
176
|
.frame(maxWidth: .infinity)
|
|
200
177
|
.frame(height: 190)
|
|
201
178
|
.clipped()
|
|
@@ -207,22 +184,6 @@ struct HUDRightBar: View {
|
|
|
207
184
|
}
|
|
208
185
|
}
|
|
209
186
|
|
|
210
|
-
private func previewPlaceholder(icon: String, title: String, subtitle: String) -> some View {
|
|
211
|
-
VStack(spacing: 8) {
|
|
212
|
-
Image(systemName: icon)
|
|
213
|
-
.font(.system(size: 18, weight: .medium))
|
|
214
|
-
.foregroundColor(Palette.textMuted.opacity(0.7))
|
|
215
|
-
Text(title)
|
|
216
|
-
.font(Typo.monoBold(10))
|
|
217
|
-
.foregroundColor(Palette.textMuted)
|
|
218
|
-
Text(subtitle)
|
|
219
|
-
.font(Typo.mono(9))
|
|
220
|
-
.foregroundColor(Palette.textDim)
|
|
221
|
-
.lineLimit(1)
|
|
222
|
-
}
|
|
223
|
-
.padding(16)
|
|
224
|
-
}
|
|
225
|
-
|
|
226
187
|
private func projectPreviewWindow(_ project: Project) -> WindowEntry? {
|
|
227
188
|
guard project.isRunning else { return nil }
|
|
228
189
|
return desktop.windowForSession(project.sessionName)
|
|
@@ -487,39 +448,25 @@ struct HUDHoverPreviewView: View {
|
|
|
487
448
|
}
|
|
488
449
|
|
|
489
450
|
ZStack {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
.
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
.
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
} else if previewModel.isLoading(window.wid) {
|
|
507
|
-
previewPlaceholder(
|
|
508
|
-
icon: "photo",
|
|
509
|
-
title: "Capturing preview",
|
|
510
|
-
subtitle: window.app
|
|
511
|
-
)
|
|
512
|
-
} else {
|
|
513
|
-
previewPlaceholder(
|
|
514
|
-
icon: "eye.slash",
|
|
515
|
-
title: "Preview unavailable",
|
|
516
|
-
subtitle: window.app
|
|
517
|
-
)
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
if isHoldingPreviousPreview(for: window) {
|
|
521
|
-
loadingOverlay(label: "Loading next preview")
|
|
451
|
+
WindowPreviewCard(
|
|
452
|
+
image: renderedImage,
|
|
453
|
+
isLoading: previewModel.isLoading(window.wid),
|
|
454
|
+
appName: window.app,
|
|
455
|
+
style: WindowPreviewCardStyle(
|
|
456
|
+
containerCornerRadius: 12,
|
|
457
|
+
imageCornerRadius: 9,
|
|
458
|
+
imagePadding: 10,
|
|
459
|
+
background: Palette.bg.opacity(0.96),
|
|
460
|
+
border: Palette.border
|
|
461
|
+
),
|
|
462
|
+
holdingPreviousPreview: isHoldingPreviousPreview(for: window)
|
|
463
|
+
) {
|
|
464
|
+
if isHoldingPreviousPreview(for: window) {
|
|
465
|
+
loadingOverlay(label: "Loading next preview")
|
|
466
|
+
}
|
|
522
467
|
}
|
|
468
|
+
.id(renderedWindowID ?? window.wid)
|
|
469
|
+
.transition(.opacity)
|
|
523
470
|
}
|
|
524
471
|
.frame(height: 190)
|
|
525
472
|
}
|
|
@@ -591,22 +538,6 @@ struct HUDHoverPreviewView: View {
|
|
|
591
538
|
}
|
|
592
539
|
}
|
|
593
540
|
|
|
594
|
-
private func previewPlaceholder(icon: String, title: String, subtitle: String) -> some View {
|
|
595
|
-
VStack(spacing: 8) {
|
|
596
|
-
Image(systemName: icon)
|
|
597
|
-
.font(.system(size: 18, weight: .medium))
|
|
598
|
-
.foregroundColor(Palette.textMuted.opacity(0.7))
|
|
599
|
-
Text(title)
|
|
600
|
-
.font(Typo.monoBold(10))
|
|
601
|
-
.foregroundColor(Palette.textMuted)
|
|
602
|
-
Text(subtitle)
|
|
603
|
-
.font(Typo.mono(9))
|
|
604
|
-
.foregroundColor(Palette.textDim)
|
|
605
|
-
.lineLimit(1)
|
|
606
|
-
}
|
|
607
|
-
.padding(16)
|
|
608
|
-
}
|
|
609
|
-
|
|
610
541
|
private func loadingOverlay(label: String) -> some View {
|
|
611
542
|
VStack {
|
|
612
543
|
Spacer()
|
|
@@ -663,112 +594,3 @@ struct HUDHoverPreviewView: View {
|
|
|
663
594
|
}
|
|
664
595
|
}
|
|
665
596
|
}
|
|
666
|
-
|
|
667
|
-
final class WindowPreviewStore: ObservableObject {
|
|
668
|
-
static let shared = WindowPreviewStore()
|
|
669
|
-
|
|
670
|
-
@Published private var images: [UInt32: NSImage] = [:]
|
|
671
|
-
@Published private var loading: Set<UInt32> = []
|
|
672
|
-
|
|
673
|
-
private var lastAttemptAt: [UInt32: Date] = [:]
|
|
674
|
-
private var accessOrder: [UInt32] = [] // LRU: oldest first
|
|
675
|
-
private let maxCached = 15
|
|
676
|
-
private let queue = DispatchQueue(label: "com.arach.lattices.hud-preview", qos: .userInitiated)
|
|
677
|
-
private let previewMaxSize = NSSize(width: 360, height: 190)
|
|
678
|
-
|
|
679
|
-
func image(for wid: UInt32) -> NSImage? {
|
|
680
|
-
if images[wid] != nil { touchLRU(wid) }
|
|
681
|
-
return images[wid]
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
private func touchLRU(_ wid: UInt32) {
|
|
685
|
-
accessOrder.removeAll { $0 == wid }
|
|
686
|
-
accessOrder.append(wid)
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
private func evictIfNeeded() {
|
|
690
|
-
while images.count > maxCached, let oldest = accessOrder.first {
|
|
691
|
-
accessOrder.removeFirst()
|
|
692
|
-
images.removeValue(forKey: oldest)
|
|
693
|
-
lastAttemptAt.removeValue(forKey: oldest)
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
func hasSettled(_ wid: UInt32) -> Bool {
|
|
698
|
-
images[wid] != nil || (lastAttemptAt[wid] != nil && !loading.contains(wid))
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
func isLoading(_ wid: UInt32) -> Bool {
|
|
702
|
-
loading.contains(wid)
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
func prewarm(windows: [WindowEntry], limit: Int = 4) {
|
|
706
|
-
for window in windows.prefix(limit) {
|
|
707
|
-
load(window: window)
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
func load(window: WindowEntry) {
|
|
712
|
-
if images[window.wid] != nil || loading.contains(window.wid) {
|
|
713
|
-
return
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
let now = Date()
|
|
717
|
-
if let lastAttemptAt = lastAttemptAt[window.wid], now.timeIntervalSince(lastAttemptAt) < 1.0 {
|
|
718
|
-
return
|
|
719
|
-
}
|
|
720
|
-
lastAttemptAt[window.wid] = now
|
|
721
|
-
|
|
722
|
-
loading.insert(window.wid)
|
|
723
|
-
let wid = window.wid
|
|
724
|
-
let frame = window.frame
|
|
725
|
-
let startedAt = Date()
|
|
726
|
-
|
|
727
|
-
queue.async { [weak self] in
|
|
728
|
-
guard let self else { return }
|
|
729
|
-
|
|
730
|
-
let cgImage = CGWindowListCreateImage(
|
|
731
|
-
.null,
|
|
732
|
-
.optionIncludingWindow,
|
|
733
|
-
CGWindowID(wid),
|
|
734
|
-
[.boundsIgnoreFraming, .nominalResolution]
|
|
735
|
-
)
|
|
736
|
-
|
|
737
|
-
let image = cgImage.map {
|
|
738
|
-
NSImage(
|
|
739
|
-
cgImage: $0,
|
|
740
|
-
size: self.previewSize(for: frame)
|
|
741
|
-
)
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
DispatchQueue.main.async {
|
|
745
|
-
self.loading.remove(wid)
|
|
746
|
-
let elapsedMs = Int(Date().timeIntervalSince(startedAt) * 1000)
|
|
747
|
-
if let image {
|
|
748
|
-
self.images[wid] = image
|
|
749
|
-
self.touchLRU(wid)
|
|
750
|
-
self.evictIfNeeded()
|
|
751
|
-
if elapsedMs >= 80 {
|
|
752
|
-
DiagnosticLog.shared.info("HUDPreview: captured wid=\(wid) in \(elapsedMs)ms")
|
|
753
|
-
}
|
|
754
|
-
} else {
|
|
755
|
-
DiagnosticLog.shared.info("HUDPreview: capture unavailable wid=\(wid) after \(elapsedMs)ms")
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
private func previewSize(for frame: WindowFrame) -> NSSize {
|
|
762
|
-
let width = max(CGFloat(frame.w), CGFloat(1))
|
|
763
|
-
let height = max(CGFloat(frame.h), CGFloat(1))
|
|
764
|
-
let scale = min(
|
|
765
|
-
previewMaxSize.width / width,
|
|
766
|
-
previewMaxSize.height / height,
|
|
767
|
-
CGFloat(1)
|
|
768
|
-
)
|
|
769
|
-
return NSSize(
|
|
770
|
-
width: max(CGFloat(1), width * scale),
|
|
771
|
-
height: max(CGFloat(1), height * scale)
|
|
772
|
-
)
|
|
773
|
-
}
|
|
774
|
-
}
|
|
@@ -25,33 +25,20 @@ final class LauncherHUD {
|
|
|
25
25
|
let view = LauncherView(dismiss: { [weak self] in self?.dismiss() })
|
|
26
26
|
.preferredColorScheme(.dark)
|
|
27
27
|
|
|
28
|
-
let
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
let p = OverlayPanelShell.makePanel(
|
|
29
|
+
config: .init(
|
|
30
|
+
size: NSSize(width: 420, height: 480),
|
|
31
|
+
styleMask: [.borderless, .nonactivatingPanel],
|
|
32
|
+
background: .clear,
|
|
33
|
+
hidesOnDeactivate: false,
|
|
34
|
+
isMovableByWindowBackground: false
|
|
35
|
+
),
|
|
36
|
+
rootView: view
|
|
35
37
|
)
|
|
36
|
-
p.
|
|
37
|
-
p.backgroundColor = .clear
|
|
38
|
-
p.level = .floating
|
|
39
|
-
p.hasShadow = true
|
|
40
|
-
p.hidesOnDeactivate = false
|
|
41
|
-
p.isReleasedWhenClosed = false
|
|
42
|
-
p.isMovableByWindowBackground = false
|
|
43
|
-
p.contentView = hosting
|
|
44
|
-
|
|
45
|
-
// Center on mouse screen
|
|
46
|
-
let mouseLocation = NSEvent.mouseLocation
|
|
47
|
-
let screen = NSScreen.screens.first(where: { $0.frame.contains(mouseLocation) }) ?? NSScreen.main ?? NSScreen.screens.first!
|
|
48
|
-
let screenFrame = screen.visibleFrame
|
|
49
|
-
let x = screenFrame.midX - 210
|
|
50
|
-
let y = screenFrame.midY - 240 + (screenFrame.height * 0.08)
|
|
51
|
-
p.setFrameOrigin(NSPoint(x: x, y: y))
|
|
38
|
+
OverlayPanelShell.position(p, placement: .mouseScreenCentered(yOffsetRatio: 0.08))
|
|
52
39
|
|
|
53
40
|
p.alphaValue = 0
|
|
54
|
-
p
|
|
41
|
+
OverlayPanelShell.present(p, activate: false, makeKey: false, orderFrontRegardless: true)
|
|
55
42
|
|
|
56
43
|
NSAnimationContext.runAnimationGroup { ctx in
|
|
57
44
|
ctx.duration = 0.12
|
|
@@ -86,7 +73,6 @@ final class LauncherHUD {
|
|
|
86
73
|
globalMonitor = NSEvent.addGlobalMonitorForEvents(matching: [.leftMouseDown, .rightMouseDown]) { [weak self] event in
|
|
87
74
|
// Don't dismiss if clicking inside the panel
|
|
88
75
|
guard let panel = self?.panel else { return }
|
|
89
|
-
let loc = event.locationInWindow
|
|
90
76
|
if !panel.frame.contains(NSEvent.mouseLocation) {
|
|
91
77
|
self?.dismiss()
|
|
92
78
|
}
|
|
@@ -115,7 +101,7 @@ struct LauncherView: View {
|
|
|
115
101
|
let q = query.lowercased()
|
|
116
102
|
return scanner.projects.filter {
|
|
117
103
|
$0.name.lowercased().contains(q) ||
|
|
118
|
-
|
|
104
|
+
$0.paneSummary.lowercased().contains(q)
|
|
119
105
|
}
|
|
120
106
|
}
|
|
121
107
|
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import SwiftUI
|
|
3
|
+
|
|
4
|
+
final class OmniSearchWindow {
|
|
5
|
+
static let shared = OmniSearchWindow()
|
|
6
|
+
|
|
7
|
+
private var panel: NSPanel?
|
|
8
|
+
private var keyMonitor: Any?
|
|
9
|
+
private var state: OmniSearchState?
|
|
10
|
+
|
|
11
|
+
var isVisible: Bool { panel?.isVisible ?? false }
|
|
12
|
+
|
|
13
|
+
func toggle() {
|
|
14
|
+
if isVisible {
|
|
15
|
+
dismiss()
|
|
16
|
+
} else {
|
|
17
|
+
show()
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
func show() {
|
|
22
|
+
if let p = panel, p.isVisible {
|
|
23
|
+
p.makeKeyAndOrderFront(nil)
|
|
24
|
+
NSApp.activate(ignoringOtherApps: true)
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Fresh state each time
|
|
29
|
+
let searchState = OmniSearchState()
|
|
30
|
+
state = searchState
|
|
31
|
+
|
|
32
|
+
let view = OmniSearchView(state: searchState) { [weak self] in
|
|
33
|
+
self?.dismiss()
|
|
34
|
+
}
|
|
35
|
+
.preferredColorScheme(.dark)
|
|
36
|
+
|
|
37
|
+
let p = OverlayPanelShell.makePanel(
|
|
38
|
+
config: .init(
|
|
39
|
+
size: NSSize(width: 520, height: 480),
|
|
40
|
+
styleMask: [.titled, .closable, .resizable, .utilityWindow, .nonactivatingPanel],
|
|
41
|
+
title: "Search",
|
|
42
|
+
titleVisible: .hidden,
|
|
43
|
+
titlebarAppearsTransparent: true,
|
|
44
|
+
background: .material(.popover),
|
|
45
|
+
cornerRadius: 14,
|
|
46
|
+
hidesOnDeactivate: false,
|
|
47
|
+
isMovableByWindowBackground: true,
|
|
48
|
+
minSize: NSSize(width: 400, height: 300),
|
|
49
|
+
maxSize: NSSize(width: 700, height: 700),
|
|
50
|
+
activatesOnMouseDown: true,
|
|
51
|
+
appearance: NSAppearance(named: .darkAqua)
|
|
52
|
+
),
|
|
53
|
+
rootView: view
|
|
54
|
+
)
|
|
55
|
+
p.hidesOnDeactivate = false
|
|
56
|
+
p.becomesKeyOnlyIfNeeded = false
|
|
57
|
+
OverlayPanelShell.position(p, placement: .centered(yOffsetRatio: 0.125))
|
|
58
|
+
OverlayPanelShell.present(p)
|
|
59
|
+
panel = p
|
|
60
|
+
|
|
61
|
+
// Key monitor: Escape → dismiss, arrow keys → navigate, Enter → activate
|
|
62
|
+
keyMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in
|
|
63
|
+
guard self?.panel?.isKeyWindow == true else { return event }
|
|
64
|
+
|
|
65
|
+
switch event.keyCode {
|
|
66
|
+
case 53: // Escape
|
|
67
|
+
self?.dismiss()
|
|
68
|
+
return nil
|
|
69
|
+
case 125: // ↓
|
|
70
|
+
self?.state?.moveSelection(1)
|
|
71
|
+
return nil
|
|
72
|
+
case 126: // ↑
|
|
73
|
+
self?.state?.moveSelection(-1)
|
|
74
|
+
return nil
|
|
75
|
+
case 36: // Enter
|
|
76
|
+
self?.state?.activateSelected()
|
|
77
|
+
self?.dismiss()
|
|
78
|
+
return nil
|
|
79
|
+
default:
|
|
80
|
+
return event
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
func dismiss() {
|
|
86
|
+
panel?.orderOut(nil)
|
|
87
|
+
panel = nil
|
|
88
|
+
state = nil
|
|
89
|
+
if let monitor = keyMonitor {
|
|
90
|
+
NSEvent.removeMonitor(monitor)
|
|
91
|
+
keyMonitor = nil
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|