@arach/lattices 0.2.1 → 0.6.1
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 +144 -69
- package/apps/mac/Info.plist +43 -0
- package/apps/mac/Lattices.app/Contents/Info.plist +43 -0
- package/apps/mac/Lattices.app/Contents/MacOS/Lattices +0 -0
- package/apps/mac/Lattices.app/Contents/Resources/AppIcon.icns +0 -0
- package/apps/mac/Lattices.app/Contents/Resources/docs/assistant-knowledge.md +130 -0
- package/apps/mac/Lattices.app/Contents/Resources/tap.wav +0 -0
- package/apps/mac/Lattices.app/Contents/_CodeSignature/CodeResources +150 -0
- package/apps/mac/Lattices.entitlements +21 -0
- 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/Resources/tap.wav +0 -0
- package/assets/AppIcon.icns +0 -0
- package/bin/assistant-intelligence.ts +912 -0
- package/bin/cli/capture.ts +252 -0
- package/bin/cli/daemon.ts +22 -0
- package/bin/cli/helpers.ts +105 -0
- package/bin/cli/layer.ts +178 -0
- package/bin/cli/runs.ts +43 -0
- package/bin/cli/search.ts +141 -0
- package/bin/cli/session.ts +32 -0
- package/bin/client.ts +17 -0
- package/bin/cua.ts +26 -0
- package/bin/{daemon-client.js → daemon-client.ts} +49 -30
- package/bin/handsoff-infer.ts +96 -0
- package/bin/handsoff-worker.ts +531 -0
- package/bin/infer.ts +424 -0
- package/bin/keychain.ts +75 -0
- package/bin/lattices-app.ts +655 -0
- package/bin/lattices-build +125 -0
- package/bin/lattices-build-env.ts +77 -0
- package/bin/lattices-dev +362 -0
- package/bin/lattices.ts +3260 -0
- package/bin/project-twin.ts +645 -0
- package/docs/agent-execution-plan.md +562 -0
- package/docs/agent-layer-guide.md +207 -0
- package/docs/agents.md +233 -0
- package/docs/ai-chat-ux-review.md +416 -0
- package/docs/api.md +1041 -47
- package/docs/app.md +96 -13
- package/docs/assistant-knowledge.md +130 -0
- package/docs/companion-deck.md +209 -0
- package/docs/component-extraction-roadmap.md +392 -0
- package/docs/concepts.md +13 -12
- package/docs/config.md +83 -10
- package/docs/gesture-customization-proposal.md +520 -0
- package/docs/handsoff-test-scenarios.md +84 -0
- package/docs/hyperspace-grid-snappiness.md +210 -0
- package/docs/layers.md +176 -28
- package/docs/mouse-gestures.md +244 -0
- package/docs/ocr.md +21 -9
- package/docs/overview.md +42 -23
- package/docs/presentation-execution-review.md +491 -0
- package/docs/prompts/hands-off-system.md +382 -0
- package/docs/prompts/hands-off-turn.md +30 -0
- package/docs/prompts/voice-advisor.md +31 -0
- package/docs/prompts/voice-fallback.md +23 -0
- 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/proposals/LAT-005-action-runtime-product-spine.md +914 -0
- package/docs/proposals/LAT-006-followup-gaps.md +103 -0
- package/docs/proposals/LAT-006-runs-and-capture-in-lattices.md +566 -0
- package/docs/proposals/LAT-007-unified-app-shell.md +128 -0
- package/docs/quickstart.md +8 -12
- package/docs/reference/dewey.config.ts +74 -0
- package/docs/reference/install-agent.md +79 -0
- package/docs/release.md +172 -0
- package/docs/repo-structure.md +100 -0
- package/docs/terminal-kit.md +87 -0
- package/docs/tiling-reference.md +224 -0
- package/docs/twins.md +138 -0
- package/docs/voice-command-protocol.md +278 -0
- package/docs/voice-error-model.md +73 -0
- package/docs/voice.md +221 -0
- package/package.json +69 -16
- package/packages/npm/sdk/cua.d.mts +1 -0
- package/packages/npm/sdk/cua.d.ts +188 -0
- package/packages/npm/sdk/cua.mjs +376 -0
- package/app/Lattices.app/Contents/Info.plist +0 -24
- package/app/Package.swift +0 -13
- package/app/Sources/ActionRow.swift +0 -61
- package/app/Sources/App.swift +0 -10
- package/app/Sources/AppDelegate.swift +0 -234
- package/app/Sources/AppShellView.swift +0 -62
- package/app/Sources/AppTypeClassifier.swift +0 -70
- package/app/Sources/AppWindowShell.swift +0 -63
- package/app/Sources/CheatSheetHUD.swift +0 -332
- package/app/Sources/CommandModeState.swift +0 -1362
- package/app/Sources/CommandModeView.swift +0 -1405
- package/app/Sources/CommandModeWindow.swift +0 -192
- package/app/Sources/CommandPaletteView.swift +0 -307
- package/app/Sources/CommandPaletteWindow.swift +0 -134
- package/app/Sources/DaemonProtocol.swift +0 -101
- package/app/Sources/DaemonServer.swift +0 -414
- package/app/Sources/DesktopModel.swift +0 -121
- package/app/Sources/DesktopModelTypes.swift +0 -71
- package/app/Sources/DiagnosticLog.swift +0 -271
- package/app/Sources/EventBus.swift +0 -30
- package/app/Sources/HotkeyManager.swift +0 -250
- package/app/Sources/HotkeyStore.swift +0 -338
- package/app/Sources/InventoryManager.swift +0 -35
- package/app/Sources/InventoryPath.swift +0 -43
- package/app/Sources/KeyRecorderView.swift +0 -210
- package/app/Sources/LatticesApi.swift +0 -1125
- package/app/Sources/MainView.swift +0 -467
- package/app/Sources/MainWindow.swift +0 -83
- package/app/Sources/OcrModel.swift +0 -309
- package/app/Sources/OcrStore.swift +0 -295
- package/app/Sources/OmniSearchState.swift +0 -283
- package/app/Sources/OmniSearchView.swift +0 -288
- package/app/Sources/OmniSearchWindow.swift +0 -105
- package/app/Sources/OrphanRow.swift +0 -129
- package/app/Sources/PaletteCommand.swift +0 -419
- package/app/Sources/PermissionChecker.swift +0 -125
- package/app/Sources/Preferences.swift +0 -92
- package/app/Sources/ProcessModel.swift +0 -199
- package/app/Sources/ProcessQuery.swift +0 -151
- package/app/Sources/Project.swift +0 -28
- package/app/Sources/ProjectRow.swift +0 -368
- package/app/Sources/ProjectScanner.swift +0 -121
- package/app/Sources/ScreenMapState.swift +0 -2387
- package/app/Sources/ScreenMapView.swift +0 -2820
- package/app/Sources/ScreenMapWindowController.swift +0 -89
- package/app/Sources/SessionManager.swift +0 -72
- package/app/Sources/SettingsView.swift +0 -1053
- package/app/Sources/SettingsWindow.swift +0 -20
- package/app/Sources/TabGroupRow.swift +0 -178
- package/app/Sources/Terminal.swift +0 -259
- package/app/Sources/TerminalQuery.swift +0 -156
- package/app/Sources/TerminalSynthesizer.swift +0 -200
- package/app/Sources/Theme.swift +0 -163
- package/app/Sources/TilePickerView.swift +0 -209
- package/app/Sources/TmuxModel.swift +0 -53
- package/app/Sources/TmuxQuery.swift +0 -81
- package/app/Sources/WindowTiler.swift +0 -1755
- package/app/Sources/WorkspaceManager.swift +0 -434
- package/bin/lattices-app.js +0 -221
- package/bin/lattices.js +0 -1418
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
import AppKit
|
|
2
|
-
import SwiftUI
|
|
3
|
-
|
|
4
|
-
/// Manages the NSStatusItem (menu bar icon), left-click popover, and right-click context menu.
|
|
5
|
-
/// Replaces the previous SwiftUI MenuBarExtra approach for full click-event control.
|
|
6
|
-
class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
|
7
|
-
|
|
8
|
-
private var statusItem: NSStatusItem!
|
|
9
|
-
private var popover: NSPopover?
|
|
10
|
-
private var contextMenu: NSMenu!
|
|
11
|
-
|
|
12
|
-
/// 3×3 grid icon for the menu bar — L-shape bright, rest dim (template for auto light/dark)
|
|
13
|
-
private static let menuBarIcon: NSImage = {
|
|
14
|
-
let size: CGFloat = 18
|
|
15
|
-
let img = NSImage(size: NSSize(width: size, height: size), flipped: true) { _ in
|
|
16
|
-
let pad: CGFloat = 2
|
|
17
|
-
let gap: CGFloat = 1.5
|
|
18
|
-
let cellSize = (size - 2 * pad - 2 * gap) / 3
|
|
19
|
-
|
|
20
|
-
let solidCells: Set<Int> = [0, 3, 6, 7, 8]
|
|
21
|
-
|
|
22
|
-
for row in 0..<3 {
|
|
23
|
-
for col in 0..<3 {
|
|
24
|
-
let idx = row * 3 + col
|
|
25
|
-
let x = pad + CGFloat(col) * (cellSize + gap)
|
|
26
|
-
let y = pad + CGFloat(row) * (cellSize + gap)
|
|
27
|
-
let rect = NSRect(x: x, y: y, width: cellSize, height: cellSize)
|
|
28
|
-
|
|
29
|
-
if solidCells.contains(idx) {
|
|
30
|
-
NSColor.black.setFill()
|
|
31
|
-
} else {
|
|
32
|
-
NSColor.black.withAlphaComponent(0.25).setFill()
|
|
33
|
-
}
|
|
34
|
-
let path = NSBezierPath(roundedRect: rect, xRadius: 0.8, yRadius: 0.8)
|
|
35
|
-
path.fill()
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return true
|
|
39
|
-
}
|
|
40
|
-
img.isTemplate = true
|
|
41
|
-
return img
|
|
42
|
-
}()
|
|
43
|
-
|
|
44
|
-
/// Toggle between .accessory (hidden from Dock/Cmd+Tab) and .regular (visible)
|
|
45
|
-
/// based on whether any managed windows are open.
|
|
46
|
-
static func updateActivationPolicy() {
|
|
47
|
-
let hasVisibleWindow =
|
|
48
|
-
CommandModeWindow.shared.isVisible ||
|
|
49
|
-
CommandPaletteWindow.shared.isVisible ||
|
|
50
|
-
MainWindow.shared.isVisible ||
|
|
51
|
-
ScreenMapWindowController.shared.isVisible ||
|
|
52
|
-
OmniSearchWindow.shared.isVisible
|
|
53
|
-
let desired: NSApplication.ActivationPolicy = hasVisibleWindow ? .regular : .accessory
|
|
54
|
-
if NSApp.activationPolicy() != desired {
|
|
55
|
-
NSApp.setActivationPolicy(desired)
|
|
56
|
-
if desired == .regular {
|
|
57
|
-
NSApp.activate(ignoringOtherApps: true)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
func applicationDidFinishLaunching(_ notification: Notification) {
|
|
63
|
-
NSApp.setActivationPolicy(.accessory)
|
|
64
|
-
NSApp.appearance = NSAppearance(named: .darkAqua)
|
|
65
|
-
|
|
66
|
-
// --- Status item ---
|
|
67
|
-
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
|
|
68
|
-
if let button = statusItem.button {
|
|
69
|
-
button.image = Self.menuBarIcon
|
|
70
|
-
button.action = #selector(statusItemClicked(_:))
|
|
71
|
-
button.sendAction(on: [.leftMouseUp, .rightMouseUp])
|
|
72
|
-
button.target = self
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// --- Context menu (right-click) ---
|
|
76
|
-
contextMenu = buildContextMenu()
|
|
77
|
-
|
|
78
|
-
// --- Hotkey registration ---
|
|
79
|
-
let scanner = ProjectScanner.shared
|
|
80
|
-
CommandPaletteWindow.shared.configure(scanner: scanner)
|
|
81
|
-
|
|
82
|
-
let store = HotkeyStore.shared
|
|
83
|
-
store.register(action: .palette) { CommandPaletteWindow.shared.toggle() }
|
|
84
|
-
store.register(action: .screenMap) { ScreenMapWindowController.shared.toggle() }
|
|
85
|
-
store.register(action: .bezel) { WindowBezel.showBezelForFrontmostWindow() }
|
|
86
|
-
store.register(action: .cheatSheet) { CheatSheetHUD.shared.toggle() }
|
|
87
|
-
store.register(action: .desktopInventory) { CommandModeWindow.shared.toggle() }
|
|
88
|
-
store.register(action: .omniSearch) { OmniSearchWindow.shared.toggle() }
|
|
89
|
-
|
|
90
|
-
// Layer-switching hotkeys
|
|
91
|
-
let workspace = WorkspaceManager.shared
|
|
92
|
-
let layerCount = (workspace.config?.layers ?? []).count
|
|
93
|
-
for (i, action) in HotkeyAction.layerActions.prefix(layerCount).enumerated() {
|
|
94
|
-
let index = i
|
|
95
|
-
store.register(action: action) { workspace.tileLayer(index: index) }
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Tiling hotkeys
|
|
99
|
-
let tileMap: [(HotkeyAction, TilePosition)] = [
|
|
100
|
-
(.tileLeft, .left), (.tileRight, .right),
|
|
101
|
-
(.tileMaximize, .maximize), (.tileCenter, .center),
|
|
102
|
-
(.tileTopLeft, .topLeft), (.tileTopRight, .topRight),
|
|
103
|
-
(.tileBottomLeft, .bottomLeft), (.tileBottomRight, .bottomRight),
|
|
104
|
-
(.tileTop, .top), (.tileBottom, .bottom),
|
|
105
|
-
(.tileLeftThird, .leftThird), (.tileCenterThird, .centerThird),
|
|
106
|
-
(.tileRightThird, .rightThird),
|
|
107
|
-
]
|
|
108
|
-
for (action, position) in tileMap {
|
|
109
|
-
store.register(action: action) { WindowTiler.tileFrontmostViaAX(to: position) }
|
|
110
|
-
}
|
|
111
|
-
store.register(action: .tileDistribute) { WindowTiler.distributeVisible() }
|
|
112
|
-
|
|
113
|
-
// Check macOS permissions (Accessibility, Screen Recording)
|
|
114
|
-
PermissionChecker.shared.check()
|
|
115
|
-
|
|
116
|
-
// Start daemon services
|
|
117
|
-
OcrStore.shared.open()
|
|
118
|
-
DesktopModel.shared.start()
|
|
119
|
-
OcrModel.shared.start()
|
|
120
|
-
TmuxModel.shared.start()
|
|
121
|
-
ProcessModel.shared.start()
|
|
122
|
-
LatticesApi.setup()
|
|
123
|
-
DaemonServer.shared.start()
|
|
124
|
-
|
|
125
|
-
// --diagnostics flag: auto-open diagnostics panel on launch
|
|
126
|
-
if CommandLine.arguments.contains("--diagnostics") {
|
|
127
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
|
128
|
-
DiagnosticWindow.shared.show()
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// --screen-map flag: auto-open screen map on launch
|
|
133
|
-
if CommandLine.arguments.contains("--screen-map") {
|
|
134
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
|
135
|
-
ScreenMapWindowController.shared.show()
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// MARK: - Status item click handler
|
|
141
|
-
|
|
142
|
-
@objc private func statusItemClicked(_ sender: Any?) {
|
|
143
|
-
guard let event = NSApp.currentEvent, let button = statusItem.button else { return }
|
|
144
|
-
|
|
145
|
-
if event.type == .rightMouseUp {
|
|
146
|
-
// Right-click → context menu
|
|
147
|
-
contextMenu.popUp(positioning: nil, at: NSPoint(x: 0, y: button.bounds.height + 4), in: button)
|
|
148
|
-
} else {
|
|
149
|
-
// Left-click → toggle popover
|
|
150
|
-
if let shown = popover, shown.isShown {
|
|
151
|
-
shown.performClose(sender)
|
|
152
|
-
} else {
|
|
153
|
-
let p = makePopover()
|
|
154
|
-
p.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
|
|
155
|
-
p.contentViewController?.view.window?.makeKey()
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/// Dismiss the popover programmatically (e.g. from the pop-out button).
|
|
161
|
-
func dismissPopover() {
|
|
162
|
-
popover?.performClose(nil)
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/// Create a fresh popover each time so the SwiftUI view tree isn't kept alive
|
|
166
|
-
/// when the popover is closed — prevents continuous CPU usage from @Published updates.
|
|
167
|
-
private func makePopover() -> NSPopover {
|
|
168
|
-
let p = NSPopover()
|
|
169
|
-
p.contentViewController = NSHostingController(rootView: MainView(scanner: ProjectScanner.shared))
|
|
170
|
-
p.behavior = .transient
|
|
171
|
-
p.contentSize = NSSize(width: 380, height: 520)
|
|
172
|
-
p.appearance = NSAppearance(named: .darkAqua)
|
|
173
|
-
p.delegate = self
|
|
174
|
-
popover = p
|
|
175
|
-
return p
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
func popoverDidClose(_ notification: Notification) {
|
|
179
|
-
// Tear down the SwiftUI view tree so observed models stop driving re-renders
|
|
180
|
-
popover?.contentViewController = nil
|
|
181
|
-
popover = nil
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// MARK: - Context menu
|
|
185
|
-
|
|
186
|
-
private func buildContextMenu() -> NSMenu {
|
|
187
|
-
let menu = NSMenu()
|
|
188
|
-
|
|
189
|
-
let actions: [(String, String, Selector)] = [
|
|
190
|
-
("Command Palette", "⌘⇧M", #selector(menuCommandPalette)),
|
|
191
|
-
("Screen Map", "", #selector(menuScreenMap)),
|
|
192
|
-
("Desktop Inventory", "", #selector(menuDesktopInventory)),
|
|
193
|
-
("Window Bezel", "", #selector(menuWindowBezel)),
|
|
194
|
-
("Cheat Sheet", "", #selector(menuCheatSheet)),
|
|
195
|
-
("Omni Search", "", #selector(menuOmniSearch)),
|
|
196
|
-
]
|
|
197
|
-
for (title, shortcut, action) in actions {
|
|
198
|
-
let item = NSMenuItem(title: title, action: action, keyEquivalent: "")
|
|
199
|
-
item.target = self
|
|
200
|
-
if !shortcut.isEmpty {
|
|
201
|
-
// Display-only; the actual hotkey is global
|
|
202
|
-
}
|
|
203
|
-
menu.addItem(item)
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
menu.addItem(.separator())
|
|
207
|
-
|
|
208
|
-
let settings = NSMenuItem(title: "Settings…", action: #selector(menuSettings), keyEquivalent: ",")
|
|
209
|
-
settings.target = self
|
|
210
|
-
menu.addItem(settings)
|
|
211
|
-
|
|
212
|
-
let diag = NSMenuItem(title: "Diagnostics", action: #selector(menuDiagnostics), keyEquivalent: "")
|
|
213
|
-
diag.target = self
|
|
214
|
-
menu.addItem(diag)
|
|
215
|
-
|
|
216
|
-
menu.addItem(.separator())
|
|
217
|
-
|
|
218
|
-
let quit = NSMenuItem(title: "Quit Lattices", action: #selector(menuQuit), keyEquivalent: "q")
|
|
219
|
-
quit.target = self
|
|
220
|
-
menu.addItem(quit)
|
|
221
|
-
|
|
222
|
-
return menu
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
@objc private func menuCommandPalette() { CommandPaletteWindow.shared.toggle() }
|
|
226
|
-
@objc private func menuScreenMap() { ScreenMapWindowController.shared.toggle() }
|
|
227
|
-
@objc private func menuDesktopInventory() { CommandModeWindow.shared.toggle() }
|
|
228
|
-
@objc private func menuWindowBezel() { WindowBezel.showBezelForFrontmostWindow() }
|
|
229
|
-
@objc private func menuCheatSheet() { CheatSheetHUD.shared.toggle() }
|
|
230
|
-
@objc private func menuOmniSearch() { OmniSearchWindow.shared.toggle() }
|
|
231
|
-
@objc private func menuSettings() { SettingsWindowController.shared.show() }
|
|
232
|
-
@objc private func menuDiagnostics() { DiagnosticWindow.shared.toggle() }
|
|
233
|
-
@objc private func menuQuit() { NSApp.terminate(nil) }
|
|
234
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import SwiftUI
|
|
2
|
-
|
|
3
|
-
// MARK: - Navigation Pages
|
|
4
|
-
|
|
5
|
-
enum AppPage: String, CaseIterable {
|
|
6
|
-
case screenMap
|
|
7
|
-
case settings
|
|
8
|
-
case docs
|
|
9
|
-
|
|
10
|
-
var label: String {
|
|
11
|
-
switch self {
|
|
12
|
-
case .screenMap: return "Screen Map"
|
|
13
|
-
case .settings: return "Settings"
|
|
14
|
-
case .docs: return "Docs"
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
var icon: String {
|
|
19
|
-
switch self {
|
|
20
|
-
case .screenMap: return "rectangle.3.group"
|
|
21
|
-
case .settings: return "gearshape"
|
|
22
|
-
case .docs: return "book"
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// MARK: - App Shell View
|
|
28
|
-
|
|
29
|
-
struct AppShellView: View {
|
|
30
|
-
@ObservedObject var controller: ScreenMapController
|
|
31
|
-
@ObservedObject var windowController = ScreenMapWindowController.shared
|
|
32
|
-
|
|
33
|
-
var body: some View {
|
|
34
|
-
contentArea
|
|
35
|
-
.background(Palette.bg)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// MARK: - Content Area
|
|
39
|
-
|
|
40
|
-
@ViewBuilder
|
|
41
|
-
private var contentArea: some View {
|
|
42
|
-
switch windowController.activePage {
|
|
43
|
-
case .screenMap:
|
|
44
|
-
ScreenMapView(controller: controller, onNavigate: { page in
|
|
45
|
-
windowController.activePage = page
|
|
46
|
-
})
|
|
47
|
-
case .settings:
|
|
48
|
-
SettingsContentView(
|
|
49
|
-
prefs: Preferences.shared,
|
|
50
|
-
scanner: ProjectScanner.shared,
|
|
51
|
-
onBack: { windowController.activePage = .screenMap; controller.enter() }
|
|
52
|
-
)
|
|
53
|
-
case .docs:
|
|
54
|
-
SettingsContentView(
|
|
55
|
-
page: .docs,
|
|
56
|
-
prefs: Preferences.shared,
|
|
57
|
-
scanner: ProjectScanner.shared,
|
|
58
|
-
onBack: { windowController.activePage = .screenMap; controller.enter() }
|
|
59
|
-
)
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
|
|
3
|
-
enum AppType: String, CaseIterable {
|
|
4
|
-
case terminal
|
|
5
|
-
case editor
|
|
6
|
-
case browser
|
|
7
|
-
case chat
|
|
8
|
-
case media
|
|
9
|
-
case design
|
|
10
|
-
case system
|
|
11
|
-
case other
|
|
12
|
-
|
|
13
|
-
var label: String { rawValue }
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
enum AppTypeClassifier {
|
|
17
|
-
private static let nameMap: [String: AppType] = [
|
|
18
|
-
// Terminals
|
|
19
|
-
"iTerm2": .terminal, "Terminal": .terminal, "Alacritty": .terminal,
|
|
20
|
-
"kitty": .terminal, "Warp": .terminal, "Hyper": .terminal,
|
|
21
|
-
"WezTerm": .terminal, "Rio": .terminal, "Ghostty": .terminal,
|
|
22
|
-
|
|
23
|
-
// Editors / IDEs
|
|
24
|
-
"Xcode": .editor, "Code": .editor, "Visual Studio Code": .editor,
|
|
25
|
-
"Cursor": .editor, "Sublime Text": .editor, "TextEdit": .editor,
|
|
26
|
-
"Nova": .editor, "BBEdit": .editor, "Zed": .editor,
|
|
27
|
-
"IntelliJ IDEA": .editor, "WebStorm": .editor, "PyCharm": .editor,
|
|
28
|
-
"CLion": .editor, "GoLand": .editor, "RustRover": .editor,
|
|
29
|
-
"Android Studio": .editor, "Fleet": .editor, "Neovide": .editor,
|
|
30
|
-
|
|
31
|
-
// Browsers
|
|
32
|
-
"Safari": .browser, "Google Chrome": .browser, "Firefox": .browser,
|
|
33
|
-
"Arc": .browser, "Brave Browser": .browser, "Microsoft Edge": .browser,
|
|
34
|
-
"Orion": .browser, "Vivaldi": .browser, "Opera": .browser,
|
|
35
|
-
"Chrome": .browser, "Zen Browser": .browser,
|
|
36
|
-
|
|
37
|
-
// Chat / Communication
|
|
38
|
-
"Slack": .chat, "Discord": .chat, "Messages": .chat,
|
|
39
|
-
"Telegram": .chat, "WhatsApp": .chat, "Signal": .chat,
|
|
40
|
-
"Teams": .chat, "Microsoft Teams": .chat, "Zoom": .chat,
|
|
41
|
-
"FaceTime": .chat, "Skype": .chat,
|
|
42
|
-
|
|
43
|
-
// Media
|
|
44
|
-
"Spotify": .media, "Music": .media, "QuickTime Player": .media,
|
|
45
|
-
"VLC": .media, "IINA": .media, "Podcasts": .media,
|
|
46
|
-
"Photos": .media, "Preview": .media, "mpv": .media,
|
|
47
|
-
|
|
48
|
-
// Design
|
|
49
|
-
"Figma": .design, "Sketch": .design, "Pixelmator Pro": .design,
|
|
50
|
-
"Affinity Designer 2": .design, "Affinity Photo 2": .design,
|
|
51
|
-
"Adobe Photoshop": .design, "Adobe Illustrator": .design,
|
|
52
|
-
"Blender": .design, "OmniGraffle": .design,
|
|
53
|
-
|
|
54
|
-
// System
|
|
55
|
-
"Finder": .system, "System Preferences": .system, "System Settings": .system,
|
|
56
|
-
"Activity Monitor": .system, "Console": .system, "Disk Utility": .system,
|
|
57
|
-
"Keychain Access": .system,
|
|
58
|
-
]
|
|
59
|
-
|
|
60
|
-
static func classify(_ appName: String) -> AppType {
|
|
61
|
-
if let exact = nameMap[appName] { return exact }
|
|
62
|
-
// Substring fallback
|
|
63
|
-
let lower = appName.lowercased()
|
|
64
|
-
if lower.contains("terminal") || lower.contains("term") { return .terminal }
|
|
65
|
-
if lower.contains("code") || lower.contains("studio") || lower.contains("edit") { return .editor }
|
|
66
|
-
if lower.contains("chrome") || lower.contains("firefox") || lower.contains("safari") || lower.contains("browser") { return .browser }
|
|
67
|
-
if lower.contains("slack") || lower.contains("discord") || lower.contains("chat") || lower.contains("teams") { return .chat }
|
|
68
|
-
return .other
|
|
69
|
-
}
|
|
70
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import AppKit
|
|
2
|
-
import SwiftUI
|
|
3
|
-
|
|
4
|
-
/// Shared factory for standalone NSWindow chrome.
|
|
5
|
-
/// Every managed window (Screen Map, Settings, Diagnostics, etc.) uses this
|
|
6
|
-
/// to get consistent title bar styling, dark appearance, and positioning.
|
|
7
|
-
struct AppWindowShell {
|
|
8
|
-
|
|
9
|
-
struct Config {
|
|
10
|
-
var title: String
|
|
11
|
-
var titleVisible: Bool = true
|
|
12
|
-
var initialSize: NSSize
|
|
13
|
-
var minSize: NSSize
|
|
14
|
-
var maxSize: NSSize
|
|
15
|
-
var miniaturizable: Bool = true
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/// Create a styled NSWindow hosting a SwiftUI root view.
|
|
19
|
-
static func makeWindow<V: View>(config: Config, rootView: V) -> NSWindow {
|
|
20
|
-
let hosting = NSHostingView(rootView: rootView.preferredColorScheme(.dark))
|
|
21
|
-
hosting.frame = NSRect(origin: .zero, size: config.initialSize)
|
|
22
|
-
|
|
23
|
-
var styleMask: NSWindow.StyleMask = [.titled, .closable, .resizable]
|
|
24
|
-
if config.miniaturizable { styleMask.insert(.miniaturizable) }
|
|
25
|
-
|
|
26
|
-
let w = NSWindow(
|
|
27
|
-
contentRect: NSRect(origin: .zero, size: config.initialSize),
|
|
28
|
-
styleMask: styleMask,
|
|
29
|
-
backing: .buffered,
|
|
30
|
-
defer: false
|
|
31
|
-
)
|
|
32
|
-
w.contentView = hosting
|
|
33
|
-
w.title = config.title
|
|
34
|
-
w.titlebarAppearsTransparent = true
|
|
35
|
-
w.titleVisibility = config.titleVisible ? .visible : .hidden
|
|
36
|
-
w.isReleasedWhenClosed = false
|
|
37
|
-
w.backgroundColor = NSColor(red: 0.11, green: 0.11, blue: 0.12, alpha: 1.0)
|
|
38
|
-
w.appearance = NSAppearance(named: .darkAqua)
|
|
39
|
-
w.minSize = config.minSize
|
|
40
|
-
w.maxSize = config.maxSize
|
|
41
|
-
return w
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/// Center the window on screen, nudged 8% above vertical center.
|
|
45
|
-
/// Clamps to 92% screen width / 85% screen height.
|
|
46
|
-
static func positionCentered(_ window: NSWindow) {
|
|
47
|
-
guard let screen = NSScreen.main else { return }
|
|
48
|
-
let frame = screen.visibleFrame
|
|
49
|
-
let size = window.frame.size
|
|
50
|
-
let w = min(size.width, frame.width * 0.92)
|
|
51
|
-
let h = min(size.height, frame.height * 0.85)
|
|
52
|
-
let x = frame.midX - w / 2
|
|
53
|
-
let y = frame.midY - h / 2 + (frame.height * 0.08)
|
|
54
|
-
window.setFrame(NSRect(x: x, y: y, width: w, height: h), display: true)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/// Bring the window to front and update activation policy.
|
|
58
|
-
static func present(_ window: NSWindow) {
|
|
59
|
-
window.makeKeyAndOrderFront(nil)
|
|
60
|
-
NSApp.activate(ignoringOtherApps: true)
|
|
61
|
-
AppDelegate.updateActivationPolicy()
|
|
62
|
-
}
|
|
63
|
-
}
|