@grida/hud 0.1.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/LICENSE +201 -0
- package/README.md +340 -0
- package/dist/index-CBqCh-ZM.d.mts +734 -0
- package/dist/index-DRBeSiI2.d.ts +734 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +11 -0
- package/dist/index.mjs +2 -0
- package/dist/react.d.mts +73 -0
- package/dist/react.d.ts +73 -0
- package/dist/react.js +124 -0
- package/dist/react.mjs +117 -0
- package/dist/surface-CNlBaEXn.js +1890 -0
- package/dist/surface-hUEeEVdL.mjs +1808 -0
- package/package.json +68 -0
|
@@ -0,0 +1,734 @@
|
|
|
1
|
+
import cmath from "@grida/cmath";
|
|
2
|
+
import { guide } from "@grida/cmath/_snap";
|
|
3
|
+
import { Measurement } from "@grida/cmath/_measurement";
|
|
4
|
+
|
|
5
|
+
//#region primitives/types.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* A line segment in document space, extending `cmath.ui.Line` with
|
|
8
|
+
* an optional `dashed` style.
|
|
9
|
+
*/
|
|
10
|
+
interface HUDLine extends cmath.ui.Line {
|
|
11
|
+
dashed?: boolean;
|
|
12
|
+
/** Stroke width in screen-space CSS px. Defaults to the canvas default. */
|
|
13
|
+
strokeWidth?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Override the canvas color for this line's stroke and label pill. Falls
|
|
16
|
+
* back to the canvas's current color when absent.
|
|
17
|
+
*/
|
|
18
|
+
color?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A full-viewport axis-aligned line (infinite extent) at a given offset.
|
|
22
|
+
*
|
|
23
|
+
* `axis` indicates which axis the `offset` lives on:
|
|
24
|
+
* - `"x"` → vertical line at x=offset
|
|
25
|
+
* - `"y"` → horizontal line at y=offset
|
|
26
|
+
*
|
|
27
|
+
* Offset is in document space; the renderer projects it to screen.
|
|
28
|
+
*/
|
|
29
|
+
interface HUDRule {
|
|
30
|
+
axis: "x" | "y";
|
|
31
|
+
offset: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A rectangle in document space.
|
|
35
|
+
*
|
|
36
|
+
* Stroke uses the canvas color by default. Fill is opt-in.
|
|
37
|
+
*/
|
|
38
|
+
interface HUDRect {
|
|
39
|
+
x: number;
|
|
40
|
+
y: number;
|
|
41
|
+
width: number;
|
|
42
|
+
height: number;
|
|
43
|
+
/** Whether to stroke the outline (default: true). */
|
|
44
|
+
stroke?: boolean;
|
|
45
|
+
/** Whether to fill the interior (default: false). */
|
|
46
|
+
fill?: boolean;
|
|
47
|
+
/** Opacity of the fill color, 0–1 (default: 1). Ignored when `fill` is falsy. */
|
|
48
|
+
fillOpacity?: number;
|
|
49
|
+
/** Draw the outline with a dash pattern. */
|
|
50
|
+
dashed?: boolean;
|
|
51
|
+
/** Stroke width in screen-space CSS px. Defaults to the canvas default. */
|
|
52
|
+
strokeWidth?: number;
|
|
53
|
+
/**
|
|
54
|
+
* Override the canvas color for this rect's stroke and fill. Falls back
|
|
55
|
+
* to the canvas's current color when absent.
|
|
56
|
+
*/
|
|
57
|
+
color?: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* A polyline (or closed polygon) in document space.
|
|
61
|
+
*
|
|
62
|
+
* Stroke uses the canvas color by default. Fill is opt-in.
|
|
63
|
+
*/
|
|
64
|
+
interface HUDPolyline {
|
|
65
|
+
points: cmath.Vector2[];
|
|
66
|
+
/** Whether to stroke the path (default: true). */
|
|
67
|
+
stroke?: boolean;
|
|
68
|
+
/** Whether to fill the interior (default: false). */
|
|
69
|
+
fill?: boolean;
|
|
70
|
+
/** Opacity of the fill color, 0–1 (default: 1). Ignored when `fill` is falsy. */
|
|
71
|
+
fillOpacity?: number;
|
|
72
|
+
/** Draw the stroke with a dash pattern. */
|
|
73
|
+
dashed?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Override the canvas color for this polyline's stroke and fill. Falls
|
|
76
|
+
* back to the canvas's current color when absent.
|
|
77
|
+
*/
|
|
78
|
+
color?: string;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* A rectangle whose **size is fixed in screen-space** but whose **anchor lives
|
|
82
|
+
* in document space**. Used for handles and other chrome that must not scale
|
|
83
|
+
* with viewport zoom.
|
|
84
|
+
*
|
|
85
|
+
* The anchor `(x, y)` is the document-space point that maps to one corner (or
|
|
86
|
+
* center) of the resulting screen-space rect — controlled by `anchor`.
|
|
87
|
+
*
|
|
88
|
+
* Default anchor is `"center"` (the doc-space point becomes the rect's center).
|
|
89
|
+
*/
|
|
90
|
+
interface HUDScreenRect {
|
|
91
|
+
/** Document-space anchor point. */
|
|
92
|
+
x: number;
|
|
93
|
+
y: number;
|
|
94
|
+
/** Screen-space size in CSS px. */
|
|
95
|
+
width: number;
|
|
96
|
+
height: number;
|
|
97
|
+
/** Which point of the rect sits at (x, y). Default: "center". */
|
|
98
|
+
anchor?: "center" | "tl" | "tr" | "bl" | "br";
|
|
99
|
+
fill?: boolean;
|
|
100
|
+
stroke?: boolean;
|
|
101
|
+
/** Override the canvas color for fill. */
|
|
102
|
+
fillColor?: string;
|
|
103
|
+
/** Override the canvas color for stroke. */
|
|
104
|
+
strokeColor?: string;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* A complete set of draw commands for one frame.
|
|
108
|
+
*
|
|
109
|
+
* The HUD clears the canvas and draws everything in this struct each frame.
|
|
110
|
+
* Callers build a `HUDDraw` from their domain data (snap result, measurement,
|
|
111
|
+
* guide state, etc.) and hand it to `HUDCanvas.draw()`.
|
|
112
|
+
*/
|
|
113
|
+
interface HUDDraw {
|
|
114
|
+
lines?: HUDLine[];
|
|
115
|
+
rules?: HUDRule[];
|
|
116
|
+
points?: cmath.Vector2[];
|
|
117
|
+
rects?: HUDRect[];
|
|
118
|
+
polylines?: HUDPolyline[];
|
|
119
|
+
/** Screen-space-sized rects anchored to document-space points (handles). */
|
|
120
|
+
screenRects?: HUDScreenRect[];
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region primitives/canvas.d.ts
|
|
124
|
+
interface HUDCanvasOptions {
|
|
125
|
+
color?: string;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Imperative Canvas 2D renderer for the HUD overlay.
|
|
129
|
+
*
|
|
130
|
+
* Owns a single `<canvas>` element and draws {@link HUDDraw} command lists
|
|
131
|
+
* each frame. All drawing is immediate-mode: the canvas is cleared and
|
|
132
|
+
* fully redrawn on every `draw()` call.
|
|
133
|
+
*
|
|
134
|
+
* The viewport transform is assumed to be axis-aligned (scale + translate only,
|
|
135
|
+
* no rotation/shear). The off-diagonal components of the transform matrix are
|
|
136
|
+
* ignored.
|
|
137
|
+
*/
|
|
138
|
+
declare class HUDCanvas {
|
|
139
|
+
private canvas;
|
|
140
|
+
private ctx;
|
|
141
|
+
private dpr;
|
|
142
|
+
private transform;
|
|
143
|
+
private color;
|
|
144
|
+
private width;
|
|
145
|
+
private height;
|
|
146
|
+
constructor(canvas: HTMLCanvasElement, options?: HUDCanvasOptions);
|
|
147
|
+
setColor(color?: string): void;
|
|
148
|
+
setSize(w: number, h: number): void;
|
|
149
|
+
setTransform(transform: cmath.Transform): void;
|
|
150
|
+
/**
|
|
151
|
+
* Clear the canvas and draw all primitives in `commands`.
|
|
152
|
+
* Pass `undefined` to clear without drawing (e.g. when no overlay is active).
|
|
153
|
+
*/
|
|
154
|
+
draw(commands: HUDDraw | undefined): void;
|
|
155
|
+
private applyViewTransform;
|
|
156
|
+
private applyScreenTransform;
|
|
157
|
+
/** Project a scalar offset on `axis` to screen-space. */
|
|
158
|
+
private deltaToScreen;
|
|
159
|
+
private drawRules;
|
|
160
|
+
private drawLines;
|
|
161
|
+
private drawRects;
|
|
162
|
+
private drawPolylines;
|
|
163
|
+
private drawPoints;
|
|
164
|
+
/**
|
|
165
|
+
* Draw rects whose **size is in screen-space** but whose **anchor is in
|
|
166
|
+
* document-space**. The doc-space point is projected via the current
|
|
167
|
+
* transform; the rect is then drawn at fixed CSS-pixel dimensions.
|
|
168
|
+
*
|
|
169
|
+
* This is the primitive used to draw resize / rotate handles — they must
|
|
170
|
+
* remain a constant visual size regardless of viewport zoom.
|
|
171
|
+
*/
|
|
172
|
+
private drawScreenRects;
|
|
173
|
+
}
|
|
174
|
+
//#endregion
|
|
175
|
+
//#region primitives/snap-guide.d.ts
|
|
176
|
+
/**
|
|
177
|
+
* Convert a `guide.SnapGuide` (the output of `guide.plot()`) into a
|
|
178
|
+
* generic {@link HUDDraw} command list.
|
|
179
|
+
*
|
|
180
|
+
* Lines pass through directly (HUDLine extends cmath.ui.Line).
|
|
181
|
+
* Points pass through directly (both are cmath.Vector2).
|
|
182
|
+
* Rules are destructured from tuples to objects.
|
|
183
|
+
*/
|
|
184
|
+
declare function snapGuideToHUDDraw(sg: guide.SnapGuide | undefined): HUDDraw | undefined;
|
|
185
|
+
//#endregion
|
|
186
|
+
//#region primitives/measurement-guide.d.ts
|
|
187
|
+
/**
|
|
188
|
+
* Convert a {@link Measurement} (the output of `measure()`) into a
|
|
189
|
+
* generic {@link HUDDraw} command list.
|
|
190
|
+
*
|
|
191
|
+
* All coordinates are in **document space** — the HUD canvas applies
|
|
192
|
+
* the viewport transform.
|
|
193
|
+
*
|
|
194
|
+
* Produces:
|
|
195
|
+
* - Two stroke-only rects for the A and B bounding boxes
|
|
196
|
+
* - One labelled guide line per non-zero distance (solid)
|
|
197
|
+
* - One auxiliary line per non-zero side connecting the guide to B (dashed)
|
|
198
|
+
*
|
|
199
|
+
* If `color` is provided, every emitted line and rect carries that color so
|
|
200
|
+
* the guides render distinctly from the canvas's chrome color. When used via
|
|
201
|
+
* `surface.draw(extra)` (the host-fed-extras channel) this is required to
|
|
202
|
+
* separate measurement from selection chrome on a shared canvas.
|
|
203
|
+
*/
|
|
204
|
+
declare function measurementToHUDDraw(m: Measurement, color?: string): HUDDraw;
|
|
205
|
+
//#endregion
|
|
206
|
+
//#region primitives/marquee.d.ts
|
|
207
|
+
/**
|
|
208
|
+
* Convert two marquee corner points into a {@link HUDDraw} command list.
|
|
209
|
+
*
|
|
210
|
+
* All coordinates are in **document space**.
|
|
211
|
+
*
|
|
212
|
+
* Produces a single rectangle with a stroke outline and a semi-transparent fill.
|
|
213
|
+
*/
|
|
214
|
+
declare function marqueeToHUDDraw(a: cmath.Vector2, b: cmath.Vector2): HUDDraw;
|
|
215
|
+
//#endregion
|
|
216
|
+
//#region primitives/lasso.d.ts
|
|
217
|
+
/**
|
|
218
|
+
* Convert a lasso point sequence into a {@link HUDDraw} command list.
|
|
219
|
+
*
|
|
220
|
+
* All coordinates are in **document space**.
|
|
221
|
+
*
|
|
222
|
+
* Produces a single polyline with a dashed stroke and a semi-transparent fill.
|
|
223
|
+
*/
|
|
224
|
+
declare function lassoToHUDDraw(points: cmath.Vector2[]): HUDDraw | undefined;
|
|
225
|
+
//#endregion
|
|
226
|
+
//#region event/event.d.ts
|
|
227
|
+
/** Modifier-key snapshot at the moment an event was produced. */
|
|
228
|
+
interface Modifiers {
|
|
229
|
+
shift: boolean;
|
|
230
|
+
alt: boolean;
|
|
231
|
+
meta: boolean;
|
|
232
|
+
ctrl: boolean;
|
|
233
|
+
}
|
|
234
|
+
declare const NO_MODS: Modifiers;
|
|
235
|
+
type PointerButton = "primary" | "secondary" | "middle";
|
|
236
|
+
/**
|
|
237
|
+
* Input event consumed by `Surface.dispatch`.
|
|
238
|
+
*
|
|
239
|
+
* All coordinates are **screen-space CSS pixels relative to the canvas**.
|
|
240
|
+
* The surface owns the camera and converts to document-space internally.
|
|
241
|
+
*/
|
|
242
|
+
type SurfaceEvent = {
|
|
243
|
+
kind: "pointer_move";
|
|
244
|
+
x: number;
|
|
245
|
+
y: number;
|
|
246
|
+
mods: Modifiers;
|
|
247
|
+
} | {
|
|
248
|
+
kind: "pointer_down";
|
|
249
|
+
x: number;
|
|
250
|
+
y: number;
|
|
251
|
+
button: PointerButton;
|
|
252
|
+
mods: Modifiers;
|
|
253
|
+
} | {
|
|
254
|
+
kind: "pointer_up";
|
|
255
|
+
x: number;
|
|
256
|
+
y: number;
|
|
257
|
+
button: PointerButton;
|
|
258
|
+
mods: Modifiers;
|
|
259
|
+
} | {
|
|
260
|
+
kind: "modifiers";
|
|
261
|
+
mods: Modifiers;
|
|
262
|
+
} | {
|
|
263
|
+
kind: "wheel";
|
|
264
|
+
x: number;
|
|
265
|
+
y: number;
|
|
266
|
+
dx: number;
|
|
267
|
+
dy: number;
|
|
268
|
+
mods: Modifiers;
|
|
269
|
+
} | {
|
|
270
|
+
kind: "key";
|
|
271
|
+
phase: "down" | "up";
|
|
272
|
+
code: string;
|
|
273
|
+
mods: Modifiers;
|
|
274
|
+
} | {
|
|
275
|
+
kind: "blur";
|
|
276
|
+
};
|
|
277
|
+
/** Result of a `Surface.dispatch` call. */
|
|
278
|
+
interface SurfaceResponse {
|
|
279
|
+
needsRedraw: boolean;
|
|
280
|
+
cursorChanged: boolean;
|
|
281
|
+
hoverChanged: boolean;
|
|
282
|
+
}
|
|
283
|
+
//#endregion
|
|
284
|
+
//#region event/cursor.d.ts
|
|
285
|
+
/** 8 cardinal/diagonal resize directions. */
|
|
286
|
+
type ResizeDirection = "n" | "ne" | "e" | "se" | "s" | "sw" | "w" | "nw";
|
|
287
|
+
/** 4 corner positions for rotation handles. */
|
|
288
|
+
type RotationCorner = "nw" | "ne" | "se" | "sw";
|
|
289
|
+
/** Logical cursor icon names — the host maps these to CSS `cursor` values. */
|
|
290
|
+
type CursorIcon = "default" | "pointer" | "move" | "crosshair" | "grab" | "grabbing" | "text" | {
|
|
291
|
+
kind: "resize";
|
|
292
|
+
direction: ResizeDirection;
|
|
293
|
+
} | {
|
|
294
|
+
kind: "rotate";
|
|
295
|
+
corner: RotationCorner;
|
|
296
|
+
};
|
|
297
|
+
//#endregion
|
|
298
|
+
//#region event/gesture.d.ts
|
|
299
|
+
/**
|
|
300
|
+
* Rect type local to the event/ layer.
|
|
301
|
+
*
|
|
302
|
+
* Mirrors `cmath.Rectangle` shape; declared here to keep `event/` free of
|
|
303
|
+
* imports beyond `cmath` types.
|
|
304
|
+
*/
|
|
305
|
+
interface Rect {
|
|
306
|
+
x: number;
|
|
307
|
+
y: number;
|
|
308
|
+
width: number;
|
|
309
|
+
height: number;
|
|
310
|
+
}
|
|
311
|
+
type NodeId = string;
|
|
312
|
+
/**
|
|
313
|
+
* Active interaction state for the surface.
|
|
314
|
+
*
|
|
315
|
+
* Coordinates inside each variant are documented per-field. The surface
|
|
316
|
+
* stores anchor points in document-space (so they survive camera pans during
|
|
317
|
+
* a gesture); incremental deltas are computed against the anchor each move.
|
|
318
|
+
*/
|
|
319
|
+
type SurfaceGesture = {
|
|
320
|
+
kind: "idle";
|
|
321
|
+
} | {
|
|
322
|
+
kind: "pan"; /** Last screen-space pointer position. */
|
|
323
|
+
prev_screen: cmath.Vector2;
|
|
324
|
+
} | {
|
|
325
|
+
kind: "marquee"; /** Anchor (pointer-down) in document-space. */
|
|
326
|
+
anchor_doc: cmath.Vector2; /** Current pointer in document-space. */
|
|
327
|
+
current_doc: cmath.Vector2;
|
|
328
|
+
} | {
|
|
329
|
+
kind: "translate"; /** Selected ids at the start of the gesture. */
|
|
330
|
+
ids: NodeId[]; /** Anchor (pointer-down) in document-space. */
|
|
331
|
+
anchor_doc: cmath.Vector2; /** Last reported pointer in document-space. */
|
|
332
|
+
last_doc: cmath.Vector2;
|
|
333
|
+
} | {
|
|
334
|
+
kind: "resize"; /** Member ids of the group being resized (1 or more). */
|
|
335
|
+
ids: NodeId[]; /** Which handle the user grabbed. */
|
|
336
|
+
direction: ResizeDirection; /** Bounding rect of the group at gesture start, in document-space. */
|
|
337
|
+
initial_rect: Rect; /** Anchor (pointer-down) in document-space. */
|
|
338
|
+
anchor_doc: cmath.Vector2; /** Current rect during the gesture, in document-space. */
|
|
339
|
+
current_rect: Rect;
|
|
340
|
+
} | {
|
|
341
|
+
kind: "rotate";
|
|
342
|
+
ids: NodeId[];
|
|
343
|
+
corner: RotationCorner; /** Subject center in document-space. */
|
|
344
|
+
center_doc: cmath.Vector2; /** Angle at gesture start (radians). */
|
|
345
|
+
anchor_angle: number; /** Current angle (radians). */
|
|
346
|
+
current_angle: number;
|
|
347
|
+
} | {
|
|
348
|
+
kind: "endpoint";
|
|
349
|
+
id: NodeId;
|
|
350
|
+
endpoint: "p1" | "p2"; /** Current endpoint position in document-space. */
|
|
351
|
+
pos_doc: cmath.Vector2;
|
|
352
|
+
};
|
|
353
|
+
//#endregion
|
|
354
|
+
//#region event/intent.d.ts
|
|
355
|
+
/** "preview" is emitted on every gesture move; "commit" once on release. */
|
|
356
|
+
type IntentPhase = "preview" | "commit";
|
|
357
|
+
/**
|
|
358
|
+
* Selection mode for `select` intents.
|
|
359
|
+
*
|
|
360
|
+
* - `replace` — clear selection, then select the given ids
|
|
361
|
+
* - `add` — union into the current selection
|
|
362
|
+
* - `toggle` — flip each given id's membership
|
|
363
|
+
*/
|
|
364
|
+
type SelectMode = "replace" | "add" | "toggle";
|
|
365
|
+
/**
|
|
366
|
+
* Actionable change emitted by the surface. The host commits the intent
|
|
367
|
+
* (wrapping in `history.preview` for `phase: "preview"`, finalizing for
|
|
368
|
+
* `phase: "commit"`).
|
|
369
|
+
*
|
|
370
|
+
* The surface itself never mutates the document.
|
|
371
|
+
*/
|
|
372
|
+
type Intent = {
|
|
373
|
+
kind: "select";
|
|
374
|
+
ids: NodeId[];
|
|
375
|
+
mode: SelectMode;
|
|
376
|
+
} | {
|
|
377
|
+
kind: "deselect_all";
|
|
378
|
+
} | {
|
|
379
|
+
kind: "translate";
|
|
380
|
+
ids: NodeId[]; /** Total delta in document-space, from gesture start. */
|
|
381
|
+
dx: number;
|
|
382
|
+
dy: number;
|
|
383
|
+
phase: IntentPhase;
|
|
384
|
+
} | {
|
|
385
|
+
kind: "resize"; /** Member ids of the group being resized (1 or more). */
|
|
386
|
+
ids: NodeId[];
|
|
387
|
+
anchor: ResizeDirection; /** Target rect in document-space. */
|
|
388
|
+
rect: Rect;
|
|
389
|
+
phase: IntentPhase;
|
|
390
|
+
} | {
|
|
391
|
+
kind: "rotate"; /** Member ids of the group being rotated (typically 1). */
|
|
392
|
+
ids: NodeId[]; /** Target angle delta in radians (relative to gesture start). */
|
|
393
|
+
angle: number;
|
|
394
|
+
phase: IntentPhase;
|
|
395
|
+
} | {
|
|
396
|
+
kind: "marquee_select"; /** Marquee rect in document-space (normalized). */
|
|
397
|
+
rect: Rect;
|
|
398
|
+
additive: boolean;
|
|
399
|
+
phase: IntentPhase;
|
|
400
|
+
} | {
|
|
401
|
+
kind: "set_endpoint"; /** Subject node id (line-shape selection). */
|
|
402
|
+
id: NodeId; /** Which endpoint is being moved. */
|
|
403
|
+
endpoint: "p1" | "p2"; /** Target position in document-space. */
|
|
404
|
+
pos: cmath.Vector2;
|
|
405
|
+
phase: IntentPhase;
|
|
406
|
+
} | {
|
|
407
|
+
kind: "enter_content_edit";
|
|
408
|
+
id: NodeId;
|
|
409
|
+
} | {
|
|
410
|
+
kind: "cancel_gesture";
|
|
411
|
+
};
|
|
412
|
+
/** Callback the host implements to receive intents. */
|
|
413
|
+
type IntentHandler = (intent: Intent) => void;
|
|
414
|
+
//#endregion
|
|
415
|
+
//#region event/transform.d.ts
|
|
416
|
+
/**
|
|
417
|
+
* Surface camera transform: axis-aligned (scale + translate only).
|
|
418
|
+
*
|
|
419
|
+
* Stored as a `cmath.Transform`:
|
|
420
|
+
* `[[sx, 0, tx], [0, sy, ty]]`
|
|
421
|
+
*
|
|
422
|
+
* Off-diagonal components are ignored; the surface does not support rotation
|
|
423
|
+
* or shear at the camera level.
|
|
424
|
+
*/
|
|
425
|
+
type Transform = cmath.Transform;
|
|
426
|
+
//#endregion
|
|
427
|
+
//#region event/shape.d.ts
|
|
428
|
+
/**
|
|
429
|
+
* Selection shape — what the chrome wraps.
|
|
430
|
+
*
|
|
431
|
+
* Hosts return a `SelectionShape` from `shapeOf(id)` so the HUD can lay out
|
|
432
|
+
* the right kind of chrome:
|
|
433
|
+
*
|
|
434
|
+
* - **`rect`** — standard bounding box. Renders selection outline + 4 corner
|
|
435
|
+
* knobs, with virtual edge / rotation hit regions.
|
|
436
|
+
* - **`line`** — two-endpoint primitive (e.g. SVG `<line>`). Renders the
|
|
437
|
+
* line segment + 2 endpoint knobs. No edge / rotation regions.
|
|
438
|
+
* - **`unresolved`** — internal-only. Used inside `SelectionGroup` when the
|
|
439
|
+
* host gave a flat `NodeId[]` to `setSelection` — the chrome builder
|
|
440
|
+
* resolves the real shape by calling `shapeOf(id)` at frame build time.
|
|
441
|
+
* Hosts never emit this; treating it as host-facing would be a bug.
|
|
442
|
+
*
|
|
443
|
+
* Future kinds (e.g. `polyline`, `ellipse_oriented`) can be added without
|
|
444
|
+
* breaking the existing kinds — the chrome builder branches on `kind`.
|
|
445
|
+
*/
|
|
446
|
+
type SelectionShape = {
|
|
447
|
+
kind: "rect";
|
|
448
|
+
rect: Rect;
|
|
449
|
+
} | {
|
|
450
|
+
kind: "line";
|
|
451
|
+
p1: cmath.Vector2;
|
|
452
|
+
p2: cmath.Vector2;
|
|
453
|
+
} | {
|
|
454
|
+
kind: "unresolved";
|
|
455
|
+
id: string;
|
|
456
|
+
};
|
|
457
|
+
/**
|
|
458
|
+
* A logical selection group. The HUD renders one chrome instance per group.
|
|
459
|
+
*
|
|
460
|
+
* The host pre-computes `shape` (typically the union of member bounds for
|
|
461
|
+
* multi-node groups). Member `ids` are the gesture target — they all
|
|
462
|
+
* translate/resize together as a unit.
|
|
463
|
+
*/
|
|
464
|
+
interface SelectionGroup {
|
|
465
|
+
ids: readonly NodeId[];
|
|
466
|
+
shape: SelectionShape;
|
|
467
|
+
}
|
|
468
|
+
//#endregion
|
|
469
|
+
//#region surface/style.d.ts
|
|
470
|
+
/**
|
|
471
|
+
* HUD style — colors, sizes, and offsets for the surface-owned chrome.
|
|
472
|
+
*
|
|
473
|
+
* All fields are optional; defaults follow.
|
|
474
|
+
*/
|
|
475
|
+
interface HUDStyle {
|
|
476
|
+
/** Primary chrome color (selection outline, handle border). */
|
|
477
|
+
chromeColor: string;
|
|
478
|
+
/** Secondary color used for hover outline (lighter than chrome). */
|
|
479
|
+
hoverColor: string;
|
|
480
|
+
/** Handle visual size in screen-px. */
|
|
481
|
+
handleSize: number;
|
|
482
|
+
/** Handle fill color. */
|
|
483
|
+
handleFill: string;
|
|
484
|
+
/** Handle stroke color. */
|
|
485
|
+
handleStroke: string;
|
|
486
|
+
/** Selection outline stroke width (in screen-px). */
|
|
487
|
+
selectionOutlineWidth: number;
|
|
488
|
+
/** Hover outline stroke width (in screen-px). Typically thicker than selection. */
|
|
489
|
+
hoverOutlineWidth: number;
|
|
490
|
+
/** Whether to render rotation handles in addition to resize handles. */
|
|
491
|
+
showRotationHandles: boolean;
|
|
492
|
+
}
|
|
493
|
+
//#endregion
|
|
494
|
+
//#region surface/surface.d.ts
|
|
495
|
+
interface SurfaceOptions {
|
|
496
|
+
/**
|
|
497
|
+
* Content pick. Given a doc-space point, return the topmost node id under
|
|
498
|
+
* the pointer, or `null`. Host wraps its scene query however it wants
|
|
499
|
+
* (e.g. `elementFromPoint` + `data-id` for SVG-DOM hosts).
|
|
500
|
+
*/
|
|
501
|
+
pick: (point_doc: [number, number]) => NodeId | null;
|
|
502
|
+
/**
|
|
503
|
+
* Selection shape for a node — what the chrome should wrap. Most nodes
|
|
504
|
+
* return `{ kind: "rect", rect }`; vector lines return
|
|
505
|
+
* `{ kind: "line", p1, p2 }`.
|
|
506
|
+
*/
|
|
507
|
+
shapeOf: (id: NodeId) => SelectionShape | null;
|
|
508
|
+
/** Surface emits intents the host commits. */
|
|
509
|
+
onIntent: IntentHandler;
|
|
510
|
+
/** Initial style (partial; merged with defaults). */
|
|
511
|
+
style?: Partial<HUDStyle>;
|
|
512
|
+
/** Initial readonly flag. Default `false`. */
|
|
513
|
+
readonly?: boolean;
|
|
514
|
+
/** Optional HUDCanvas color override. */
|
|
515
|
+
color?: string;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Top-level wired surface.
|
|
519
|
+
*
|
|
520
|
+
* Owns an internal `HUDCanvas`, a `SurfaceState` (gesture/hover/...) and
|
|
521
|
+
* the host providers. On every `dispatch`, the state machine runs;
|
|
522
|
+
* `draw` composes surface chrome + host-fed extras into a single canvas
|
|
523
|
+
* paint.
|
|
524
|
+
*/
|
|
525
|
+
declare class Surface {
|
|
526
|
+
private hudCanvas;
|
|
527
|
+
private state;
|
|
528
|
+
private style;
|
|
529
|
+
private opts;
|
|
530
|
+
private width;
|
|
531
|
+
private height;
|
|
532
|
+
constructor(canvas: HTMLCanvasElement, options: SurfaceOptions);
|
|
533
|
+
setSize(w: number, h: number): void;
|
|
534
|
+
setTransform(t: Transform): void;
|
|
535
|
+
/**
|
|
536
|
+
* Push a new selection from the host.
|
|
537
|
+
*
|
|
538
|
+
* Accepts either:
|
|
539
|
+
* - `NodeId[]` — each id becomes its own single-member group, shape
|
|
540
|
+
* resolved via `shapeOf(id)` by the chrome builder.
|
|
541
|
+
* - `SelectionGroup[]` — pre-computed groups with their union shape.
|
|
542
|
+
*
|
|
543
|
+
* See `SurfaceState.setSelection` for details.
|
|
544
|
+
*/
|
|
545
|
+
setSelection(input: readonly NodeId[] | readonly SelectionGroup[]): void;
|
|
546
|
+
setStyle(partial: Partial<HUDStyle>): void;
|
|
547
|
+
setReadonly(v: boolean): void;
|
|
548
|
+
/**
|
|
549
|
+
* Set or clear a host-driven hover override.
|
|
550
|
+
*
|
|
551
|
+
* The surface tracks two hover sources:
|
|
552
|
+
* - **Pointer pick** — what scene content is under the cursor (updated
|
|
553
|
+
* automatically on `pointer_move`).
|
|
554
|
+
* - **Host override** — what the host wants to show as hovered, e.g.
|
|
555
|
+
* from a layers panel row mouseenter.
|
|
556
|
+
*
|
|
557
|
+
* The override (when non-null) wins. `hover()` returns the effective
|
|
558
|
+
* value; chrome renders the effective value. Pass `null` to clear and
|
|
559
|
+
* fall back to pointer pick.
|
|
560
|
+
*
|
|
561
|
+
* Returns the same response shape as `dispatch` so the host can react
|
|
562
|
+
* to whether anything actually changed.
|
|
563
|
+
*/
|
|
564
|
+
setHoverOverride(id: NodeId | null): SurfaceResponse;
|
|
565
|
+
dispose(): void;
|
|
566
|
+
dispatch(event: SurfaceEvent): SurfaceResponse;
|
|
567
|
+
draw(extra?: HUDDraw): void;
|
|
568
|
+
/** Convenience: clear the canvas (e.g. when the host stops the surface). */
|
|
569
|
+
clear(): void;
|
|
570
|
+
gesture(): SurfaceGesture;
|
|
571
|
+
/**
|
|
572
|
+
* The effective hover: host override (when set) wins over pointer pick.
|
|
573
|
+
* Use this for chrome decisions and host-side reads.
|
|
574
|
+
*/
|
|
575
|
+
hover(): NodeId | null;
|
|
576
|
+
cursor(): CursorIcon;
|
|
577
|
+
modifiers(): Modifiers;
|
|
578
|
+
}
|
|
579
|
+
//#endregion
|
|
580
|
+
//#region event/hit-regions.d.ts
|
|
581
|
+
/**
|
|
582
|
+
* Action a UI hit-region triggers when clicked.
|
|
583
|
+
*
|
|
584
|
+
* Each variant carries a snapshot of the relevant shape state at the time
|
|
585
|
+
* the chrome was built — so the surface can start a gesture without an
|
|
586
|
+
* extra round-trip to host providers. The chrome builder is the single
|
|
587
|
+
* source of truth for "what does this hit region act on?"
|
|
588
|
+
*
|
|
589
|
+
* - `select_node` — user clicked on a node-representative UI region.
|
|
590
|
+
* - `resize_handle` — one of 8 resize regions (4 corner knobs + 4 virtual
|
|
591
|
+
* edges). Carries the group's member ids and the group's initial rect.
|
|
592
|
+
* - `rotate_handle` — one of 4 virtual rotation regions outside the group's
|
|
593
|
+
* corners. Carries the group's initial rect for center math.
|
|
594
|
+
* - `endpoint_handle` — endpoint of a line-shape selection. Carries the
|
|
595
|
+
* line's current p1/p2 so dragging is relative to a stable snapshot.
|
|
596
|
+
* - `translate_handle` — body region covering a selection group's bbox.
|
|
597
|
+
* Pushed under the corner / edge / rotation regions so resize wins on
|
|
598
|
+
* overlap. Lets the user grab any part of the selection chrome — including
|
|
599
|
+
* transparent corners of a circle's bbox — to start a translate. Hud is
|
|
600
|
+
* the event source once present.
|
|
601
|
+
*/
|
|
602
|
+
type OverlayAction = {
|
|
603
|
+
kind: "select_node";
|
|
604
|
+
id: NodeId;
|
|
605
|
+
} | {
|
|
606
|
+
kind: "resize_handle";
|
|
607
|
+
direction: ResizeDirection;
|
|
608
|
+
ids: readonly NodeId[];
|
|
609
|
+
initial_rect: {
|
|
610
|
+
x: number;
|
|
611
|
+
y: number;
|
|
612
|
+
width: number;
|
|
613
|
+
height: number;
|
|
614
|
+
};
|
|
615
|
+
} | {
|
|
616
|
+
kind: "rotate_handle";
|
|
617
|
+
corner: RotationCorner;
|
|
618
|
+
ids: readonly NodeId[];
|
|
619
|
+
initial_rect: {
|
|
620
|
+
x: number;
|
|
621
|
+
y: number;
|
|
622
|
+
width: number;
|
|
623
|
+
height: number;
|
|
624
|
+
};
|
|
625
|
+
} | {
|
|
626
|
+
kind: "endpoint_handle";
|
|
627
|
+
endpoint: "p1" | "p2";
|
|
628
|
+
id: NodeId; /** Snapshot of the line endpoints in doc-space at chrome build time. */
|
|
629
|
+
p1: [number, number];
|
|
630
|
+
p2: [number, number];
|
|
631
|
+
} | {
|
|
632
|
+
kind: "translate_handle";
|
|
633
|
+
ids: readonly NodeId[];
|
|
634
|
+
};
|
|
635
|
+
//#endregion
|
|
636
|
+
//#region event/overlay.d.ts
|
|
637
|
+
/**
|
|
638
|
+
* Minimum hit-target size in screen-px.
|
|
639
|
+
*
|
|
640
|
+
* Visual knobs are typically 8px, but the hit region is 16px so users don't
|
|
641
|
+
* need pixel-perfect aim. Matches `MIN_HIT_SIZE` in the Rust overlay.
|
|
642
|
+
*/
|
|
643
|
+
declare const MIN_HIT_SIZE = 16;
|
|
644
|
+
/**
|
|
645
|
+
* Below this selection size (in screen-px on either axis), chrome is
|
|
646
|
+
* suppressed — both the visual handles AND the hit regions. Matches
|
|
647
|
+
* `MIN_HANDLES_VISIBLE_SIZE` in the Rust overlay.
|
|
648
|
+
*/
|
|
649
|
+
declare const MIN_CHROME_VISIBLE_SIZE = 12;
|
|
650
|
+
/**
|
|
651
|
+
* A hit region for one overlay element. Always screen-space; either anchored
|
|
652
|
+
* to a doc-space point (so the hit region tracks the document under camera
|
|
653
|
+
* changes) or expressed as a pre-projected screen-space AABB (for things
|
|
654
|
+
* like edge regions whose layout is fundamentally screen-space).
|
|
655
|
+
*/
|
|
656
|
+
type HitShape =
|
|
657
|
+
/**
|
|
658
|
+
* Fixed screen-space rectangle, centered (or otherwise anchored) on a
|
|
659
|
+
* doc-space point. The surface projects `anchor_doc` through the current
|
|
660
|
+
* transform each frame; rect dimensions stay constant in CSS px.
|
|
661
|
+
*/
|
|
662
|
+
{
|
|
663
|
+
kind: "screen_rect_at_doc";
|
|
664
|
+
anchor_doc: cmath.Vector2; /** Screen-space size in CSS px. */
|
|
665
|
+
width: number;
|
|
666
|
+
height: number; /** Which point of the rect sits on the anchor. Default: "center". */
|
|
667
|
+
placement?: "center" | "tl" | "tr" | "bl" | "br";
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Screen-space AABB at pre-projected screen coordinates. Used for elements
|
|
671
|
+
* whose layout the chrome builder already projected (e.g. edge strips).
|
|
672
|
+
*/
|
|
673
|
+
| {
|
|
674
|
+
kind: "screen_aabb";
|
|
675
|
+
rect: Rect;
|
|
676
|
+
};
|
|
677
|
+
/**
|
|
678
|
+
* Visual representation for one overlay element. Maps directly to the
|
|
679
|
+
* primitive layer's `HUDDraw` entries — the surface fans these out into the
|
|
680
|
+
* one merged `HUDDraw` it hands to `HUDCanvas.draw()` each frame.
|
|
681
|
+
*
|
|
682
|
+
* Virtual elements (rotation, side resize) omit `render`.
|
|
683
|
+
*/
|
|
684
|
+
type RenderShape = /** Screen-space sized rect at doc anchor — e.g. resize knob. */{
|
|
685
|
+
kind: "screen_rect";
|
|
686
|
+
anchor_doc: cmath.Vector2;
|
|
687
|
+
width: number;
|
|
688
|
+
height: number;
|
|
689
|
+
placement?: "center" | "tl" | "tr" | "bl" | "br";
|
|
690
|
+
fill?: boolean;
|
|
691
|
+
stroke?: boolean;
|
|
692
|
+
fillColor?: string;
|
|
693
|
+
strokeColor?: string;
|
|
694
|
+
} /** Doc-space rect — e.g. selection outline, marquee. */ | {
|
|
695
|
+
kind: "doc_rect";
|
|
696
|
+
x: number;
|
|
697
|
+
y: number;
|
|
698
|
+
width: number;
|
|
699
|
+
height: number;
|
|
700
|
+
stroke?: boolean;
|
|
701
|
+
fill?: boolean;
|
|
702
|
+
fillOpacity?: number;
|
|
703
|
+
dashed?: boolean;
|
|
704
|
+
} /** Doc-space line — e.g. line-shape selection outline. */ | {
|
|
705
|
+
kind: "doc_line";
|
|
706
|
+
x1: number;
|
|
707
|
+
y1: number;
|
|
708
|
+
x2: number;
|
|
709
|
+
y2: number;
|
|
710
|
+
dashed?: boolean;
|
|
711
|
+
};
|
|
712
|
+
/**
|
|
713
|
+
* One interactable element of overlay UI.
|
|
714
|
+
*
|
|
715
|
+
* Pairs (visual, event, action) into a single struct so the discipline of
|
|
716
|
+
* "render box ≠ event box" is explicit. The chrome builder emits a list of
|
|
717
|
+
* these per frame; the surface fans them into render commands and hit
|
|
718
|
+
* regions.
|
|
719
|
+
*
|
|
720
|
+
* - **Virtual elements** (e.g. rotation handles, edge resize strips) omit
|
|
721
|
+
* `render` — they exist only as hit regions.
|
|
722
|
+
* - **Padded elements** have `hit` larger than the corresponding `render`
|
|
723
|
+
* shape (e.g. 16px hit AABB around an 8px visual knob).
|
|
724
|
+
* - **Cursor hover** is driven by the element's `cursor` field when the
|
|
725
|
+
* pointer enters its hit region.
|
|
726
|
+
*/
|
|
727
|
+
interface OverlayElement {
|
|
728
|
+
action: OverlayAction;
|
|
729
|
+
hit: HitShape;
|
|
730
|
+
render?: RenderShape;
|
|
731
|
+
cursor?: CursorIcon;
|
|
732
|
+
}
|
|
733
|
+
//#endregion
|
|
734
|
+
export { HUDPolyline as A, marqueeToHUDDraw as C, HUDCanvasOptions as D, HUDCanvas as E, HUDRule as M, HUDScreenRect as N, HUDDraw as O, lassoToHUDDraw as S, snapGuideToHUDDraw as T, Modifiers as _, RenderShape as a, SurfaceEvent as b, HUDStyle as c, Intent as d, IntentPhase as f, RotationCorner as g, ResizeDirection as h, OverlayElement as i, HUDRect as j, HUDLine as k, SelectionGroup as l, CursorIcon as m, MIN_CHROME_VISIBLE_SIZE as n, Surface as o, SurfaceGesture as p, MIN_HIT_SIZE as r, SurfaceOptions as s, HitShape as t, SelectionShape as u, NO_MODS as v, measurementToHUDDraw as w, SurfaceResponse as x, PointerButton as y };
|