@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
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
# LAT-004: Generative Overlay UI
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Approved.
|
|
6
|
+
|
|
7
|
+
This document proposes a general generative overlay UI system for Lattices. The goal is not to build a mascot-specific feature. The goal is to let agents and local systems generate structured interface requests that Lattices renders as small, stateful, movable, clickable, native desktop surfaces.
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
Lattices now has the beginning of a shared screen overlay canvas and an agent-facing overlay API. That is enough for passive visuals: labels, highlights, toasts, and simple pet-style sprites.
|
|
12
|
+
|
|
13
|
+
The next step is interaction.
|
|
14
|
+
|
|
15
|
+
Agents and local systems need a way to surface attention requests without opening a full app window. Examples include:
|
|
16
|
+
|
|
17
|
+
- an agent asking for feedback
|
|
18
|
+
- a permission request that needs approval
|
|
19
|
+
- a build, merge, or review result
|
|
20
|
+
- a useful link to a PR, thread, workspace, or file
|
|
21
|
+
- a reminder that a background process is waiting
|
|
22
|
+
- a lightweight status surface that can be moved out of the way
|
|
23
|
+
|
|
24
|
+
The proposed primitive is an **overlay actor**: a small persistent or transient object on the desktop that can move, animate, change state, show attached information, expose click targets, accept drag/drop, and emit interaction events back to the daemon.
|
|
25
|
+
|
|
26
|
+
The broader product frame is **Generative Overlay UI**:
|
|
27
|
+
|
|
28
|
+
- callers generate intent and schema
|
|
29
|
+
- Lattices owns rendering, style, hit-testing, permissions, safety, and interaction behavior
|
|
30
|
+
- the desktop receives native contextual UI instead of arbitrary injected web content
|
|
31
|
+
|
|
32
|
+
Ranger or any pet-like asset can be one renderer for this system. A logo, status chip, icon, or minimal badge should be equally valid renderers.
|
|
33
|
+
|
|
34
|
+
## Why Now
|
|
35
|
+
|
|
36
|
+
The shared overlay canvas gives Lattices a stable place to draw. The agent overlay API proves that outside processes can publish useful visual state.
|
|
37
|
+
|
|
38
|
+
The early pet experiments also exposed the missing pieces:
|
|
39
|
+
|
|
40
|
+
- WebSocket-driven position updates are too chunky for motion.
|
|
41
|
+
- Text needs crisp typography and carefully controlled translucent backing.
|
|
42
|
+
- Overlays must be easy to dismiss and should not linger accidentally.
|
|
43
|
+
- Some surfaces need real hit-testing, while the rest of the canvas must remain click-through.
|
|
44
|
+
- Agents need events when the user clicks, dismisses, drags, or chooses an action.
|
|
45
|
+
|
|
46
|
+
Those are general generative UI needs, not pet-specific needs.
|
|
47
|
+
|
|
48
|
+
## Goals
|
|
49
|
+
|
|
50
|
+
- Provide a reusable model for small interactive overlay actors.
|
|
51
|
+
- Keep the canvas click-through except where an actor or attached surface explicitly opts into input.
|
|
52
|
+
- Let the app own animation timing, easing, and sprite state.
|
|
53
|
+
- Let agents express intent instead of manually streaming frames or coordinates.
|
|
54
|
+
- Render agent-generated UI from safe structured schemas instead of arbitrary HTML.
|
|
55
|
+
- Support crisp, minimal information presentation with optional translucent text/card backing.
|
|
56
|
+
- Support hover, click, drag, dismiss, drop, menu, and action-button interactions.
|
|
57
|
+
- Emit daemon events for user interactions.
|
|
58
|
+
- Keep all attention surfaces dismissible, snoozable, or moveable.
|
|
59
|
+
- Make sound possible but controlled by app settings and priority.
|
|
60
|
+
|
|
61
|
+
## Non-Goals
|
|
62
|
+
|
|
63
|
+
This is not a replacement for the HUD, command palette, settings window, Screen Map, or menu bar app.
|
|
64
|
+
|
|
65
|
+
This is not a general webview or arbitrary HTML surface.
|
|
66
|
+
|
|
67
|
+
This is not a notification center clone. The system can present attention requests, but it should stay compact, contextual, and action-oriented.
|
|
68
|
+
|
|
69
|
+
This does not make every overlay interactive. Passive layers such as snap zones, gesture trails, and focus highlights should remain passive unless they have a clear reason to capture input.
|
|
70
|
+
|
|
71
|
+
## Conceptual Model
|
|
72
|
+
|
|
73
|
+
### Actor
|
|
74
|
+
|
|
75
|
+
An actor is the small visible object.
|
|
76
|
+
|
|
77
|
+
Examples:
|
|
78
|
+
|
|
79
|
+
- sprite asset
|
|
80
|
+
- app/service logo
|
|
81
|
+
- status chip
|
|
82
|
+
- progress puck
|
|
83
|
+
- minimal icon
|
|
84
|
+
|
|
85
|
+
An actor has:
|
|
86
|
+
|
|
87
|
+
- stable id
|
|
88
|
+
- renderer type
|
|
89
|
+
- asset id
|
|
90
|
+
- state
|
|
91
|
+
- position
|
|
92
|
+
- size or scale
|
|
93
|
+
- opacity
|
|
94
|
+
- optional pinned position
|
|
95
|
+
- input policy
|
|
96
|
+
- optional attached surface
|
|
97
|
+
|
|
98
|
+
### Surface
|
|
99
|
+
|
|
100
|
+
A surface is information attached to an actor.
|
|
101
|
+
|
|
102
|
+
Examples:
|
|
103
|
+
|
|
104
|
+
- one-line message
|
|
105
|
+
- compact card
|
|
106
|
+
- action prompt
|
|
107
|
+
- link preview
|
|
108
|
+
- progress status
|
|
109
|
+
- permission request
|
|
110
|
+
|
|
111
|
+
The surface should support:
|
|
112
|
+
|
|
113
|
+
- title
|
|
114
|
+
- body
|
|
115
|
+
- severity/priority
|
|
116
|
+
- links
|
|
117
|
+
- buttons
|
|
118
|
+
- dismiss/snooze affordance
|
|
119
|
+
- optional timeout
|
|
120
|
+
|
|
121
|
+
Surfaces should be visually precise: crisp text, restrained translucent backing, thin edges when needed, no heavy blurry panel chrome.
|
|
122
|
+
|
|
123
|
+
### Effect
|
|
124
|
+
|
|
125
|
+
An effect is visual or audio feedback that supports state.
|
|
126
|
+
|
|
127
|
+
Examples:
|
|
128
|
+
|
|
129
|
+
- hover lift/brightness
|
|
130
|
+
- pulse for waiting state
|
|
131
|
+
- success flash
|
|
132
|
+
- failure shake
|
|
133
|
+
- path trail during movement
|
|
134
|
+
- soft sound cue for attention
|
|
135
|
+
|
|
136
|
+
Effects should be optional and bounded. They should never become the core interaction contract.
|
|
137
|
+
|
|
138
|
+
## Actor State
|
|
139
|
+
|
|
140
|
+
The system should define common states while allowing renderer-specific mapping.
|
|
141
|
+
|
|
142
|
+
Initial common states:
|
|
143
|
+
|
|
144
|
+
- `idle`
|
|
145
|
+
- `active`
|
|
146
|
+
- `moving`
|
|
147
|
+
- `waiting`
|
|
148
|
+
- `thinking`
|
|
149
|
+
- `success`
|
|
150
|
+
- `warning`
|
|
151
|
+
- `failed`
|
|
152
|
+
- `review`
|
|
153
|
+
- `muted`
|
|
154
|
+
|
|
155
|
+
Renderers can map these states to their own animation names. For example, a sprite renderer might map:
|
|
156
|
+
|
|
157
|
+
- `movingRight` -> `run_right`
|
|
158
|
+
- `movingLeft` -> `run_left`
|
|
159
|
+
- `waiting` -> `waiting`
|
|
160
|
+
- `failed` -> `failed`
|
|
161
|
+
|
|
162
|
+
The API should not require callers to know sprite rows or frame sizes.
|
|
163
|
+
|
|
164
|
+
## Interaction Model
|
|
165
|
+
|
|
166
|
+
Actors should support explicit input capabilities:
|
|
167
|
+
|
|
168
|
+
- `hoverable`
|
|
169
|
+
- `clickable`
|
|
170
|
+
- `draggable`
|
|
171
|
+
- `droppable`
|
|
172
|
+
- `dismissible`
|
|
173
|
+
- `menuEnabled`
|
|
174
|
+
|
|
175
|
+
The default should be conservative:
|
|
176
|
+
|
|
177
|
+
- the canvas remains click-through
|
|
178
|
+
- only actor bounds and attached surface/button bounds capture input
|
|
179
|
+
- input capture should never cover the whole screen unless a modal feature explicitly owns that surface
|
|
180
|
+
|
|
181
|
+
Expected interactions:
|
|
182
|
+
|
|
183
|
+
- hover: brighten, lift, preview, or emit event
|
|
184
|
+
- click: primary action, open compact surface, or emit event
|
|
185
|
+
- double-click: optional secondary action
|
|
186
|
+
- drag: move actor and optionally pin final position
|
|
187
|
+
- right-click: close that actor without clearing other actors
|
|
188
|
+
- drop: emit dropped item payload if supported
|
|
189
|
+
- Escape: dismiss the active surface
|
|
190
|
+
- click-away: dismiss transient surfaces when appropriate
|
|
191
|
+
- context menu: mute, snooze, hide, settings, inspect source
|
|
192
|
+
|
|
193
|
+
Dismissal should normally dismiss the current message/surface, not delete the actor.
|
|
194
|
+
Persistent actors can be parked globally with the overlay actor hotkey
|
|
195
|
+
without clearing their ids or state. The initial shortcut is **Hyper+B**.
|
|
196
|
+
|
|
197
|
+
## Motion
|
|
198
|
+
|
|
199
|
+
Motion should be renderer-owned, not API-spammed.
|
|
200
|
+
|
|
201
|
+
Callers should be able to say:
|
|
202
|
+
|
|
203
|
+
```json
|
|
204
|
+
{
|
|
205
|
+
"id": "scout-ranger",
|
|
206
|
+
"position": { "x": 640, "y": 320 },
|
|
207
|
+
"durationMs": 700,
|
|
208
|
+
"easing": "spring"
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The app should:
|
|
213
|
+
|
|
214
|
+
- keep current position
|
|
215
|
+
- interpolate toward target position
|
|
216
|
+
- draw at display cadence
|
|
217
|
+
- update directional state during movement
|
|
218
|
+
- settle into the target state when complete
|
|
219
|
+
|
|
220
|
+
Initial easing modes:
|
|
221
|
+
|
|
222
|
+
- `linear`
|
|
223
|
+
- `easeInOut`
|
|
224
|
+
- `spring`
|
|
225
|
+
|
|
226
|
+
Path support can come later:
|
|
227
|
+
|
|
228
|
+
```json
|
|
229
|
+
{
|
|
230
|
+
"id": "scout-ranger",
|
|
231
|
+
"path": [
|
|
232
|
+
{ "x": 320, "y": 180 },
|
|
233
|
+
{ "x": 540, "y": 230 },
|
|
234
|
+
{ "x": 760, "y": 190 }
|
|
235
|
+
],
|
|
236
|
+
"durationMs": 1400,
|
|
237
|
+
"easing": "spring"
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## API Sketch
|
|
242
|
+
|
|
243
|
+
### Create Or Update Actor
|
|
244
|
+
|
|
245
|
+
```json
|
|
246
|
+
{
|
|
247
|
+
"method": "overlay.actor.publish",
|
|
248
|
+
"params": {
|
|
249
|
+
"id": "scout-ranger",
|
|
250
|
+
"renderer": "sprite",
|
|
251
|
+
"asset": "scout-ranger",
|
|
252
|
+
"state": "idle",
|
|
253
|
+
"position": {
|
|
254
|
+
"anchor": "bottomRight",
|
|
255
|
+
"x": -40,
|
|
256
|
+
"y": -40
|
|
257
|
+
},
|
|
258
|
+
"draggable": true,
|
|
259
|
+
"clickable": true,
|
|
260
|
+
"dismissible": true
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Set State
|
|
266
|
+
|
|
267
|
+
```json
|
|
268
|
+
{
|
|
269
|
+
"method": "overlay.actor.setState",
|
|
270
|
+
"params": {
|
|
271
|
+
"id": "scout-ranger",
|
|
272
|
+
"state": "waiting",
|
|
273
|
+
"message": {
|
|
274
|
+
"title": "Agent needs feedback",
|
|
275
|
+
"body": "Review the proposed merge?",
|
|
276
|
+
"priority": "decision",
|
|
277
|
+
"actions": [
|
|
278
|
+
{ "id": "approve", "label": "Approve", "style": "primary" },
|
|
279
|
+
{ "id": "open", "label": "Open PR", "url": "https://github.com/arach/lattices/pull/31" },
|
|
280
|
+
{ "id": "dismiss", "label": "Dismiss" }
|
|
281
|
+
]
|
|
282
|
+
},
|
|
283
|
+
"sound": "attention-soft"
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Move Actor
|
|
289
|
+
|
|
290
|
+
```json
|
|
291
|
+
{
|
|
292
|
+
"method": "overlay.actor.moveTo",
|
|
293
|
+
"params": {
|
|
294
|
+
"id": "scout-ranger",
|
|
295
|
+
"position": { "x": 640, "y": 320 },
|
|
296
|
+
"durationMs": 700,
|
|
297
|
+
"easing": "spring"
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Clear Actor Or Surface
|
|
303
|
+
|
|
304
|
+
```json
|
|
305
|
+
{
|
|
306
|
+
"method": "overlay.actor.clear",
|
|
307
|
+
"params": {
|
|
308
|
+
"id": "scout-ranger",
|
|
309
|
+
"surfaceOnly": true
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Subscribe To Events
|
|
315
|
+
|
|
316
|
+
The daemon should emit events such as:
|
|
317
|
+
|
|
318
|
+
- `overlay.actor.hovered`
|
|
319
|
+
- `overlay.actor.clicked`
|
|
320
|
+
- `overlay.actor.doubleClicked`
|
|
321
|
+
- `overlay.actor.dragStarted`
|
|
322
|
+
- `overlay.actor.moved`
|
|
323
|
+
- `overlay.actor.dropped`
|
|
324
|
+
- `overlay.actor.dismissed`
|
|
325
|
+
- `overlay.actor.actionSelected`
|
|
326
|
+
- `overlay.actor.snoozed`
|
|
327
|
+
|
|
328
|
+
Example event:
|
|
329
|
+
|
|
330
|
+
```json
|
|
331
|
+
{
|
|
332
|
+
"event": "overlay.actor.actionSelected",
|
|
333
|
+
"data": {
|
|
334
|
+
"actorId": "scout-ranger",
|
|
335
|
+
"surfaceId": "ask-123",
|
|
336
|
+
"actionId": "approve"
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Visual Rules
|
|
342
|
+
|
|
343
|
+
The visual system should stay quiet and exact.
|
|
344
|
+
|
|
345
|
+
- Prefer actor plus attached text over large panels.
|
|
346
|
+
- Use translucent backing only when it improves readability.
|
|
347
|
+
- Keep text crisp: system fonts, no excessive blur, no heavy shadows.
|
|
348
|
+
- Use thin strokes and hairlines when edges are needed.
|
|
349
|
+
- Avoid decorative opacity stacks that make the surface feel fuzzy.
|
|
350
|
+
- Avoid sticky surfaces unless the request truly needs a decision.
|
|
351
|
+
- Make hover/click affordances legible without shouting.
|
|
352
|
+
|
|
353
|
+
For text next to an actor, the baseline style should be:
|
|
354
|
+
|
|
355
|
+
- white text with high alpha
|
|
356
|
+
- subtle black text halo or small shadow
|
|
357
|
+
- optional dark wash at low alpha
|
|
358
|
+
- thin light edge if needed
|
|
359
|
+
- no large rounded card unless actions require it
|
|
360
|
+
|
|
361
|
+
## Sound
|
|
362
|
+
|
|
363
|
+
Sound should be event-driven and user-controlled.
|
|
364
|
+
|
|
365
|
+
Initial cue types:
|
|
366
|
+
|
|
367
|
+
- `none`
|
|
368
|
+
- `attention-soft`
|
|
369
|
+
- `success-soft`
|
|
370
|
+
- `warning-soft`
|
|
371
|
+
- `failure-soft`
|
|
372
|
+
|
|
373
|
+
Rules:
|
|
374
|
+
|
|
375
|
+
- sound should default to quiet or disabled depending app settings
|
|
376
|
+
- priority should gate whether sound is allowed
|
|
377
|
+
- repeated sounds should be rate-limited
|
|
378
|
+
- dragging/hovering should not spam sounds
|
|
379
|
+
- all sounds should be suppressible globally and per actor/source
|
|
380
|
+
|
|
381
|
+
## Architecture
|
|
382
|
+
|
|
383
|
+
Proposed components:
|
|
384
|
+
|
|
385
|
+
### `OverlayActorStore`
|
|
386
|
+
|
|
387
|
+
Owns actor state:
|
|
388
|
+
|
|
389
|
+
- actors by id
|
|
390
|
+
- current position
|
|
391
|
+
- target position
|
|
392
|
+
- current state
|
|
393
|
+
- attached surface
|
|
394
|
+
- interaction policy
|
|
395
|
+
- pin/mute/snooze metadata
|
|
396
|
+
|
|
397
|
+
### `OverlayActorAnimator`
|
|
398
|
+
|
|
399
|
+
Owns time-based interpolation:
|
|
400
|
+
|
|
401
|
+
- active motion records
|
|
402
|
+
- easing functions
|
|
403
|
+
- display-link or timer updates
|
|
404
|
+
- direction/state transitions during movement
|
|
405
|
+
|
|
406
|
+
### `OverlayActorRenderer`
|
|
407
|
+
|
|
408
|
+
Draws actors and surfaces into the shared overlay canvas.
|
|
409
|
+
|
|
410
|
+
Renderer variants can include:
|
|
411
|
+
|
|
412
|
+
- sprite renderer
|
|
413
|
+
- logo renderer
|
|
414
|
+
- chip renderer
|
|
415
|
+
- progress renderer
|
|
416
|
+
|
|
417
|
+
### `OverlayActorHitTester`
|
|
418
|
+
|
|
419
|
+
Tracks interactive regions:
|
|
420
|
+
|
|
421
|
+
- actor bounds
|
|
422
|
+
- attached card bounds
|
|
423
|
+
- action button bounds
|
|
424
|
+
- drag handles if needed
|
|
425
|
+
|
|
426
|
+
This component decides whether the overlay window should accept input at a given point.
|
|
427
|
+
|
|
428
|
+
### `OverlayActorInteractionController`
|
|
429
|
+
|
|
430
|
+
Handles:
|
|
431
|
+
|
|
432
|
+
- hover state
|
|
433
|
+
- click/double-click
|
|
434
|
+
- drag/drop
|
|
435
|
+
- dismissal
|
|
436
|
+
- context menu
|
|
437
|
+
- event emission
|
|
438
|
+
|
|
439
|
+
### `OverlayActorApi`
|
|
440
|
+
|
|
441
|
+
Adds daemon methods and schemas:
|
|
442
|
+
|
|
443
|
+
- `overlay.actor.publish`
|
|
444
|
+
- `overlay.actor.setState`
|
|
445
|
+
- `overlay.actor.moveTo`
|
|
446
|
+
- `overlay.actor.clear`
|
|
447
|
+
- `overlay.actor.list`
|
|
448
|
+
|
|
449
|
+
## Relationship To `ScreenOverlayCanvas`
|
|
450
|
+
|
|
451
|
+
`ScreenOverlayCanvasController` should remain the rendering substrate.
|
|
452
|
+
|
|
453
|
+
Interactive actors may require the overlay window to stop ignoring mouse events in a very controlled way. The preferred approach is:
|
|
454
|
+
|
|
455
|
+
- keep the panel non-activating
|
|
456
|
+
- override hit-testing so only registered actor/surface regions return a view
|
|
457
|
+
- return `nil` for all other points so normal desktop clicks pass through
|
|
458
|
+
- avoid making the app active for simple hover/click where possible
|
|
459
|
+
|
|
460
|
+
The shared canvas can continue supporting passive layers alongside interactive actors.
|
|
461
|
+
|
|
462
|
+
## Relationship To Existing Overlay API
|
|
463
|
+
|
|
464
|
+
The current `overlay.publish` API is still useful for simple passive layers:
|
|
465
|
+
|
|
466
|
+
- `toast`
|
|
467
|
+
- `label`
|
|
468
|
+
- `highlight`
|
|
469
|
+
- simple `pet`
|
|
470
|
+
|
|
471
|
+
The actor API should be added next to it rather than replacing it immediately.
|
|
472
|
+
|
|
473
|
+
Over time, `kind: "pet"` can become a compatibility wrapper around `overlay.actor.publish` for sprite actors.
|
|
474
|
+
|
|
475
|
+
## Implementation Plan
|
|
476
|
+
|
|
477
|
+
### Phase 1: Actor State And Rendering
|
|
478
|
+
|
|
479
|
+
- Add actor model types.
|
|
480
|
+
- Add actor payload to the screen overlay renderer.
|
|
481
|
+
- Support sprite assets, state mapping, and attached text wash.
|
|
482
|
+
- Add `overlay.actor.publish`.
|
|
483
|
+
- Keep interaction disabled except dismiss-on-click-away.
|
|
484
|
+
|
|
485
|
+
### Phase 2: Smooth Motion
|
|
486
|
+
|
|
487
|
+
- Add current/target position state.
|
|
488
|
+
- Add display-cadence animation timer.
|
|
489
|
+
- Add `overlay.actor.moveTo`.
|
|
490
|
+
- Map direction to movement animation state where assets support it.
|
|
491
|
+
|
|
492
|
+
### Phase 3: Hit-Testing And Drag
|
|
493
|
+
|
|
494
|
+
- Make the overlay window selectively interactive.
|
|
495
|
+
- Hit-test only actor and attached surface bounds.
|
|
496
|
+
- Support hover state.
|
|
497
|
+
- Support dragging to move/pin.
|
|
498
|
+
- Emit `overlay.actor.moved`.
|
|
499
|
+
|
|
500
|
+
### Phase 4: Action Surfaces
|
|
501
|
+
|
|
502
|
+
- Add compact action cards.
|
|
503
|
+
- Support links and action buttons.
|
|
504
|
+
- Emit `overlay.actor.actionSelected`.
|
|
505
|
+
- Add dismiss and snooze behaviors.
|
|
506
|
+
|
|
507
|
+
### Phase 5: Sound And Settings
|
|
508
|
+
|
|
509
|
+
- Add sound cue names.
|
|
510
|
+
- Add global overlay sound setting.
|
|
511
|
+
- Add rate limits.
|
|
512
|
+
- Add mute/snooze per source or actor.
|
|
513
|
+
|
|
514
|
+
## Open Questions
|
|
515
|
+
|
|
516
|
+
- Should actors be global across Spaces, per Space, or configurable per actor?
|
|
517
|
+
- Should pinned actor positions be saved per display, per Space, or globally?
|
|
518
|
+
- What is the right default anchor for a persistent actor?
|
|
519
|
+
- Should action cards use AppKit drawing or a small SwiftUI-hosted island?
|
|
520
|
+
- How much interaction can a non-activating panel handle without surprising focus behavior?
|
|
521
|
+
- Should daemon events be broadcast to all clients or routed back to the caller that created the actor?
|
|
522
|
+
- How should drops be represented when users drag files, URLs, or text onto an actor?
|
|
523
|
+
|
|
524
|
+
## First Useful Slice
|
|
525
|
+
|
|
526
|
+
The first useful implementation should be small:
|
|
527
|
+
|
|
528
|
+
1. Add `overlay.actor.publish` for a sprite actor with state and attached message.
|
|
529
|
+
2. Add smooth `overlay.actor.moveTo` with app-owned easing.
|
|
530
|
+
3. Add selective hit-testing for hover and drag.
|
|
531
|
+
4. Emit `overlay.actor.moved` and `overlay.actor.clicked`.
|
|
532
|
+
5. Keep action cards and sound for the next slice.
|
|
533
|
+
|
|
534
|
+
That would make the system immediately feel different from passive overlays while keeping the surface area manageable.
|