@lattices/cli 0.4.13 → 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 +191 -63
- 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 -2271
- 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,209 +0,0 @@
|
|
|
1
|
-
import SwiftUI
|
|
2
|
-
|
|
3
|
-
struct TilePickerView: View {
|
|
4
|
-
let sessionName: String
|
|
5
|
-
let terminal: Terminal
|
|
6
|
-
let onSelect: (TilePosition) -> Void
|
|
7
|
-
let onGoToSpace: (Int) -> Void // space ID
|
|
8
|
-
let onDismiss: () -> Void
|
|
9
|
-
|
|
10
|
-
@State private var hoveredTile: TilePosition?
|
|
11
|
-
@State private var hoveredSpace: Int? // space ID
|
|
12
|
-
@State private var displaySpaces: [DisplaySpaces] = []
|
|
13
|
-
@State private var windowSpaceId: Int = 0
|
|
14
|
-
@State private var currentTile: TilePosition?
|
|
15
|
-
|
|
16
|
-
private let grid: [[TilePosition]] = [
|
|
17
|
-
[.topLeft, .topRight],
|
|
18
|
-
[.left, .right],
|
|
19
|
-
[.bottomLeft, .bottomRight],
|
|
20
|
-
]
|
|
21
|
-
|
|
22
|
-
var body: some View {
|
|
23
|
-
VStack(spacing: 8) {
|
|
24
|
-
HStack {
|
|
25
|
-
Text("TILE WINDOW")
|
|
26
|
-
.font(Typo.pixel(12))
|
|
27
|
-
.foregroundColor(Palette.running)
|
|
28
|
-
Spacer()
|
|
29
|
-
Button(action: onDismiss) {
|
|
30
|
-
Image(systemName: "xmark")
|
|
31
|
-
.font(.system(size: 8, weight: .bold))
|
|
32
|
-
.foregroundColor(Palette.textDim)
|
|
33
|
-
.frame(width: 18, height: 18)
|
|
34
|
-
.background(
|
|
35
|
-
RoundedRectangle(cornerRadius: 3)
|
|
36
|
-
.fill(Palette.surface)
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
.buttonStyle(.plain)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Tile grid
|
|
43
|
-
VStack(spacing: 3) {
|
|
44
|
-
ForEach(grid, id: \.first?.id) { row in
|
|
45
|
-
HStack(spacing: 3) {
|
|
46
|
-
ForEach(row) { tile in
|
|
47
|
-
tileCell(tile)
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
HStack(spacing: 3) {
|
|
54
|
-
tileWideCell(.maximize)
|
|
55
|
-
tileWideCell(.center)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Spaces per display — navigate to space
|
|
59
|
-
ForEach(displaySpaces, id: \.displayIndex) { display in
|
|
60
|
-
if display.spaces.count > 1 || displaySpaces.count > 1 {
|
|
61
|
-
Rectangle()
|
|
62
|
-
.fill(Palette.border)
|
|
63
|
-
.frame(height: 0.5)
|
|
64
|
-
.padding(.vertical, 2)
|
|
65
|
-
|
|
66
|
-
HStack {
|
|
67
|
-
Text(displaySpaces.count > 1
|
|
68
|
-
? "DISPLAY \(display.displayIndex + 1) SPACES"
|
|
69
|
-
: "GO TO SPACE")
|
|
70
|
-
.font(Typo.pixel(10))
|
|
71
|
-
.foregroundColor(Palette.textMuted)
|
|
72
|
-
Spacer()
|
|
73
|
-
if windowSpaceId > 0 {
|
|
74
|
-
let windowOnDisplay = display.spaces.contains { $0.id == windowSpaceId }
|
|
75
|
-
if windowOnDisplay {
|
|
76
|
-
Text("window here")
|
|
77
|
-
.font(Typo.mono(9))
|
|
78
|
-
.foregroundColor(Palette.running)
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
HStack(spacing: 3) {
|
|
84
|
-
ForEach(display.spaces) { space in
|
|
85
|
-
spaceCell(space: space)
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
.padding(12)
|
|
92
|
-
.background(
|
|
93
|
-
RoundedRectangle(cornerRadius: 6)
|
|
94
|
-
.fill(Palette.surface)
|
|
95
|
-
.overlay(
|
|
96
|
-
RoundedRectangle(cornerRadius: 6)
|
|
97
|
-
.strokeBorder(Palette.borderLit, lineWidth: 0.5)
|
|
98
|
-
)
|
|
99
|
-
)
|
|
100
|
-
.onAppear {
|
|
101
|
-
displaySpaces = WindowTiler.getDisplaySpaces()
|
|
102
|
-
// Find which space this session's window is on + current tile
|
|
103
|
-
if let info = WindowTiler.getWindowInfo(session: sessionName, terminal: terminal) {
|
|
104
|
-
if let spaceId = WindowTiler.getSpacesForWindow(info.wid).first {
|
|
105
|
-
windowSpaceId = spaceId
|
|
106
|
-
}
|
|
107
|
-
currentTile = info.tilePosition
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
private func tileCell(_ tile: TilePosition) -> some View {
|
|
113
|
-
let isCurrent = currentTile == tile
|
|
114
|
-
let isHovered = hoveredTile == tile
|
|
115
|
-
return Button {
|
|
116
|
-
onSelect(tile)
|
|
117
|
-
} label: {
|
|
118
|
-
Image(systemName: tile.icon)
|
|
119
|
-
.font(.system(size: 14))
|
|
120
|
-
.foregroundColor(isHovered ? Palette.running : isCurrent ? Palette.running.opacity(0.8) : Palette.textDim)
|
|
121
|
-
.frame(maxWidth: .infinity)
|
|
122
|
-
.frame(height: 32)
|
|
123
|
-
.background(
|
|
124
|
-
RoundedRectangle(cornerRadius: 4)
|
|
125
|
-
.fill(isHovered ? Palette.running.opacity(0.1) : isCurrent ? Palette.running.opacity(0.06) : Palette.bg)
|
|
126
|
-
.overlay(
|
|
127
|
-
RoundedRectangle(cornerRadius: 4)
|
|
128
|
-
.strokeBorder(
|
|
129
|
-
isHovered ? Palette.running.opacity(0.3) : isCurrent ? Palette.running.opacity(0.25) : Palette.border,
|
|
130
|
-
lineWidth: isCurrent ? 1 : 0.5
|
|
131
|
-
)
|
|
132
|
-
)
|
|
133
|
-
)
|
|
134
|
-
}
|
|
135
|
-
.buttonStyle(.plain)
|
|
136
|
-
.onHover { hoveredTile = $0 ? tile : nil }
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
private func tileWideCell(_ tile: TilePosition) -> some View {
|
|
140
|
-
let isCurrent = currentTile == tile
|
|
141
|
-
let isHovered = hoveredTile == tile
|
|
142
|
-
return Button {
|
|
143
|
-
onSelect(tile)
|
|
144
|
-
} label: {
|
|
145
|
-
HStack(spacing: 4) {
|
|
146
|
-
Image(systemName: tile.icon)
|
|
147
|
-
.font(.system(size: 12))
|
|
148
|
-
Text(tile.label)
|
|
149
|
-
.font(Typo.mono(10))
|
|
150
|
-
}
|
|
151
|
-
.foregroundColor(isHovered ? Palette.running : isCurrent ? Palette.running.opacity(0.8) : Palette.textDim)
|
|
152
|
-
.frame(maxWidth: .infinity)
|
|
153
|
-
.frame(height: 28)
|
|
154
|
-
.background(
|
|
155
|
-
RoundedRectangle(cornerRadius: 4)
|
|
156
|
-
.fill(isHovered ? Palette.running.opacity(0.1) : isCurrent ? Palette.running.opacity(0.06) : Palette.bg)
|
|
157
|
-
.overlay(
|
|
158
|
-
RoundedRectangle(cornerRadius: 4)
|
|
159
|
-
.strokeBorder(
|
|
160
|
-
isHovered ? Palette.running.opacity(0.3) : isCurrent ? Palette.running.opacity(0.25) : Palette.border,
|
|
161
|
-
lineWidth: isCurrent ? 1 : 0.5
|
|
162
|
-
)
|
|
163
|
-
)
|
|
164
|
-
)
|
|
165
|
-
}
|
|
166
|
-
.buttonStyle(.plain)
|
|
167
|
-
.onHover { hoveredTile = $0 ? tile : nil }
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
private func spaceCell(space: SpaceInfo) -> some View {
|
|
171
|
-
let hasWindow = space.id == windowSpaceId
|
|
172
|
-
return Button {
|
|
173
|
-
onGoToSpace(space.id)
|
|
174
|
-
} label: {
|
|
175
|
-
HStack(spacing: 4) {
|
|
176
|
-
Image(systemName: space.isCurrent ? "desktopcomputer" : hasWindow ? "macwindow" : "rectangle.on.rectangle")
|
|
177
|
-
.font(.system(size: 10))
|
|
178
|
-
Text("\(space.index)")
|
|
179
|
-
.font(Typo.monoBold(11))
|
|
180
|
-
}
|
|
181
|
-
.foregroundColor(
|
|
182
|
-
hoveredSpace == space.id ? Palette.running :
|
|
183
|
-
hasWindow ? Palette.running :
|
|
184
|
-
space.isCurrent ? Palette.text : Palette.textDim
|
|
185
|
-
)
|
|
186
|
-
.frame(maxWidth: .infinity)
|
|
187
|
-
.frame(height: 28)
|
|
188
|
-
.background(
|
|
189
|
-
RoundedRectangle(cornerRadius: 4)
|
|
190
|
-
.fill(
|
|
191
|
-
hoveredSpace == space.id ? Palette.running.opacity(0.1) :
|
|
192
|
-
hasWindow ? Palette.running.opacity(0.05) :
|
|
193
|
-
space.isCurrent ? Palette.bg.opacity(0.5) : Palette.bg
|
|
194
|
-
)
|
|
195
|
-
.overlay(
|
|
196
|
-
RoundedRectangle(cornerRadius: 4)
|
|
197
|
-
.strokeBorder(
|
|
198
|
-
hoveredSpace == space.id ? Palette.running.opacity(0.3) :
|
|
199
|
-
hasWindow ? Palette.running.opacity(0.3) :
|
|
200
|
-
space.isCurrent ? Palette.borderLit : Palette.border,
|
|
201
|
-
lineWidth: 0.5
|
|
202
|
-
)
|
|
203
|
-
)
|
|
204
|
-
)
|
|
205
|
-
}
|
|
206
|
-
.buttonStyle(.plain)
|
|
207
|
-
.onHover { hoveredSpace = $0 ? space.id : nil }
|
|
208
|
-
}
|
|
209
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import CoreGraphics
|
|
2
|
-
import Darwin
|
|
3
|
-
|
|
4
|
-
enum WindowCapture {
|
|
5
|
-
// Transitional wrapper for the old CoreGraphics window snapshot API.
|
|
6
|
-
// macOS 26 rejects direct references because ScreenCaptureKit is the supported path;
|
|
7
|
-
// this can return nil if Apple removes the symbol, so preview/OCR callers must degrade.
|
|
8
|
-
private typealias CGWindowListCreateImageFn = @convention(c) (
|
|
9
|
-
CGRect,
|
|
10
|
-
CGWindowListOption,
|
|
11
|
-
CGWindowID,
|
|
12
|
-
CGWindowImageOption
|
|
13
|
-
) -> Unmanaged<CGImage>?
|
|
14
|
-
|
|
15
|
-
private static let createImage: CGWindowListCreateImageFn? = {
|
|
16
|
-
guard let handle = dlopen("/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics", RTLD_LAZY) else {
|
|
17
|
-
return nil
|
|
18
|
-
}
|
|
19
|
-
guard let symbol = dlsym(handle, "CGWindowListCreateImage") else {
|
|
20
|
-
return nil
|
|
21
|
-
}
|
|
22
|
-
return unsafeBitCast(symbol, to: CGWindowListCreateImageFn.self)
|
|
23
|
-
}()
|
|
24
|
-
|
|
25
|
-
static func image(
|
|
26
|
-
bounds: CGRect = .null,
|
|
27
|
-
listOption: CGWindowListOption,
|
|
28
|
-
windowID: CGWindowID,
|
|
29
|
-
imageOption: CGWindowImageOption
|
|
30
|
-
) -> CGImage? {
|
|
31
|
-
createImage?(bounds, listOption, windowID, imageOption)?.takeRetainedValue()
|
|
32
|
-
}
|
|
33
|
-
}
|
|
@@ -1,429 +0,0 @@
|
|
|
1
|
-
import AppKit
|
|
2
|
-
import CoreGraphics
|
|
3
|
-
|
|
4
|
-
final class WindowDragSnapController {
|
|
5
|
-
static let shared = WindowDragSnapController()
|
|
6
|
-
|
|
7
|
-
private struct DragWindowCandidate {
|
|
8
|
-
let pid: pid_t
|
|
9
|
-
let wid: UInt32?
|
|
10
|
-
let axWindow: AXUIElement
|
|
11
|
-
let initialAXFrame: CGRect
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
private struct ResolvedSnapZone {
|
|
15
|
-
let id: String
|
|
16
|
-
let label: String
|
|
17
|
-
let placement: PlacementSpec
|
|
18
|
-
let screen: NSScreen
|
|
19
|
-
let screenID: String
|
|
20
|
-
let triggerRect: CGRect
|
|
21
|
-
let visibleRect: CGRect
|
|
22
|
-
let previewRect: CGRect
|
|
23
|
-
let priority: Int
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
private struct DragSession {
|
|
27
|
-
let pid: pid_t
|
|
28
|
-
let wid: UInt32?
|
|
29
|
-
let zones: [ResolvedSnapZone]
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
private var mouseDownMonitor: Any?
|
|
33
|
-
private var mouseDragMonitor: Any?
|
|
34
|
-
private var mouseUpMonitor: Any?
|
|
35
|
-
private var flagsChangedMonitor: Any?
|
|
36
|
-
|
|
37
|
-
private var dragCandidate: DragWindowCandidate?
|
|
38
|
-
private var activeSession: DragSession?
|
|
39
|
-
private var modifierModeEnabled = false
|
|
40
|
-
private var windowHasMoved = false
|
|
41
|
-
|
|
42
|
-
private init() {}
|
|
43
|
-
|
|
44
|
-
func start() {
|
|
45
|
-
guard mouseDownMonitor == nil,
|
|
46
|
-
mouseDragMonitor == nil,
|
|
47
|
-
mouseUpMonitor == nil,
|
|
48
|
-
flagsChangedMonitor == nil else { return }
|
|
49
|
-
|
|
50
|
-
mouseDownMonitor = NSEvent.addGlobalMonitorForEvents(matching: .leftMouseDown) { [weak self] event in
|
|
51
|
-
self?.handleMouseDown(event)
|
|
52
|
-
}
|
|
53
|
-
mouseDragMonitor = NSEvent.addGlobalMonitorForEvents(matching: .leftMouseDragged) { [weak self] event in
|
|
54
|
-
self?.handleMouseDragged(event)
|
|
55
|
-
}
|
|
56
|
-
mouseUpMonitor = NSEvent.addGlobalMonitorForEvents(matching: .leftMouseUp) { [weak self] event in
|
|
57
|
-
self?.handleMouseUp(event)
|
|
58
|
-
}
|
|
59
|
-
flagsChangedMonitor = NSEvent.addGlobalMonitorForEvents(matching: .flagsChanged) { [weak self] event in
|
|
60
|
-
self?.handleFlagsChanged(event)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
DiagnosticLog.shared.info("WindowDragSnap: global drag monitors started")
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
func stop() {
|
|
67
|
-
if let monitor = mouseDownMonitor { NSEvent.removeMonitor(monitor) }
|
|
68
|
-
if let monitor = mouseDragMonitor { NSEvent.removeMonitor(monitor) }
|
|
69
|
-
if let monitor = mouseUpMonitor { NSEvent.removeMonitor(monitor) }
|
|
70
|
-
if let monitor = flagsChangedMonitor { NSEvent.removeMonitor(monitor) }
|
|
71
|
-
mouseDownMonitor = nil
|
|
72
|
-
mouseDragMonitor = nil
|
|
73
|
-
mouseUpMonitor = nil
|
|
74
|
-
flagsChangedMonitor = nil
|
|
75
|
-
clearTracking()
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
private func handleMouseDown(_ event: NSEvent) {
|
|
79
|
-
guard Preferences.shared.dragSnapEnabled else {
|
|
80
|
-
clearTracking()
|
|
81
|
-
return
|
|
82
|
-
}
|
|
83
|
-
guard PermissionChecker.shared.accessibility else {
|
|
84
|
-
clearTracking()
|
|
85
|
-
return
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
WorkspaceManager.shared.loadGridConfig()
|
|
89
|
-
modifierModeEnabled = Self.snapModifierPressed()
|
|
90
|
-
windowHasMoved = false
|
|
91
|
-
activeSession = nil
|
|
92
|
-
hideOverlays()
|
|
93
|
-
dragCandidate = captureFocusedWindow(at: NSEvent.mouseLocation)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
private func handleMouseDragged(_ event: NSEvent) {
|
|
97
|
-
guard Preferences.shared.dragSnapEnabled else {
|
|
98
|
-
clearTracking()
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
guard PermissionChecker.shared.accessibility else {
|
|
102
|
-
clearTracking()
|
|
103
|
-
return
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
guard let candidate = dragCandidate else { return }
|
|
107
|
-
modifierModeEnabled = Self.snapModifierPressed()
|
|
108
|
-
updateDragProgress(for: candidate)
|
|
109
|
-
updateSnapInteraction(at: NSEvent.mouseLocation)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
private func handleFlagsChanged(_ event: NSEvent) {
|
|
113
|
-
guard dragCandidate != nil else { return }
|
|
114
|
-
modifierModeEnabled = Self.snapModifierPressed()
|
|
115
|
-
updateSnapInteraction(at: NSEvent.mouseLocation)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
private func updateDragProgress(for candidate: DragWindowCandidate) {
|
|
119
|
-
guard let currentFrame = WindowTiler.readAXFrame(candidate.axWindow) else { return }
|
|
120
|
-
|
|
121
|
-
let moved = hypot(
|
|
122
|
-
currentFrame.origin.x - candidate.initialAXFrame.origin.x,
|
|
123
|
-
currentFrame.origin.y - candidate.initialAXFrame.origin.y
|
|
124
|
-
)
|
|
125
|
-
if moved >= 12 {
|
|
126
|
-
windowHasMoved = true
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
private func updateSnapInteraction(at mouseLocation: NSPoint) {
|
|
131
|
-
guard windowHasMoved else {
|
|
132
|
-
if activeSession != nil {
|
|
133
|
-
activeSession = nil
|
|
134
|
-
hideOverlays()
|
|
135
|
-
}
|
|
136
|
-
return
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
guard modifierModeEnabled else {
|
|
140
|
-
if activeSession != nil {
|
|
141
|
-
activeSession = nil
|
|
142
|
-
hideOverlays()
|
|
143
|
-
}
|
|
144
|
-
return
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
guard let candidate = dragCandidate else { return }
|
|
148
|
-
if activeSession == nil {
|
|
149
|
-
beginDragSession(with: candidate, mouseLocation: mouseLocation)
|
|
150
|
-
} else {
|
|
151
|
-
updateActiveSession(at: mouseLocation)
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
private func handleMouseUp(_ event: NSEvent) {
|
|
156
|
-
defer { clearTracking() }
|
|
157
|
-
modifierModeEnabled = Self.snapModifierPressed()
|
|
158
|
-
guard modifierModeEnabled, let activeSession else { return }
|
|
159
|
-
|
|
160
|
-
let mouseLocation = NSEvent.mouseLocation
|
|
161
|
-
guard let zone = bestZone(at: mouseLocation, in: activeSession.zones) else { return }
|
|
162
|
-
|
|
163
|
-
DiagnosticLog.shared.info("WindowDragSnap: drop → \(zone.label) (\(zone.id)) on \(zone.screen.localizedName)")
|
|
164
|
-
if let wid = activeSession.wid {
|
|
165
|
-
WindowTiler.tileWindowById(wid: wid, pid: activeSession.pid, to: zone.placement, on: zone.screen)
|
|
166
|
-
WindowTiler.highlightWindowById(wid: wid)
|
|
167
|
-
} else {
|
|
168
|
-
WindowTiler.tileFrontmostViaAX(to: zone.placement)
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
private func beginDragSession(with candidate: DragWindowCandidate, mouseLocation: NSPoint) {
|
|
173
|
-
WorkspaceManager.shared.loadGridConfig()
|
|
174
|
-
let config = WorkspaceManager.shared.snapZonesConfig
|
|
175
|
-
guard config.enabled ?? false else { return }
|
|
176
|
-
|
|
177
|
-
let zones = resolveZones(using: config)
|
|
178
|
-
guard !zones.isEmpty else { return }
|
|
179
|
-
|
|
180
|
-
activeSession = DragSession(
|
|
181
|
-
pid: candidate.pid,
|
|
182
|
-
wid: candidate.wid,
|
|
183
|
-
zones: zones
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
DiagnosticLog.shared.info("WindowDragSnap: tracking drag for pid=\(candidate.pid) wid=\(candidate.wid ?? 0)")
|
|
187
|
-
updateActiveSession(at: mouseLocation)
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
private func updateActiveSession(at mouseLocation: NSPoint) {
|
|
191
|
-
guard let activeSession else { return }
|
|
192
|
-
let hoveredZone = bestZone(at: mouseLocation, in: activeSession.zones)
|
|
193
|
-
render(zones: activeSession.zones, hoveredZone: hoveredZone)
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
private func clearTracking() {
|
|
197
|
-
dragCandidate = nil
|
|
198
|
-
activeSession = nil
|
|
199
|
-
modifierModeEnabled = false
|
|
200
|
-
windowHasMoved = false
|
|
201
|
-
hideOverlays()
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
private func hideOverlays() {
|
|
205
|
-
ScreenOverlayCanvasController.shared.removeLayers(owner: .dragSnap)
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
private func captureFocusedWindow(at mouseLocation: NSPoint) -> DragWindowCandidate? {
|
|
209
|
-
guard let frontApp = NSWorkspace.shared.frontmostApplication,
|
|
210
|
-
frontApp.bundleIdentifier != Bundle.main.bundleIdentifier else {
|
|
211
|
-
return nil
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
let appRef = AXUIElementCreateApplication(frontApp.processIdentifier)
|
|
215
|
-
var focusedRef: CFTypeRef?
|
|
216
|
-
guard AXUIElementCopyAttributeValue(appRef, kAXFocusedWindowAttribute as CFString, &focusedRef) == .success,
|
|
217
|
-
let focusedRef else {
|
|
218
|
-
return nil
|
|
219
|
-
}
|
|
220
|
-
let axWindow = focusedRef as! AXUIElement
|
|
221
|
-
guard let axFrame = WindowTiler.readAXFrame(axWindow) else { return nil }
|
|
222
|
-
|
|
223
|
-
let windowRect = Self.screenRect(fromAX: axFrame)
|
|
224
|
-
guard windowRect.insetBy(dx: -8, dy: -8).contains(mouseLocation) else {
|
|
225
|
-
return nil
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
var widValue: CGWindowID = 0
|
|
229
|
-
let wid = _AXUIElementGetWindow(axWindow, &widValue) == .success ? widValue : nil
|
|
230
|
-
|
|
231
|
-
return DragWindowCandidate(
|
|
232
|
-
pid: frontApp.processIdentifier,
|
|
233
|
-
wid: wid,
|
|
234
|
-
axWindow: axWindow,
|
|
235
|
-
initialAXFrame: axFrame
|
|
236
|
-
)
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
private func resolveZones(using config: SnapZonesConfig) -> [ResolvedSnapZone] {
|
|
240
|
-
let wm = WorkspaceManager.shared
|
|
241
|
-
let baseZones = (config.rules ?? []).compactMap { zone -> (SnapZoneDefinition, PlacementSpec, (CGFloat, CGFloat, CGFloat, CGFloat), Int)? in
|
|
242
|
-
let placement: PlacementSpec
|
|
243
|
-
switch zone.placement {
|
|
244
|
-
case .named(let name):
|
|
245
|
-
guard let resolved = wm.resolvePlacement(name) else {
|
|
246
|
-
DiagnosticLog.shared.warn("WindowDragSnap: ignoring snap zone \(zone.id) — unknown placement \(name)")
|
|
247
|
-
return nil
|
|
248
|
-
}
|
|
249
|
-
placement = resolved
|
|
250
|
-
case .fractions(let fractionalPlacement):
|
|
251
|
-
placement = .fractions(fractionalPlacement)
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
let triggerFractions: (CGFloat, CGFloat, CGFloat, CGFloat)
|
|
255
|
-
switch zone.trigger {
|
|
256
|
-
case .named(let name):
|
|
257
|
-
guard let triggerPlacement = wm.resolvePlacement(name) else {
|
|
258
|
-
DiagnosticLog.shared.warn("WindowDragSnap: ignoring snap zone \(zone.id) — unknown trigger \(name)")
|
|
259
|
-
return nil
|
|
260
|
-
}
|
|
261
|
-
triggerFractions = triggerPlacement.fractions
|
|
262
|
-
case .fractions(let placement):
|
|
263
|
-
triggerFractions = placement.fractions
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
return (zone, placement, triggerFractions, zone.priority ?? 0)
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
var resolved: [ResolvedSnapZone] = []
|
|
270
|
-
for screen in NSScreen.screens {
|
|
271
|
-
let screenID = ScreenOverlayCanvasController.screenID(for: screen)
|
|
272
|
-
for (zone, placement, triggerFractions, priority) in baseZones {
|
|
273
|
-
let triggerRect = Self.screenRect(for: triggerFractions, on: screen)
|
|
274
|
-
let previewRect = Self.screenRect(fromAX: WindowTiler.tileFrame(for: placement, on: screen))
|
|
275
|
-
let visibleRect = Self.visibleRect(forTriggerRect: triggerRect, previewRect: previewRect, on: screen)
|
|
276
|
-
resolved.append(
|
|
277
|
-
ResolvedSnapZone(
|
|
278
|
-
id: zone.id,
|
|
279
|
-
label: zone.label ?? zone.id,
|
|
280
|
-
placement: placement,
|
|
281
|
-
screen: screen,
|
|
282
|
-
screenID: screenID,
|
|
283
|
-
triggerRect: triggerRect,
|
|
284
|
-
visibleRect: visibleRect,
|
|
285
|
-
previewRect: previewRect,
|
|
286
|
-
priority: priority
|
|
287
|
-
)
|
|
288
|
-
)
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
return resolved.sorted {
|
|
293
|
-
if $0.priority != $1.priority {
|
|
294
|
-
return $0.priority > $1.priority
|
|
295
|
-
}
|
|
296
|
-
let leftArea = $0.triggerRect.width * $0.triggerRect.height
|
|
297
|
-
let rightArea = $1.triggerRect.width * $1.triggerRect.height
|
|
298
|
-
if leftArea != rightArea {
|
|
299
|
-
return leftArea < rightArea
|
|
300
|
-
}
|
|
301
|
-
return $0.id < $1.id
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
private func bestZone(at mouseLocation: NSPoint, in zones: [ResolvedSnapZone]) -> ResolvedSnapZone? {
|
|
306
|
-
zones.first(where: { $0.triggerRect.contains(mouseLocation) })
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
private func render(zones: [ResolvedSnapZone], hoveredZone: ResolvedSnapZone?) {
|
|
310
|
-
let config = WorkspaceManager.shared.snapZonesConfig
|
|
311
|
-
let grouped = Dictionary(grouping: zones, by: \.screenID)
|
|
312
|
-
var layers: [ScreenOverlayLayerSnapshot] = []
|
|
313
|
-
|
|
314
|
-
for screen in NSScreen.screens {
|
|
315
|
-
let screenID = ScreenOverlayCanvasController.screenID(for: screen)
|
|
316
|
-
guard let screenZones = grouped[screenID], !screenZones.isEmpty else { continue }
|
|
317
|
-
|
|
318
|
-
let localZones = screenZones.map {
|
|
319
|
-
ScreenOverlaySnapZone(
|
|
320
|
-
id: $0.id,
|
|
321
|
-
label: $0.label,
|
|
322
|
-
rect: $0.visibleRect.offsetBy(dx: -screen.frame.minX, dy: -screen.frame.minY),
|
|
323
|
-
isHovered: hoveredZone?.id == $0.id && hoveredZone?.screenID == screenID
|
|
324
|
-
)
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
let previewRect = hoveredZone?.screenID == screenID
|
|
328
|
-
? hoveredZone?.previewRect.offsetBy(dx: -screen.frame.minX, dy: -screen.frame.minY)
|
|
329
|
-
: nil
|
|
330
|
-
|
|
331
|
-
let payload = ScreenOverlaySnapZonesPayload(
|
|
332
|
-
zones: localZones,
|
|
333
|
-
previewRect: previewRect,
|
|
334
|
-
previewLabel: nil,
|
|
335
|
-
zoneOpacity: CGFloat(config.zoneOpacity ?? SnapZonesConfig.defaults.zoneOpacity ?? 0.10),
|
|
336
|
-
highlightOpacity: CGFloat(config.highlightOpacity ?? SnapZonesConfig.defaults.highlightOpacity ?? 0.22),
|
|
337
|
-
previewOpacity: CGFloat(config.previewOpacity ?? SnapZonesConfig.defaults.previewOpacity ?? 0.18),
|
|
338
|
-
cornerRadius: config.cornerRadius ?? SnapZonesConfig.defaults.cornerRadius ?? 18
|
|
339
|
-
)
|
|
340
|
-
|
|
341
|
-
layers.append(
|
|
342
|
-
ScreenOverlayLayerSnapshot(
|
|
343
|
-
id: ScreenOverlayLayerID("dragSnap.\(screenID)"),
|
|
344
|
-
owner: .dragSnap,
|
|
345
|
-
screen: .screen(id: screenID),
|
|
346
|
-
zIndex: 100,
|
|
347
|
-
opacity: 1,
|
|
348
|
-
payload: .snapZones(payload),
|
|
349
|
-
expiresAt: nil
|
|
350
|
-
)
|
|
351
|
-
)
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
ScreenOverlayCanvasController.shared.replaceLayers(owner: .dragSnap, with: layers)
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
private static func screenRect(for fractions: (CGFloat, CGFloat, CGFloat, CGFloat), on screen: NSScreen) -> CGRect {
|
|
358
|
-
let visible = screen.visibleFrame
|
|
359
|
-
let (fx, fy, fw, fh) = fractions
|
|
360
|
-
return CGRect(
|
|
361
|
-
x: visible.minX + visible.width * fx,
|
|
362
|
-
y: visible.maxY - visible.height * (fy + fh),
|
|
363
|
-
width: visible.width * fw,
|
|
364
|
-
height: visible.height * fh
|
|
365
|
-
)
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
private static func screenRect(fromAX rect: CGRect) -> CGRect {
|
|
369
|
-
let primaryHeight = NSScreen.screens.first?.frame.height ?? 0
|
|
370
|
-
return CGRect(
|
|
371
|
-
x: rect.origin.x,
|
|
372
|
-
y: primaryHeight - rect.origin.y - rect.height,
|
|
373
|
-
width: rect.width,
|
|
374
|
-
height: rect.height
|
|
375
|
-
)
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
private static func snapModifierPressed() -> Bool {
|
|
379
|
-
let flags = CGEventSource.flagsState(.combinedSessionState)
|
|
380
|
-
return flags.contains(Self.snapModifier().cgEventFlags)
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
private static func snapModifier() -> SnapModifierKey {
|
|
384
|
-
WorkspaceManager.shared.snapZonesConfig.modifier ?? .command
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
private static func visibleRect(forTriggerRect triggerRect: CGRect, previewRect: CGRect, on screen: NSScreen) -> CGRect {
|
|
388
|
-
let visible = screen.visibleFrame
|
|
389
|
-
let inset: CGFloat = 18
|
|
390
|
-
let nearLeft = abs(triggerRect.minX - visible.minX) < 8
|
|
391
|
-
let nearRight = abs(triggerRect.maxX - visible.maxX) < 8
|
|
392
|
-
let nearTop = abs(triggerRect.maxY - visible.maxY) < 8
|
|
393
|
-
let nearBottom = abs(triggerRect.minY - visible.minY) < 8
|
|
394
|
-
|
|
395
|
-
if (nearLeft || nearRight) && (nearTop || nearBottom) {
|
|
396
|
-
let width: CGFloat = 94
|
|
397
|
-
let height: CGFloat = 56
|
|
398
|
-
let x = nearLeft ? visible.minX + inset : visible.maxX - inset - width
|
|
399
|
-
let y = nearBottom ? visible.minY + inset : visible.maxY - inset - height
|
|
400
|
-
return CGRect(x: x, y: y, width: width, height: height)
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
if nearLeft || nearRight {
|
|
404
|
-
let width: CGFloat = 110
|
|
405
|
-
let height: CGFloat = 38
|
|
406
|
-
let x = nearLeft ? visible.minX + inset : visible.maxX - inset - width
|
|
407
|
-
let y = clamp(previewRect.midY - height / 2, min: visible.minY + 54, max: visible.maxY - 54 - height)
|
|
408
|
-
return CGRect(x: x, y: y, width: width, height: height)
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
if nearTop || nearBottom {
|
|
412
|
-
let width = min(max(triggerRect.width * 0.34, 132), 240)
|
|
413
|
-
let height: CGFloat = 38
|
|
414
|
-
let x = clamp(previewRect.midX - width / 2, min: visible.minX + 54, max: visible.maxX - 54 - width)
|
|
415
|
-
let y = nearBottom ? visible.minY + inset : visible.maxY - inset - height
|
|
416
|
-
return CGRect(x: x, y: y, width: width, height: height)
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
let width = min(max(previewRect.width * 0.28, 132), 220)
|
|
420
|
-
let height: CGFloat = 38
|
|
421
|
-
let x = clamp(previewRect.midX - width / 2, min: visible.minX + 54, max: visible.maxX - 54 - width)
|
|
422
|
-
let y = clamp(previewRect.maxY - height - 16, min: visible.minY + 40, max: visible.maxY - 40 - height)
|
|
423
|
-
return CGRect(x: x, y: y, width: width, height: height)
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
private static func clamp(_ value: CGFloat, min lower: CGFloat, max upper: CGFloat) -> CGFloat {
|
|
427
|
-
Swift.max(lower, Swift.min(upper, value))
|
|
428
|
-
}
|
|
429
|
-
}
|