@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
|
@@ -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
|
+
}
|
|
@@ -254,6 +254,7 @@ struct CheatSheetView: View {
|
|
|
254
254
|
// Center + Distribute
|
|
255
255
|
shortcutRow(action: .tileCenter)
|
|
256
256
|
shortcutRow(action: .tileDistribute)
|
|
257
|
+
shortcutRow(action: .tileTypeGrid)
|
|
257
258
|
|
|
258
259
|
// Hovered shortcut detail
|
|
259
260
|
if let hovered = hoveredAction, let binding = hotkeyStore.bindings[hovered] {
|
|
@@ -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
|
|
|
@@ -3,7 +3,10 @@ import SwiftUI
|
|
|
3
3
|
struct OmniSearchView: View {
|
|
4
4
|
@ObservedObject var state: OmniSearchState
|
|
5
5
|
var onDismiss: () -> Void
|
|
6
|
+
var isEmbedded: Bool = false
|
|
6
7
|
|
|
8
|
+
@ObservedObject private var ocrModel = OcrModel.shared
|
|
9
|
+
@State private var expandedOcrWindow: UInt32?
|
|
7
10
|
@FocusState private var searchFocused: Bool
|
|
8
11
|
|
|
9
12
|
var body: some View {
|
|
@@ -46,8 +49,22 @@ struct OmniSearchView: View {
|
|
|
46
49
|
resultsView
|
|
47
50
|
}
|
|
48
51
|
}
|
|
49
|
-
.frame(
|
|
50
|
-
|
|
52
|
+
.frame(
|
|
53
|
+
minWidth: isEmbedded ? 0 : 520,
|
|
54
|
+
idealWidth: isEmbedded ? nil : 520,
|
|
55
|
+
maxWidth: isEmbedded ? .infinity : 700,
|
|
56
|
+
minHeight: isEmbedded ? 0 : 360,
|
|
57
|
+
idealHeight: isEmbedded ? nil : 480,
|
|
58
|
+
maxHeight: isEmbedded ? .infinity : 600,
|
|
59
|
+
alignment: .top
|
|
60
|
+
)
|
|
61
|
+
.background {
|
|
62
|
+
if isEmbedded {
|
|
63
|
+
Palette.bg
|
|
64
|
+
} else {
|
|
65
|
+
PanelBackground()
|
|
66
|
+
}
|
|
67
|
+
}
|
|
51
68
|
.preferredColorScheme(.dark)
|
|
52
69
|
.onAppear {
|
|
53
70
|
searchFocused = true
|
|
@@ -234,6 +251,10 @@ struct OmniSearchView: View {
|
|
|
234
251
|
}
|
|
235
252
|
.padding(.horizontal, 14)
|
|
236
253
|
}
|
|
254
|
+
|
|
255
|
+
if !recentOcrResults.isEmpty {
|
|
256
|
+
ocrResultsSection
|
|
257
|
+
}
|
|
237
258
|
} else {
|
|
238
259
|
Text("Loading...")
|
|
239
260
|
.font(Typo.mono(11))
|
|
@@ -245,6 +266,105 @@ struct OmniSearchView: View {
|
|
|
245
266
|
}
|
|
246
267
|
}
|
|
247
268
|
|
|
269
|
+
private var recentOcrResults: [OcrWindowResult] {
|
|
270
|
+
Array(ocrModel.results.values.sorted { $0.timestamp > $1.timestamp }.prefix(10))
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private var ocrResultsSection: some View {
|
|
274
|
+
summarySection("SCREEN TEXT", icon: "doc.text.magnifyingglass", count: ocrModel.results.count) {
|
|
275
|
+
ForEach(recentOcrResults, id: \.wid) { result in
|
|
276
|
+
ocrResultRow(result)
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
private func ocrResultRow(_ result: OcrWindowResult) -> some View {
|
|
282
|
+
let isExpanded = expandedOcrWindow == result.wid
|
|
283
|
+
let title = result.title.isEmpty ? "Untitled" : result.title
|
|
284
|
+
let preview = compactPreview(result.fullText)
|
|
285
|
+
|
|
286
|
+
return VStack(alignment: .leading, spacing: 5) {
|
|
287
|
+
Button {
|
|
288
|
+
withAnimation(.easeOut(duration: 0.12)) {
|
|
289
|
+
expandedOcrWindow = isExpanded ? nil : result.wid
|
|
290
|
+
}
|
|
291
|
+
} label: {
|
|
292
|
+
VStack(alignment: .leading, spacing: 4) {
|
|
293
|
+
HStack(spacing: 7) {
|
|
294
|
+
Image(systemName: isExpanded ? "chevron.down" : "chevron.right")
|
|
295
|
+
.font(.system(size: 8, weight: .semibold))
|
|
296
|
+
.foregroundColor(Palette.textMuted)
|
|
297
|
+
.frame(width: 9)
|
|
298
|
+
|
|
299
|
+
Text(result.app)
|
|
300
|
+
.font(Typo.monoBold(11))
|
|
301
|
+
.foregroundColor(Palette.textDim)
|
|
302
|
+
.lineLimit(1)
|
|
303
|
+
|
|
304
|
+
Text(sourceLabel(result.source))
|
|
305
|
+
.font(Typo.mono(8))
|
|
306
|
+
.foregroundColor(Palette.textMuted)
|
|
307
|
+
.padding(.horizontal, 4)
|
|
308
|
+
.padding(.vertical, 1)
|
|
309
|
+
.background(
|
|
310
|
+
RoundedRectangle(cornerRadius: 3)
|
|
311
|
+
.fill(Palette.surface.opacity(0.8))
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
Spacer()
|
|
315
|
+
|
|
316
|
+
Text(relativeTime(result.timestamp))
|
|
317
|
+
.font(Typo.mono(9))
|
|
318
|
+
.foregroundColor(Palette.textMuted)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
Text(title)
|
|
322
|
+
.font(Typo.mono(10))
|
|
323
|
+
.foregroundColor(Palette.textMuted)
|
|
324
|
+
.lineLimit(1)
|
|
325
|
+
|
|
326
|
+
if !isExpanded && !preview.isEmpty {
|
|
327
|
+
Text(preview)
|
|
328
|
+
.font(Typo.mono(9))
|
|
329
|
+
.foregroundColor(Palette.textMuted.opacity(0.75))
|
|
330
|
+
.lineLimit(2)
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
.padding(8)
|
|
334
|
+
.background(
|
|
335
|
+
RoundedRectangle(cornerRadius: 5)
|
|
336
|
+
.fill(Palette.surface.opacity(isExpanded ? 0.72 : 0.38))
|
|
337
|
+
.overlay(
|
|
338
|
+
RoundedRectangle(cornerRadius: 5)
|
|
339
|
+
.strokeBorder(Color.white.opacity(isExpanded ? 0.10 : 0.05), lineWidth: 0.5)
|
|
340
|
+
)
|
|
341
|
+
)
|
|
342
|
+
.contentShape(Rectangle())
|
|
343
|
+
}
|
|
344
|
+
.buttonStyle(.plain)
|
|
345
|
+
|
|
346
|
+
if isExpanded {
|
|
347
|
+
ScrollView {
|
|
348
|
+
Text(result.fullText.isEmpty ? "No text captured." : result.fullText)
|
|
349
|
+
.font(Typo.mono(10))
|
|
350
|
+
.foregroundColor(Palette.textDim)
|
|
351
|
+
.textSelection(.enabled)
|
|
352
|
+
.frame(maxWidth: .infinity, alignment: .leading)
|
|
353
|
+
.padding(8)
|
|
354
|
+
}
|
|
355
|
+
.frame(maxHeight: 140)
|
|
356
|
+
.background(
|
|
357
|
+
RoundedRectangle(cornerRadius: 5)
|
|
358
|
+
.fill(Color.black.opacity(0.22))
|
|
359
|
+
.overlay(
|
|
360
|
+
RoundedRectangle(cornerRadius: 5)
|
|
361
|
+
.strokeBorder(Color.white.opacity(0.06), lineWidth: 0.5)
|
|
362
|
+
)
|
|
363
|
+
)
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
248
368
|
private func summarySection<Content: View>(
|
|
249
369
|
_ title: String,
|
|
250
370
|
icon: String,
|
|
@@ -285,4 +405,18 @@ struct OmniSearchView: View {
|
|
|
285
405
|
if seconds < 3600 { return "\(seconds / 60)m ago" }
|
|
286
406
|
return "\(seconds / 3600)h ago"
|
|
287
407
|
}
|
|
408
|
+
|
|
409
|
+
private func sourceLabel(_ source: TextSource) -> String {
|
|
410
|
+
switch source {
|
|
411
|
+
case .accessibility: return "AX"
|
|
412
|
+
case .ocr: return "OCR"
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
private func compactPreview(_ text: String) -> String {
|
|
417
|
+
text
|
|
418
|
+
.components(separatedBy: .whitespacesAndNewlines)
|
|
419
|
+
.filter { !$0.isEmpty }
|
|
420
|
+
.joined(separator: " ")
|
|
421
|
+
}
|
|
288
422
|
}
|
package/app/Sources/{OmniSearchWindow.swift → Core/Overlays/OmniSearch/OmniSearchWindow.swift}
RENAMED
|
@@ -34,39 +34,28 @@ final class OmniSearchWindow {
|
|
|
34
34
|
}
|
|
35
35
|
.preferredColorScheme(.dark)
|
|
36
36
|
|
|
37
|
-
let
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
45
54
|
)
|
|
46
|
-
p.
|
|
47
|
-
p.
|
|
48
|
-
p.
|
|
49
|
-
p
|
|
50
|
-
p.isMovableByWindowBackground = true
|
|
51
|
-
p.level = .floating
|
|
52
|
-
p.isOpaque = false
|
|
53
|
-
p.backgroundColor = NSColor(red: 0.11, green: 0.11, blue: 0.12, alpha: 1.0)
|
|
54
|
-
p.hasShadow = true
|
|
55
|
-
p.appearance = NSAppearance(named: .darkAqua)
|
|
56
|
-
p.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
|
|
57
|
-
p.minSize = NSSize(width: 400, height: 300)
|
|
58
|
-
p.maxSize = NSSize(width: 700, height: 700)
|
|
59
|
-
|
|
60
|
-
// Center on screen
|
|
61
|
-
if let screen = NSScreen.main {
|
|
62
|
-
let visibleFrame = screen.visibleFrame
|
|
63
|
-
let x = visibleFrame.midX - 260
|
|
64
|
-
let y = visibleFrame.midY + 60 // slightly above center
|
|
65
|
-
p.setFrameOrigin(NSPoint(x: x, y: y))
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
p.makeKeyAndOrderFront(nil)
|
|
69
|
-
NSApp.activate(ignoringOtherApps: true)
|
|
55
|
+
p.hidesOnDeactivate = false
|
|
56
|
+
p.becomesKeyOnlyIfNeeded = false
|
|
57
|
+
OverlayPanelShell.position(p, placement: .centered(yOffsetRatio: 0.125))
|
|
58
|
+
OverlayPanelShell.present(p)
|
|
70
59
|
panel = p
|
|
71
60
|
|
|
72
61
|
// Key monitor: Escape → dismiss, arrow keys → navigate, Enter → activate
|