@lattices/cli 0.4.14 → 0.5.0
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 +5 -7
- package/apps/mac/Info.plist +2 -2
- package/apps/mac/Lattices.app/Contents/Info.plist +4 -12
- package/apps/mac/Lattices.app/Contents/MacOS/Lattices +0 -0
- package/bin/lattices-app.ts +110 -17
- package/bin/lattices-build +125 -0
- package/bin/lattices-dev +89 -16
- package/bin/lattices.ts +977 -16
- package/docs/agents.md +81 -4
- package/docs/ai-chat-ux-review.md +416 -0
- package/docs/api.md +135 -3
- package/docs/app.md +30 -8
- package/docs/config.md +4 -0
- package/docs/mouse-gestures.md +60 -1
- package/docs/proposals/LAT-004-interactive-overlay-actors.md +1 -1
- package/docs/proposals/LAT-005-action-runtime-product-spine.md +914 -0
- package/docs/proposals/LAT-006-mira-in-lattices.md +553 -0
- package/docs/reference/dewey.config.ts +2 -2
- package/docs/release.md +171 -0
- package/docs/repo-structure.md +4 -5
- package/docs/voice.md +11 -27
- package/package.json +9 -10
- package/apps/mac/Package.swift +0 -27
- package/apps/mac/Sources/AppShell/App.swift +0 -26
- package/apps/mac/Sources/AppShell/AppActivationCoordinator.swift +0 -27
- package/apps/mac/Sources/AppShell/AppDelegate.swift +0 -189
- package/apps/mac/Sources/AppShell/AppServicesBootstrap.swift +0 -25
- package/apps/mac/Sources/AppShell/AppShellView.swift +0 -171
- package/apps/mac/Sources/AppShell/AppUpdater.swift +0 -305
- package/apps/mac/Sources/AppShell/CliActionLauncher.swift +0 -50
- package/apps/mac/Sources/AppShell/HomeDashboardView.swift +0 -133
- package/apps/mac/Sources/AppShell/HotkeyBootstrap.swift +0 -87
- package/apps/mac/Sources/AppShell/KeyRecorderView.swift +0 -210
- package/apps/mac/Sources/AppShell/LatticesRuntime.swift +0 -104
- package/apps/mac/Sources/AppShell/MainView.swift +0 -847
- package/apps/mac/Sources/AppShell/MainWindow.swift +0 -83
- package/apps/mac/Sources/AppShell/MenuBarController.swift +0 -177
- package/apps/mac/Sources/AppShell/OnboardingView.swift +0 -483
- package/apps/mac/Sources/AppShell/PermissionsAssistantView.swift +0 -366
- package/apps/mac/Sources/AppShell/PermissionsAssistantWindow.swift +0 -70
- package/apps/mac/Sources/AppShell/Preferences.swift +0 -297
- package/apps/mac/Sources/AppShell/SettingsView.swift +0 -3163
- package/apps/mac/Sources/AppShell/SettingsWindow.swift +0 -34
- package/apps/mac/Sources/AppShell/WorkspaceInspectorPresenter.swift +0 -13
- package/apps/mac/Sources/Core/Actions/HotkeyManager.swift +0 -256
- package/apps/mac/Sources/Core/Actions/HotkeyStore.swift +0 -399
- package/apps/mac/Sources/Core/Actions/IntentEngine.swift +0 -988
- package/apps/mac/Sources/Core/Actions/IntentSchema.swift +0 -94
- package/apps/mac/Sources/Core/Actions/Intents/CreateLayerIntent.swift +0 -54
- package/apps/mac/Sources/Core/Actions/Intents/DistributeIntent.swift +0 -56
- package/apps/mac/Sources/Core/Actions/Intents/FocusIntent.swift +0 -69
- package/apps/mac/Sources/Core/Actions/Intents/HelpIntent.swift +0 -41
- package/apps/mac/Sources/Core/Actions/Intents/KillIntent.swift +0 -47
- package/apps/mac/Sources/Core/Actions/Intents/LatticeIntent.swift +0 -53
- package/apps/mac/Sources/Core/Actions/Intents/LaunchIntent.swift +0 -67
- package/apps/mac/Sources/Core/Actions/Intents/ListSessionsIntent.swift +0 -32
- package/apps/mac/Sources/Core/Actions/Intents/ListWindowsIntent.swift +0 -30
- package/apps/mac/Sources/Core/Actions/Intents/ScanIntent.swift +0 -52
- package/apps/mac/Sources/Core/Actions/Intents/SearchIntent.swift +0 -190
- package/apps/mac/Sources/Core/Actions/Intents/SwitchLayerIntent.swift +0 -50
- package/apps/mac/Sources/Core/Actions/Intents/TileIntent.swift +0 -61
- package/apps/mac/Sources/Core/Actions/PaletteCommand.swift +0 -439
- package/apps/mac/Sources/Core/Actions/VoiceIntentResolver.swift +0 -713
- package/apps/mac/Sources/Core/Companion/CompanionActivityLog.swift +0 -70
- package/apps/mac/Sources/Core/Companion/CompanionKeyboardController.swift +0 -141
- package/apps/mac/Sources/Core/Companion/LatticesCompanionBridgeServer.swift +0 -454
- package/apps/mac/Sources/Core/Companion/LatticesCompanionCockpit.swift +0 -555
- package/apps/mac/Sources/Core/Companion/LatticesCompanionSecurityCoordinator.swift +0 -629
- package/apps/mac/Sources/Core/Companion/LatticesCompanionTrackpadController.swift +0 -204
- package/apps/mac/Sources/Core/Companion/LatticesDeckHost.swift +0 -1463
- package/apps/mac/Sources/Core/Daemon/DaemonProtocol.swift +0 -114
- package/apps/mac/Sources/Core/Daemon/DaemonServer.swift +0 -427
- package/apps/mac/Sources/Core/Daemon/LatticesApi.swift +0 -2965
- package/apps/mac/Sources/Core/Desktop/AccessibilityTextExtractor.swift +0 -111
- package/apps/mac/Sources/Core/Desktop/AppTypeClassifier.swift +0 -106
- package/apps/mac/Sources/Core/Desktop/DesktopModel.swift +0 -331
- package/apps/mac/Sources/Core/Desktop/DesktopModelTypes.swift +0 -73
- package/apps/mac/Sources/Core/Desktop/InventoryManager.swift +0 -35
- package/apps/mac/Sources/Core/Desktop/InventoryPath.swift +0 -43
- package/apps/mac/Sources/Core/Desktop/MouseFinder.swift +0 -527
- package/apps/mac/Sources/Core/Desktop/OcrModel.swift +0 -467
- package/apps/mac/Sources/Core/Desktop/OcrStore.swift +0 -329
- package/apps/mac/Sources/Core/Desktop/PlacementSpec.swift +0 -195
- package/apps/mac/Sources/Core/Desktop/SessionWindowLocator.swift +0 -139
- package/apps/mac/Sources/Core/Desktop/TilePickerView.swift +0 -209
- package/apps/mac/Sources/Core/Desktop/WindowCapture.swift +0 -33
- package/apps/mac/Sources/Core/Desktop/WindowDragSnapController.swift +0 -429
- package/apps/mac/Sources/Core/Desktop/WindowPreviewCard.swift +0 -100
- package/apps/mac/Sources/Core/Desktop/WindowPreviewStore.swift +0 -112
- package/apps/mac/Sources/Core/Desktop/WindowSelectionStore.swift +0 -76
- package/apps/mac/Sources/Core/Desktop/WindowTiler.swift +0 -2222
- package/apps/mac/Sources/Core/Input/EventTapBreaker.swift +0 -124
- package/apps/mac/Sources/Core/Input/EventTapThread.swift +0 -54
- package/apps/mac/Sources/Core/Input/InputCaptureResetCenter.swift +0 -20
- package/apps/mac/Sources/Core/Input/KeyboardRemapConfig.swift +0 -69
- package/apps/mac/Sources/Core/Input/KeyboardRemapController.swift +0 -346
- package/apps/mac/Sources/Core/Input/KeyboardRemapStore.swift +0 -141
- package/apps/mac/Sources/Core/Input/MouseGestureConfig.swift +0 -499
- package/apps/mac/Sources/Core/Input/MouseGestureController.swift +0 -2583
- package/apps/mac/Sources/Core/Input/MouseInputDeviceStore.swift +0 -98
- package/apps/mac/Sources/Core/Input/MouseInputEventViewer.swift +0 -272
- package/apps/mac/Sources/Core/Input/MouseShortcutStore.swift +0 -170
- package/apps/mac/Sources/Core/Input/SecureEventInputMonitor.swift +0 -39
- package/apps/mac/Sources/Core/Input/ShapeRecognizer.swift +0 -624
- package/apps/mac/Sources/Core/Input/TapBudgetMeter.swift +0 -56
- package/apps/mac/Sources/Core/Overlays/AppWindowShell.swift +0 -63
- package/apps/mac/Sources/Core/Overlays/CommandMode/CommandModeState.swift +0 -1566
- package/apps/mac/Sources/Core/Overlays/CommandMode/CommandModeView.swift +0 -1927
- package/apps/mac/Sources/Core/Overlays/CommandMode/CommandModeWindow.swift +0 -196
- package/apps/mac/Sources/Core/Overlays/CommandPalette/CommandPaletteView.swift +0 -307
- package/apps/mac/Sources/Core/Overlays/CommandPalette/CommandPaletteWindow.swift +0 -67
- package/apps/mac/Sources/Core/Overlays/HUD/CheatSheetHUD.swift +0 -576
- package/apps/mac/Sources/Core/Overlays/HUD/HUDBottomBar.swift +0 -279
- package/apps/mac/Sources/Core/Overlays/HUD/HUDController.swift +0 -1158
- package/apps/mac/Sources/Core/Overlays/HUD/HUDLeftBar.swift +0 -849
- package/apps/mac/Sources/Core/Overlays/HUD/HUDMinimap.swift +0 -179
- package/apps/mac/Sources/Core/Overlays/HUD/HUDRightBar.swift +0 -596
- package/apps/mac/Sources/Core/Overlays/HUD/HUDState.swift +0 -367
- package/apps/mac/Sources/Core/Overlays/HUD/HUDTopBar.swift +0 -243
- package/apps/mac/Sources/Core/Overlays/HUD/LauncherHUD.swift +0 -334
- package/apps/mac/Sources/Core/Overlays/HUD/LayerBezel.swift +0 -203
- package/apps/mac/Sources/Core/Overlays/OmniSearch/OmniSearchState.swift +0 -280
- package/apps/mac/Sources/Core/Overlays/OmniSearch/OmniSearchView.swift +0 -422
- package/apps/mac/Sources/Core/Overlays/OmniSearch/OmniSearchWindow.swift +0 -94
- package/apps/mac/Sources/Core/Overlays/OverlayPanelShell.swift +0 -241
- package/apps/mac/Sources/Core/Overlays/ScreenMap/ScreenMapState.swift +0 -3135
- package/apps/mac/Sources/Core/Overlays/ScreenMap/ScreenMapView.swift +0 -3977
- package/apps/mac/Sources/Core/Overlays/ScreenMap/ScreenMapWindowController.swift +0 -119
- package/apps/mac/Sources/Core/Overlays/ScreenOverlayCanvasController.swift +0 -1217
- package/apps/mac/Sources/Core/Overlays/Voice/VoiceCommandWindow.swift +0 -1575
- package/apps/mac/Sources/Core/Pi/PiAuthNextStepCard.swift +0 -148
- package/apps/mac/Sources/Core/Pi/PiAuthPromptCard.swift +0 -90
- package/apps/mac/Sources/Core/Pi/PiChatDock.swift +0 -564
- package/apps/mac/Sources/Core/Pi/PiChatSession.swift +0 -1948
- package/apps/mac/Sources/Core/Pi/PiInstallCallout.swift +0 -86
- package/apps/mac/Sources/Core/Pi/PiProviderSetupCallout.swift +0 -99
- package/apps/mac/Sources/Core/Pi/PiWorkspaceView.swift +0 -510
- package/apps/mac/Sources/Core/System/Capability.swift +0 -79
- package/apps/mac/Sources/Core/System/DiagnosticLog.swift +0 -373
- package/apps/mac/Sources/Core/System/EventBus.swift +0 -31
- package/apps/mac/Sources/Core/System/PermissionChecker.swift +0 -224
- package/apps/mac/Sources/Core/System/ProcessModel.swift +0 -199
- package/apps/mac/Sources/Core/System/ProcessQuery.swift +0 -151
- package/apps/mac/Sources/Core/System/SystemTelemetryMonitor.swift +0 -273
- package/apps/mac/Sources/Core/Voice/AdvisorLearningStore.swift +0 -90
- package/apps/mac/Sources/Core/Voice/AgentSession.swift +0 -377
- package/apps/mac/Sources/Core/Voice/AudioProvider.swift +0 -555
- package/apps/mac/Sources/Core/Voice/HandsOffSession.swift +0 -839
- package/apps/mac/Sources/Core/Voice/VoiceChatView.swift +0 -192
- package/apps/mac/Sources/Core/Voice/VoxClient.swift +0 -454
- package/apps/mac/Sources/Core/Workspace/Project.swift +0 -28
- package/apps/mac/Sources/Core/Workspace/ProjectScanner.swift +0 -141
- package/apps/mac/Sources/Core/Workspace/SessionLayerStore.swift +0 -285
- package/apps/mac/Sources/Core/Workspace/SessionManager.swift +0 -75
- package/apps/mac/Sources/Core/Workspace/Terminal/Terminal.swift +0 -259
- package/apps/mac/Sources/Core/Workspace/Terminal/TerminalQuery.swift +0 -156
- package/apps/mac/Sources/Core/Workspace/Terminal/TerminalSynthesizer.swift +0 -200
- package/apps/mac/Sources/Core/Workspace/Tmux/TmuxModel.swift +0 -60
- package/apps/mac/Sources/Core/Workspace/Tmux/TmuxQuery.swift +0 -105
- package/apps/mac/Sources/Core/Workspace/WorkspaceManager.swift +0 -1027
- package/apps/mac/Sources/UI/ActionRow.swift +0 -78
- package/apps/mac/Sources/UI/OrphanRow.swift +0 -129
- package/apps/mac/Sources/UI/ProjectRow.swift +0 -368
- package/apps/mac/Sources/UI/TabGroupRow.swift +0 -178
- package/apps/mac/Sources/UI/Theme.swift +0 -164
- package/apps/mac/Tests/StageDragTests.swift +0 -333
- package/apps/mac/Tests/StageJoinTests.swift +0 -313
- package/apps/mac/Tests/StageManagerTests.swift +0 -280
- package/apps/mac/Tests/StageTileTests.swift +0 -353
- package/swift/Package.swift +0 -20
- package/swift/Sources/DeckKit/DeckAction.swift +0 -51
- package/swift/Sources/DeckKit/DeckBridgeSecurity.swift +0 -152
- package/swift/Sources/DeckKit/DeckCockpit.swift +0 -82
- package/swift/Sources/DeckKit/DeckHost.swift +0 -7
- package/swift/Sources/DeckKit/DeckManifest.swift +0 -145
- package/swift/Sources/DeckKit/DeckRuntimeSnapshot.swift +0 -533
- package/swift/Sources/DeckKit/DeckTrackpad.swift +0 -63
- package/swift/Sources/DeckKit/DeckValue.swift +0 -93
- package/swift/Sources/DeckKit/DeckVoiceError.swift +0 -88
- package/swift/Tests/DeckKitTests/DeckKitTests.swift +0 -286
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
import SwiftUI
|
|
2
|
-
|
|
3
|
-
// MARK: - Navigation Pages
|
|
4
|
-
|
|
5
|
-
enum AppPage: String, CaseIterable {
|
|
6
|
-
case home
|
|
7
|
-
case screenMap
|
|
8
|
-
case desktopInventory
|
|
9
|
-
case pi
|
|
10
|
-
case settings
|
|
11
|
-
case companionSettings
|
|
12
|
-
case docs
|
|
13
|
-
|
|
14
|
-
var label: String {
|
|
15
|
-
switch self {
|
|
16
|
-
case .home: return "Home"
|
|
17
|
-
case .screenMap: return "Layout"
|
|
18
|
-
case .desktopInventory: return "Desktop Inventory"
|
|
19
|
-
case .pi: return "Assistant"
|
|
20
|
-
case .settings: return "Settings"
|
|
21
|
-
case .companionSettings:return "Settings"
|
|
22
|
-
case .docs: return "Docs"
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
var icon: String {
|
|
27
|
-
switch self {
|
|
28
|
-
case .home: return "house"
|
|
29
|
-
case .screenMap: return "rectangle.3.group"
|
|
30
|
-
case .desktopInventory: return "macwindow.on.rectangle"
|
|
31
|
-
case .pi: return "bubble.left.and.bubble.right"
|
|
32
|
-
case .settings: return "gearshape"
|
|
33
|
-
case .companionSettings:return "ipad.and.iphone"
|
|
34
|
-
case .docs: return "book"
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/// Pages shown as primary tabs in the unified window
|
|
39
|
-
static var primaryTabs: [AppPage] { [.home, .screenMap, .desktopInventory, .pi] }
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// MARK: - App Shell View
|
|
43
|
-
|
|
44
|
-
struct AppShellView: View {
|
|
45
|
-
@ObservedObject var controller: ScreenMapController
|
|
46
|
-
@ObservedObject var windowController = ScreenMapWindowController.shared
|
|
47
|
-
@StateObject private var commandState = CommandModeState()
|
|
48
|
-
|
|
49
|
-
var body: some View {
|
|
50
|
-
VStack(spacing: 0) {
|
|
51
|
-
// Tab bar (only on primary pages)
|
|
52
|
-
if AppPage.primaryTabs.contains(windowController.activePage) {
|
|
53
|
-
tabBar
|
|
54
|
-
Rectangle().fill(Palette.border).frame(height: 0.5)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
contentArea
|
|
58
|
-
}
|
|
59
|
-
.background(Palette.bg)
|
|
60
|
-
.onAppear {
|
|
61
|
-
commandState.onDismiss = { windowController.activePage = .home }
|
|
62
|
-
syncPageState(windowController.activePage)
|
|
63
|
-
}
|
|
64
|
-
.onChange(of: windowController.activePage) { page in
|
|
65
|
-
syncPageState(page)
|
|
66
|
-
clearRelevantDismissals(for: page)
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/// Entering a feature page clears its capability snooze — the user is
|
|
71
|
-
/// telling us they want this to work, so the banner can resurface.
|
|
72
|
-
private func clearRelevantDismissals(for page: AppPage) {
|
|
73
|
-
let prefs = Preferences.shared
|
|
74
|
-
switch page {
|
|
75
|
-
case .screenMap:
|
|
76
|
-
prefs.clearDismissal(Capability.windowControl.rawValue)
|
|
77
|
-
case .desktopInventory:
|
|
78
|
-
prefs.clearDismissal(Capability.screenSearch.rawValue)
|
|
79
|
-
default:
|
|
80
|
-
break
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// MARK: - Tab Bar
|
|
85
|
-
|
|
86
|
-
private var tabBar: some View {
|
|
87
|
-
HStack(spacing: 0) {
|
|
88
|
-
ForEach(AppPage.primaryTabs, id: \.rawValue) { tab in
|
|
89
|
-
tabButton(tab)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
Spacer()
|
|
93
|
-
}
|
|
94
|
-
.padding(.horizontal, 12)
|
|
95
|
-
.padding(.top, 8)
|
|
96
|
-
.padding(.bottom, 4)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
private func tabButton(_ tab: AppPage) -> some View {
|
|
100
|
-
let isActive = windowController.activePage == tab
|
|
101
|
-
|
|
102
|
-
return Button {
|
|
103
|
-
windowController.activePage = tab
|
|
104
|
-
if tab == .screenMap { controller.enter() }
|
|
105
|
-
if tab == .desktopInventory { commandState.enter() }
|
|
106
|
-
} label: {
|
|
107
|
-
HStack(spacing: 5) {
|
|
108
|
-
Image(systemName: tab.icon)
|
|
109
|
-
.font(.system(size: 10))
|
|
110
|
-
Text(tab.label)
|
|
111
|
-
.font(Typo.monoBold(11))
|
|
112
|
-
}
|
|
113
|
-
.foregroundColor(isActive ? Palette.text : Palette.textMuted)
|
|
114
|
-
.padding(.horizontal, 12)
|
|
115
|
-
.padding(.vertical, 6)
|
|
116
|
-
.contentShape(Rectangle())
|
|
117
|
-
.background(
|
|
118
|
-
RoundedRectangle(cornerRadius: 6)
|
|
119
|
-
.fill(isActive ? Palette.surfaceHov : Color.clear)
|
|
120
|
-
)
|
|
121
|
-
}
|
|
122
|
-
.buttonStyle(.plain)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// MARK: - Content Area
|
|
126
|
-
|
|
127
|
-
@ViewBuilder
|
|
128
|
-
private var contentArea: some View {
|
|
129
|
-
switch windowController.activePage {
|
|
130
|
-
case .home:
|
|
131
|
-
HomeDashboardView(onNavigate: { page in
|
|
132
|
-
windowController.activePage = page
|
|
133
|
-
if page == .screenMap { controller.enter() }
|
|
134
|
-
if page == .desktopInventory { commandState.enter() }
|
|
135
|
-
})
|
|
136
|
-
case .screenMap:
|
|
137
|
-
ScreenMapView(controller: controller, onNavigate: { page in
|
|
138
|
-
windowController.activePage = page
|
|
139
|
-
})
|
|
140
|
-
case .desktopInventory:
|
|
141
|
-
CommandModeView(state: commandState, presentation: .embedded)
|
|
142
|
-
case .pi:
|
|
143
|
-
PiWorkspaceView()
|
|
144
|
-
case .settings:
|
|
145
|
-
SettingsContentView(
|
|
146
|
-
prefs: Preferences.shared,
|
|
147
|
-
scanner: ProjectScanner.shared,
|
|
148
|
-
onBack: { windowController.activePage = .screenMap; controller.enter() }
|
|
149
|
-
)
|
|
150
|
-
case .companionSettings:
|
|
151
|
-
SettingsContentView(
|
|
152
|
-
page: .companionSettings,
|
|
153
|
-
prefs: Preferences.shared,
|
|
154
|
-
scanner: ProjectScanner.shared,
|
|
155
|
-
onBack: { windowController.activePage = .screenMap; controller.enter() }
|
|
156
|
-
)
|
|
157
|
-
case .docs:
|
|
158
|
-
SettingsContentView(
|
|
159
|
-
page: .docs,
|
|
160
|
-
prefs: Preferences.shared,
|
|
161
|
-
scanner: ProjectScanner.shared,
|
|
162
|
-
onBack: { windowController.activePage = .screenMap; controller.enter() }
|
|
163
|
-
)
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
private func syncPageState(_ page: AppPage) {
|
|
168
|
-
if page == .screenMap { controller.enter() }
|
|
169
|
-
if page == .desktopInventory { commandState.enter() }
|
|
170
|
-
}
|
|
171
|
-
}
|
|
@@ -1,305 +0,0 @@
|
|
|
1
|
-
import AppKit
|
|
2
|
-
import Combine
|
|
3
|
-
import Foundation
|
|
4
|
-
import SwiftUI
|
|
5
|
-
|
|
6
|
-
struct LatticesUpdateInfo: Equatable {
|
|
7
|
-
let version: String
|
|
8
|
-
let downloadURL: URL
|
|
9
|
-
let releaseNotes: String
|
|
10
|
-
let publishedAt: Date
|
|
11
|
-
let htmlURL: URL
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
@MainActor
|
|
15
|
-
final class AppUpdater: ObservableObject {
|
|
16
|
-
static let shared = AppUpdater()
|
|
17
|
-
|
|
18
|
-
@Published private(set) var isUpdating = false
|
|
19
|
-
@Published private(set) var statusMessage: String?
|
|
20
|
-
@Published private(set) var availableUpdate: LatticesUpdateInfo?
|
|
21
|
-
@Published private(set) var isChecking = false
|
|
22
|
-
@Published private(set) var lastChecked: Date?
|
|
23
|
-
@Published private(set) var lastError: String?
|
|
24
|
-
|
|
25
|
-
@AppStorage("appUpdater.autoCheck") var autoCheckEnabled = true
|
|
26
|
-
@AppStorage("appUpdater.lastCheckTime") private var lastCheckTimeInterval: Double = 0
|
|
27
|
-
@AppStorage("appUpdater.skippedVersion") private var skippedVersion = ""
|
|
28
|
-
|
|
29
|
-
private let checkInterval: TimeInterval = 24 * 60 * 60
|
|
30
|
-
|
|
31
|
-
private init() {}
|
|
32
|
-
|
|
33
|
-
var currentVersion: String { LatticesRuntime.appVersion }
|
|
34
|
-
var currentDisplayVersion: String { LatticesRuntime.appDisplayVersion }
|
|
35
|
-
|
|
36
|
-
var canUpdate: Bool {
|
|
37
|
-
LatticesRuntime.bunPath != nil && LatticesRuntime.appHelperScriptPath != nil
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
var unavailableReason: String? {
|
|
41
|
-
if LatticesRuntime.bunPath == nil {
|
|
42
|
-
return "Install Bun to enable in-app updates."
|
|
43
|
-
}
|
|
44
|
-
if LatticesRuntime.appHelperScriptPath == nil {
|
|
45
|
-
return "Launch Lattices via `lattices app` so the updater can find the CLI bundle."
|
|
46
|
-
}
|
|
47
|
-
return nil
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
func checkIfNeeded() async {
|
|
51
|
-
guard autoCheckEnabled else { return }
|
|
52
|
-
|
|
53
|
-
let now = Date()
|
|
54
|
-
let lastCheck = Date(timeIntervalSince1970: lastCheckTimeInterval)
|
|
55
|
-
if now.timeIntervalSince(lastCheck) < checkInterval { return }
|
|
56
|
-
|
|
57
|
-
await check()
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
func check() async {
|
|
61
|
-
guard !isChecking else { return }
|
|
62
|
-
|
|
63
|
-
isChecking = true
|
|
64
|
-
lastError = nil
|
|
65
|
-
|
|
66
|
-
defer {
|
|
67
|
-
isChecking = false
|
|
68
|
-
lastChecked = Date()
|
|
69
|
-
lastCheckTimeInterval = Date().timeIntervalSince1970
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
do {
|
|
73
|
-
let release = try await fetchLatestRelease()
|
|
74
|
-
guard let update = parseRelease(release), isNewerVersion(update.version) else {
|
|
75
|
-
availableUpdate = nil
|
|
76
|
-
return
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if update.version == skippedVersion {
|
|
80
|
-
availableUpdate = nil
|
|
81
|
-
return
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
availableUpdate = update
|
|
85
|
-
} catch UpdateCheckError.noRelease {
|
|
86
|
-
availableUpdate = nil
|
|
87
|
-
} catch {
|
|
88
|
-
lastError = error.localizedDescription
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
func skipCurrentUpdate() {
|
|
93
|
-
guard let update = availableUpdate else { return }
|
|
94
|
-
skippedVersion = update.version
|
|
95
|
-
availableUpdate = nil
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
func viewCurrentRelease() {
|
|
99
|
-
if let update = availableUpdate {
|
|
100
|
-
NSWorkspace.shared.open(update.htmlURL)
|
|
101
|
-
} else if let url = URL(string: "https://github.com/arach/lattices/releases/latest") {
|
|
102
|
-
NSWorkspace.shared.open(url)
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
func promptForUpdate() {
|
|
107
|
-
guard canUpdate else {
|
|
108
|
-
presentAlert(
|
|
109
|
-
title: "Update Unavailable",
|
|
110
|
-
message: unavailableReason ?? "Lattices could not locate its updater."
|
|
111
|
-
)
|
|
112
|
-
return
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
guard availableUpdate != nil else {
|
|
116
|
-
Task {
|
|
117
|
-
await check()
|
|
118
|
-
if availableUpdate != nil {
|
|
119
|
-
presentUpdateConfirmation()
|
|
120
|
-
} else if let error = lastError {
|
|
121
|
-
presentAlert(
|
|
122
|
-
title: "Could Not Check for Updates",
|
|
123
|
-
message: error
|
|
124
|
-
)
|
|
125
|
-
} else {
|
|
126
|
-
presentAlert(
|
|
127
|
-
title: "Lattices Is Up to Date",
|
|
128
|
-
message: "You’re running \(currentDisplayVersion), which is the latest available build for this install."
|
|
129
|
-
)
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
return
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
presentUpdateConfirmation()
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
private func presentUpdateConfirmation() {
|
|
139
|
-
let alert = NSAlert()
|
|
140
|
-
alert.alertStyle = .informational
|
|
141
|
-
if let update = availableUpdate {
|
|
142
|
-
alert.messageText = "Update Lattices?"
|
|
143
|
-
alert.informativeText = """
|
|
144
|
-
Current version: \(currentDisplayVersion)
|
|
145
|
-
New version: \(update.version)
|
|
146
|
-
|
|
147
|
-
Lattices will download the signed release, quit briefly, replace the app, and relaunch when the update is ready.
|
|
148
|
-
"""
|
|
149
|
-
} else {
|
|
150
|
-
alert.messageText = "Check and update Lattices?"
|
|
151
|
-
alert.informativeText = """
|
|
152
|
-
Current version: \(currentDisplayVersion)
|
|
153
|
-
New version: latest published release
|
|
154
|
-
|
|
155
|
-
Lattices will download the signed release, quit briefly, replace the app, and relaunch when the update is ready.
|
|
156
|
-
"""
|
|
157
|
-
}
|
|
158
|
-
alert.addButton(withTitle: availableUpdate == nil ? "Check & Update" : "Update")
|
|
159
|
-
alert.addButton(withTitle: "Cancel")
|
|
160
|
-
|
|
161
|
-
guard alert.runModal() == .alertFirstButtonReturn else { return }
|
|
162
|
-
startDetachedUpdate()
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
private func startDetachedUpdate() {
|
|
166
|
-
guard !isUpdating else { return }
|
|
167
|
-
guard let bunPath = LatticesRuntime.bunPath,
|
|
168
|
-
let scriptPath = LatticesRuntime.appHelperScriptPath else {
|
|
169
|
-
presentAlert(
|
|
170
|
-
title: "Update Unavailable",
|
|
171
|
-
message: unavailableReason ?? "Lattices could not locate its updater."
|
|
172
|
-
)
|
|
173
|
-
return
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
let proc = Process()
|
|
177
|
-
proc.executableURL = URL(fileURLWithPath: bunPath)
|
|
178
|
-
proc.arguments = [scriptPath, "update", "--detach", "--launch"]
|
|
179
|
-
if let cliRoot = LatticesRuntime.cliRoot {
|
|
180
|
-
proc.currentDirectoryURL = URL(fileURLWithPath: cliRoot)
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
var env = ProcessInfo.processInfo.environment
|
|
184
|
-
env.removeValue(forKey: "CLAUDECODE")
|
|
185
|
-
proc.environment = env
|
|
186
|
-
|
|
187
|
-
do {
|
|
188
|
-
try proc.run()
|
|
189
|
-
isUpdating = true
|
|
190
|
-
if let update = availableUpdate {
|
|
191
|
-
statusMessage = "Preparing Lattices \(update.version). The app will relaunch when the update is ready."
|
|
192
|
-
} else {
|
|
193
|
-
statusMessage = "Preparing the latest Lattices release. The app will relaunch when the update is ready."
|
|
194
|
-
}
|
|
195
|
-
} catch {
|
|
196
|
-
presentAlert(
|
|
197
|
-
title: "Update Failed",
|
|
198
|
-
message: "Lattices could not start the updater.\n\n\(error.localizedDescription)"
|
|
199
|
-
)
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
private func presentAlert(title: String, message: String) {
|
|
204
|
-
let alert = NSAlert()
|
|
205
|
-
alert.alertStyle = .warning
|
|
206
|
-
alert.messageText = title
|
|
207
|
-
alert.informativeText = message
|
|
208
|
-
alert.addButton(withTitle: "OK")
|
|
209
|
-
alert.runModal()
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
private func fetchLatestRelease() async throws -> GitHubRelease {
|
|
213
|
-
let url = URL(string: "https://api.github.com/repos/arach/lattices/releases/latest")!
|
|
214
|
-
var request = URLRequest(url: url)
|
|
215
|
-
request.setValue("application/vnd.github+json", forHTTPHeaderField: "Accept")
|
|
216
|
-
request.setValue("Lattices/\(currentVersion)", forHTTPHeaderField: "User-Agent")
|
|
217
|
-
|
|
218
|
-
let (data, response) = try await URLSession.shared.data(for: request)
|
|
219
|
-
guard let http = response as? HTTPURLResponse else {
|
|
220
|
-
throw UpdateCheckError.invalidResponse
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
switch http.statusCode {
|
|
224
|
-
case 200:
|
|
225
|
-
let decoder = JSONDecoder()
|
|
226
|
-
decoder.keyDecodingStrategy = .convertFromSnakeCase
|
|
227
|
-
decoder.dateDecodingStrategy = .iso8601
|
|
228
|
-
return try decoder.decode(GitHubRelease.self, from: data)
|
|
229
|
-
case 404:
|
|
230
|
-
throw UpdateCheckError.noRelease
|
|
231
|
-
case 403:
|
|
232
|
-
throw UpdateCheckError.rateLimited
|
|
233
|
-
default:
|
|
234
|
-
throw UpdateCheckError.httpError(http.statusCode)
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
private func parseRelease(_ release: GitHubRelease) -> LatticesUpdateInfo? {
|
|
239
|
-
guard !release.draft, !release.prerelease else { return nil }
|
|
240
|
-
|
|
241
|
-
let asset = release.assets.first { asset in
|
|
242
|
-
asset.name == "Lattices.dmg" ||
|
|
243
|
-
(asset.name.hasPrefix("Lattices") && asset.name.hasSuffix(".dmg"))
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
guard let asset,
|
|
247
|
-
let downloadURL = URL(string: asset.browserDownloadUrl),
|
|
248
|
-
let htmlURL = URL(string: release.htmlUrl) else {
|
|
249
|
-
return nil
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
let version = release.tagName.hasPrefix("v")
|
|
253
|
-
? String(release.tagName.dropFirst())
|
|
254
|
-
: release.tagName
|
|
255
|
-
|
|
256
|
-
return LatticesUpdateInfo(
|
|
257
|
-
version: version,
|
|
258
|
-
downloadURL: downloadURL,
|
|
259
|
-
releaseNotes: release.body ?? "",
|
|
260
|
-
publishedAt: release.publishedAt,
|
|
261
|
-
htmlURL: htmlURL
|
|
262
|
-
)
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
private func isNewerVersion(_ remoteVersion: String) -> Bool {
|
|
266
|
-
guard currentVersion != "unknown" else { return false }
|
|
267
|
-
return remoteVersion.compare(currentVersion, options: .numeric) == .orderedDescending
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
private struct GitHubRelease: Decodable {
|
|
272
|
-
let tagName: String
|
|
273
|
-
let name: String
|
|
274
|
-
let body: String?
|
|
275
|
-
let htmlUrl: String
|
|
276
|
-
let publishedAt: Date
|
|
277
|
-
let assets: [GitHubAsset]
|
|
278
|
-
let prerelease: Bool
|
|
279
|
-
let draft: Bool
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
private struct GitHubAsset: Decodable {
|
|
283
|
-
let name: String
|
|
284
|
-
let browserDownloadUrl: String
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
private enum UpdateCheckError: LocalizedError {
|
|
288
|
-
case invalidResponse
|
|
289
|
-
case noRelease
|
|
290
|
-
case rateLimited
|
|
291
|
-
case httpError(Int)
|
|
292
|
-
|
|
293
|
-
var errorDescription: String? {
|
|
294
|
-
switch self {
|
|
295
|
-
case .invalidResponse:
|
|
296
|
-
return "Invalid response from GitHub."
|
|
297
|
-
case .noRelease:
|
|
298
|
-
return "No published release found."
|
|
299
|
-
case .rateLimited:
|
|
300
|
-
return "GitHub rate limited the update check."
|
|
301
|
-
case .httpError(let code):
|
|
302
|
-
return "GitHub returned HTTP \(code)."
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import AppKit
|
|
2
|
-
|
|
3
|
-
enum CliActionLauncher {
|
|
4
|
-
private static var defaultDirectory: String {
|
|
5
|
-
let root = Preferences.shared.scanRoot
|
|
6
|
-
return root.isEmpty ? NSHomeDirectory() : root
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
private static func chooseProjectDirectory(message: String, prompt: String) -> String? {
|
|
10
|
-
let panel = NSOpenPanel()
|
|
11
|
-
panel.message = message
|
|
12
|
-
panel.prompt = prompt
|
|
13
|
-
panel.canChooseFiles = false
|
|
14
|
-
panel.canChooseDirectories = true
|
|
15
|
-
panel.allowsMultipleSelection = false
|
|
16
|
-
panel.directoryURL = URL(fileURLWithPath: defaultDirectory)
|
|
17
|
-
return panel.runModal() == .OK ? panel.url?.path : nil
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
static func initializeProjectInTerminal() {
|
|
21
|
-
guard let directory = chooseProjectDirectory(
|
|
22
|
-
message: "Choose a project folder to initialize with Lattices.",
|
|
23
|
-
prompt: "Initialize"
|
|
24
|
-
) else { return }
|
|
25
|
-
|
|
26
|
-
Preferences.shared.terminal.launch(
|
|
27
|
-
command: "lattices init && lattices start",
|
|
28
|
-
in: directory
|
|
29
|
-
)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
static func launchProjectInTerminal() {
|
|
33
|
-
guard let directory = chooseProjectDirectory(
|
|
34
|
-
message: "Choose a project folder to launch with Lattices.",
|
|
35
|
-
prompt: "Launch"
|
|
36
|
-
) else { return }
|
|
37
|
-
|
|
38
|
-
Preferences.shared.terminal.launch(
|
|
39
|
-
command: "lattices start",
|
|
40
|
-
in: directory
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
static func installTmuxInTerminal() {
|
|
45
|
-
Preferences.shared.terminal.launch(
|
|
46
|
-
command: "brew install tmux",
|
|
47
|
-
in: defaultDirectory
|
|
48
|
-
)
|
|
49
|
-
}
|
|
50
|
-
}
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import SwiftUI
|
|
2
|
-
|
|
3
|
-
struct HomeDashboardView: View {
|
|
4
|
-
var onNavigate: ((AppPage) -> Void)? = nil
|
|
5
|
-
|
|
6
|
-
@ObservedObject private var scanner = ProjectScanner.shared
|
|
7
|
-
@ObservedObject private var piSession = PiChatSession.shared
|
|
8
|
-
|
|
9
|
-
var body: some View {
|
|
10
|
-
VStack(spacing: 0) {
|
|
11
|
-
hero
|
|
12
|
-
|
|
13
|
-
Rectangle()
|
|
14
|
-
.fill(Palette.border)
|
|
15
|
-
.frame(height: 0.5)
|
|
16
|
-
|
|
17
|
-
MainView(scanner: scanner, layout: .embedded)
|
|
18
|
-
}
|
|
19
|
-
.background(Palette.bg)
|
|
20
|
-
.onAppear {
|
|
21
|
-
piSession.refreshBinaryAvailability()
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
private var hero: some View {
|
|
26
|
-
VStack(alignment: .leading, spacing: 18) {
|
|
27
|
-
HStack(alignment: .top) {
|
|
28
|
-
VStack(alignment: .leading, spacing: 8) {
|
|
29
|
-
Text("Lattices Home")
|
|
30
|
-
.font(Typo.heading(18))
|
|
31
|
-
.foregroundColor(Palette.text)
|
|
32
|
-
|
|
33
|
-
Text("Workspace status, project launch, layout, search, and chat in one place.")
|
|
34
|
-
.font(Typo.mono(11))
|
|
35
|
-
.foregroundColor(Palette.textDim)
|
|
36
|
-
.fixedSize(horizontal: false, vertical: true)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
Spacer()
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
HStack(spacing: 10) {
|
|
43
|
-
homeActionCard(
|
|
44
|
-
title: "Layout",
|
|
45
|
-
subtitle: "Arrange windows",
|
|
46
|
-
icon: "rectangle.3.group",
|
|
47
|
-
tint: Palette.running
|
|
48
|
-
) {
|
|
49
|
-
onNavigate?(.screenMap)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
homeActionCard(
|
|
53
|
-
title: "Search",
|
|
54
|
-
subtitle: "Find workspace context",
|
|
55
|
-
icon: "magnifyingglass",
|
|
56
|
-
tint: Palette.detach
|
|
57
|
-
) {
|
|
58
|
-
onNavigate?(.desktopInventory)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
homeActionCard(
|
|
62
|
-
title: "Chat",
|
|
63
|
-
subtitle: piSession.hasPiBinary
|
|
64
|
-
? (piSession.needsProviderSetup || piSession.isAuthenticating
|
|
65
|
-
? piSession.setupStatusSummary
|
|
66
|
-
: "Standalone conversation surface")
|
|
67
|
-
: "Install Pi to enable the assistant",
|
|
68
|
-
icon: "bubble.left.and.bubble.right",
|
|
69
|
-
tint: piSession.hasPiBinary ? Palette.text : Palette.kill
|
|
70
|
-
) {
|
|
71
|
-
onNavigate?(.pi)
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
.padding(.horizontal, 16)
|
|
76
|
-
.padding(.vertical, 16)
|
|
77
|
-
.background(
|
|
78
|
-
LinearGradient(
|
|
79
|
-
colors: [
|
|
80
|
-
Palette.running.opacity(0.08),
|
|
81
|
-
Color.black.opacity(0.18),
|
|
82
|
-
],
|
|
83
|
-
startPoint: .topLeading,
|
|
84
|
-
endPoint: .bottomTrailing
|
|
85
|
-
)
|
|
86
|
-
)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
private func homeActionCard(
|
|
90
|
-
title: String,
|
|
91
|
-
subtitle: String,
|
|
92
|
-
icon: String,
|
|
93
|
-
tint: Color,
|
|
94
|
-
action: @escaping () -> Void
|
|
95
|
-
) -> some View {
|
|
96
|
-
Button(action: action) {
|
|
97
|
-
VStack(alignment: .leading, spacing: 10) {
|
|
98
|
-
HStack {
|
|
99
|
-
Image(systemName: icon)
|
|
100
|
-
.font(.system(size: 12, weight: .semibold))
|
|
101
|
-
.foregroundColor(tint)
|
|
102
|
-
|
|
103
|
-
Spacer()
|
|
104
|
-
|
|
105
|
-
Circle()
|
|
106
|
-
.fill(tint.opacity(0.85))
|
|
107
|
-
.frame(width: 6, height: 6)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
Text(title)
|
|
111
|
-
.font(Typo.monoBold(12))
|
|
112
|
-
.foregroundColor(Palette.text)
|
|
113
|
-
|
|
114
|
-
Text(subtitle)
|
|
115
|
-
.font(Typo.mono(10))
|
|
116
|
-
.foregroundColor(Palette.textMuted)
|
|
117
|
-
.multilineTextAlignment(.leading)
|
|
118
|
-
.fixedSize(horizontal: false, vertical: true)
|
|
119
|
-
}
|
|
120
|
-
.frame(maxWidth: .infinity, alignment: .leading)
|
|
121
|
-
.padding(12)
|
|
122
|
-
.background(
|
|
123
|
-
RoundedRectangle(cornerRadius: 8)
|
|
124
|
-
.fill(Palette.surface.opacity(0.7))
|
|
125
|
-
.overlay(
|
|
126
|
-
RoundedRectangle(cornerRadius: 8)
|
|
127
|
-
.strokeBorder(tint.opacity(0.18), lineWidth: 0.5)
|
|
128
|
-
)
|
|
129
|
-
)
|
|
130
|
-
}
|
|
131
|
-
.buttonStyle(.plain)
|
|
132
|
-
}
|
|
133
|
-
}
|