@grida/hud 0.2.0 → 0.2.2

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.
Files changed (38) hide show
  1. package/README.md +562 -37
  2. package/dist/core/index.d.mts +181 -0
  3. package/dist/core/index.d.ts +181 -0
  4. package/dist/core/index.js +301 -0
  5. package/dist/core/index.mjs +291 -0
  6. package/dist/cursors/index.d.mts +1 -1
  7. package/dist/cursors/index.d.ts +1 -1
  8. package/dist/cursors/index.js +1 -1
  9. package/dist/cursors/index.mjs +1 -1
  10. package/dist/index-BQtDtpHM.d.mts +3215 -0
  11. package/dist/index-BlfZbeEJ.d.ts +3215 -0
  12. package/dist/index.d.mts +4 -3
  13. package/dist/index.d.ts +4 -3
  14. package/dist/index.js +55 -2
  15. package/dist/index.mjs +3 -3
  16. package/dist/overlay-CVV4s3IL.d.ts +241 -0
  17. package/dist/overlay-dsG32baA.d.mts +241 -0
  18. package/dist/primitives/bedrock.d.mts +47 -0
  19. package/dist/primitives/bedrock.d.ts +47 -0
  20. package/dist/primitives/bedrock.js +71 -0
  21. package/dist/primitives/bedrock.mjs +65 -0
  22. package/dist/react.d.mts +3 -2
  23. package/dist/react.d.ts +2 -1
  24. package/dist/react.js +4 -1
  25. package/dist/react.mjs +4 -1
  26. package/dist/surface-BHDH6P6p.js +7383 -0
  27. package/dist/surface-B_8w6VWG.mjs +6929 -0
  28. package/dist/types-3wwFisZs.d.mts +296 -0
  29. package/dist/types-3wwFisZs.d.ts +296 -0
  30. package/package.json +16 -3
  31. package/dist/index-Cp0X4SV7.d.ts +0 -947
  32. package/dist/index-DhGdcuQz.d.mts +0 -947
  33. package/dist/surface-BvMmXoEl.mjs +0 -2471
  34. package/dist/surface-ofSNTJ8H.js +0 -2607
  35. /package/dist/{cursor-BFGUuD2M.d.mts → cursor-CxS8EMvm.d.mts} +0 -0
  36. /package/dist/{cursor-CIYvFshz.d.ts → cursor-CxS8EMvm.d.ts} +0 -0
  37. /package/dist/{cursor-BieMVb71.mjs → cursor-DW-uAPVE.mjs} +0 -0
  38. /package/dist/{cursor-DsP9qtN2.js → cursor-FGiJBdU-.js} +0 -0
@@ -1,947 +0,0 @@
1
- import { i as RotationCorner, n as CursorRenderer, r as ResizeDirection, t as CursorIcon } from "./cursor-CIYvFshz.js";
2
- import cmath from "@grida/cmath";
3
- import { guide } from "@grida/cmath/_snap";
4
- import { Measurement } from "@grida/cmath/_measurement";
5
-
6
- //#region primitives/types.d.ts
7
- type HUDSemanticGroup = string;
8
- interface HUDSemantic {
9
- /** Semantic owner of this primitive, used for group-level visibility policy. */
10
- group?: HUDSemanticGroup;
11
- }
12
- /**
13
- * A line segment in document space, extending `cmath.ui.Line` with
14
- * an optional `dashed` style.
15
- */
16
- interface HUDLine extends cmath.ui.Line, HUDSemantic {
17
- dashed?: boolean;
18
- /** Stroke width in screen-space CSS px. Defaults to the canvas default. */
19
- strokeWidth?: number;
20
- /**
21
- * Override the canvas color for this line's stroke and label pill. Falls
22
- * back to the canvas's current color when absent.
23
- */
24
- color?: string;
25
- /**
26
- * Label rotation in radians (CCW) around the label pill's screen-space
27
- * center. Defaults to `0`. Used to make the size meter pill rotate with
28
- * a `SelectionShape.transformed` parent. Only affects the pill +
29
- * text; the underlying line stroke is unaffected.
30
- */
31
- labelAngle?: number;
32
- }
33
- /**
34
- * A full-viewport axis-aligned line (infinite extent) at a given offset.
35
- *
36
- * `axis` indicates which axis the `offset` lives on:
37
- * - `"x"` → vertical line at x=offset
38
- * - `"y"` → horizontal line at y=offset
39
- *
40
- * Offset is in document space; the renderer projects it to screen.
41
- */
42
- interface HUDRule extends HUDSemantic {
43
- axis: "x" | "y";
44
- offset: number;
45
- /**
46
- * Override the canvas color for this rule's stroke. Falls back to
47
- * the canvas's current color when absent.
48
- */
49
- color?: string;
50
- }
51
- /**
52
- * A single document-space point rendered as a small crosshair "X" on
53
- * the canvas. The size of the crosshair is fixed in CSS pixels; the
54
- * anchor (x, y) is in document space.
55
- *
56
- * Stroke uses the canvas color by default. Per-point override is opt-in.
57
- */
58
- interface HUDPoint extends HUDSemantic {
59
- x: number;
60
- y: number;
61
- /**
62
- * Override the canvas color for this point's crosshair stroke.
63
- * Falls back to the canvas's current color when absent.
64
- */
65
- color?: string;
66
- }
67
- /**
68
- * A rectangle in document space.
69
- *
70
- * Stroke uses the canvas color by default. Fill is opt-in.
71
- */
72
- interface HUDRect extends HUDSemantic {
73
- x: number;
74
- y: number;
75
- width: number;
76
- height: number;
77
- /** Whether to stroke the outline (default: true). */
78
- stroke?: boolean;
79
- /** Whether to fill the interior (default: false). */
80
- fill?: boolean;
81
- /** Opacity of the fill color, 0–1 (default: 1). Ignored when `fill` is falsy. */
82
- fillOpacity?: number;
83
- /** Draw the outline with a dash pattern. */
84
- dashed?: boolean;
85
- /** Stroke width in screen-space CSS px. Defaults to the canvas default. */
86
- strokeWidth?: number;
87
- /**
88
- * Override the canvas color for this rect's stroke and fill. Falls back
89
- * to the canvas's current color when absent.
90
- */
91
- color?: string;
92
- }
93
- /**
94
- * A polyline (or closed polygon) in document space.
95
- *
96
- * Stroke uses the canvas color by default. Fill is opt-in.
97
- */
98
- interface HUDPolyline extends HUDSemantic {
99
- points: cmath.Vector2[];
100
- /** Whether to stroke the path (default: true). */
101
- stroke?: boolean;
102
- /** Whether to fill the interior (default: false). */
103
- fill?: boolean;
104
- /** Opacity of the fill color, 0–1 (default: 1). Ignored when `fill` is falsy. */
105
- fillOpacity?: number;
106
- /** Draw the stroke with a dash pattern. */
107
- dashed?: boolean;
108
- /**
109
- * Override the canvas color for this polyline's stroke and fill. Falls
110
- * back to the canvas's current color when absent.
111
- */
112
- color?: string;
113
- }
114
- /**
115
- * A rectangle whose **size is fixed in screen-space** but whose **anchor lives
116
- * in document space**. Used for handles and other chrome that must not scale
117
- * with viewport zoom.
118
- *
119
- * The anchor `(x, y)` is the document-space point that maps to one corner (or
120
- * center) of the resulting screen-space rect — controlled by `anchor`.
121
- *
122
- * Default anchor is `"center"` (the doc-space point becomes the rect's center).
123
- */
124
- interface HUDScreenRect extends HUDSemantic {
125
- /** Document-space anchor point. */
126
- x: number;
127
- y: number;
128
- /** Screen-space size in CSS px. */
129
- width: number;
130
- height: number;
131
- /** Which point of the rect sits at (x, y). Default: "center". */
132
- anchor?: "center" | "tl" | "tr" | "bl" | "br";
133
- fill?: boolean;
134
- stroke?: boolean;
135
- /** Override the canvas color for fill. */
136
- fillColor?: string;
137
- /** Override the canvas color for stroke. */
138
- strokeColor?: string;
139
- /**
140
- * Rotation in radians (CCW) around the rect's screen-space center.
141
- * Defaults to `0`. Used to make handle knobs / size badges rotate
142
- * together with a `SelectionShape.transformed` parent. Hit-testing is
143
- * unaffected — render and hit live on independent shapes per the
144
- * package's render/hit-test split (see README).
145
- */
146
- angle?: number;
147
- }
148
- /**
149
- * A complete set of draw commands for one frame.
150
- *
151
- * The HUD clears the canvas and draws everything in this struct each frame.
152
- * Callers build a `HUDDraw` from their domain data (snap result, measurement,
153
- * guide state, etc.) and hand it to `HUDCanvas.draw()`.
154
- */
155
- interface HUDDraw {
156
- lines?: HUDLine[];
157
- rules?: HUDRule[];
158
- points?: HUDPoint[];
159
- rects?: HUDRect[];
160
- polylines?: HUDPolyline[];
161
- /** Screen-space-sized rects anchored to document-space points (handles). */
162
- screenRects?: HUDScreenRect[];
163
- }
164
- //#endregion
165
- //#region primitives/pixel-grid.d.ts
166
- declare const DEFAULT_PIXEL_GRID_COLOR = "rgba(150, 150, 150, 0.15)";
167
- declare const DEFAULT_PIXEL_GRID_STEPS: [number, number];
168
- interface PixelGridConfig {
169
- enabled: boolean;
170
- /** Minimum `transform[0][0]` (uniform scale) at which the grid renders. */
171
- zoomThreshold: number;
172
- /**
173
- * Optional camera transform used to space the grid. Hosts that drive the
174
- * HUD canvas's own `setTransform` can omit this — the pixel grid falls
175
- * back to the canvas's chrome transform. Hosts that keep the HUD at
176
- * identity (e.g. `@grida/svg-editor`, which applies the camera as a CSS
177
- * transform on the `<svg>` element) must supply this explicitly and
178
- * update it on every camera change via `setPixelGridTransform`.
179
- */
180
- transform?: cmath.Transform;
181
- color?: string;
182
- steps?: [number, number];
183
- }
184
- interface DrawPixelGridParams {
185
- ctx: CanvasRenderingContext2D;
186
- transform: cmath.Transform;
187
- width: number;
188
- height: number;
189
- dpr: number;
190
- color?: string;
191
- steps?: [number, number];
192
- }
193
- declare function drawPixelGrid(p: DrawPixelGridParams): void;
194
- //#endregion
195
- //#region primitives/canvas.d.ts
196
- interface HUDCanvasOptions {
197
- color?: string;
198
- }
199
- /**
200
- * Imperative Canvas 2D renderer for the HUD overlay.
201
- *
202
- * Owns a single `<canvas>` element and draws {@link HUDDraw} command lists
203
- * each frame. All drawing is immediate-mode: the canvas is cleared and
204
- * fully redrawn on every `draw()` call.
205
- *
206
- * The viewport transform is assumed to be axis-aligned (scale + translate only,
207
- * no rotation/shear). The off-diagonal components of the transform matrix are
208
- * ignored.
209
- */
210
- declare class HUDCanvas {
211
- private canvas;
212
- private ctx;
213
- private dpr;
214
- private transform;
215
- private color;
216
- private width;
217
- private height;
218
- private pixelGrid;
219
- constructor(canvas: HTMLCanvasElement, options?: HUDCanvasOptions);
220
- setColor(color?: string): void;
221
- setSize(w: number, h: number): void;
222
- setTransform(transform: cmath.Transform): void;
223
- /**
224
- * Configure the back-most pixel-grid layer. Pass `null` to disable.
225
- * Drawn before any HUD primitive, gated by `zoomThreshold`. See
226
- * `PixelGridConfig.transform` for the two-transform contract.
227
- */
228
- setPixelGrid(config: PixelGridConfig | null): void;
229
- /**
230
- * Update only the pixel grid's transform, without replacing the rest of
231
- * the config. Cheap to call per camera tick.
232
- */
233
- setPixelGridTransform(transform: cmath.Transform): void;
234
- /**
235
- * Clear the canvas and draw all primitives in `commands`.
236
- * Pass `undefined` to clear without drawing (e.g. when no overlay is active).
237
- */
238
- draw(commands: HUDDraw | undefined): void;
239
- private applyViewTransform;
240
- private applyScreenTransform;
241
- /** Project a scalar offset on `axis` to screen-space. */
242
- private deltaToScreen;
243
- private drawRules;
244
- private drawLines;
245
- private drawRects;
246
- private drawPolylines;
247
- private drawPoints;
248
- /**
249
- * Draw rects whose **size is in screen-space** but whose **anchor is in
250
- * document-space**. The doc-space point is projected via the current
251
- * transform; the rect is then drawn at fixed CSS-pixel dimensions.
252
- *
253
- * This is the primitive used to draw resize / rotate handles — they must
254
- * remain a constant visual size regardless of viewport zoom.
255
- */
256
- private drawScreenRects;
257
- }
258
- //#endregion
259
- //#region primitives/draw.d.ts
260
- interface HUDGroupFilter {
261
- hidden?: Iterable<HUDSemanticGroup>;
262
- }
263
- /**
264
- * Filter a draw command list by semantic group.
265
- *
266
- * Ungrouped primitives are always kept. The function is intentionally shallow:
267
- * primitives are immutable command objects on the hot draw path, so preserving
268
- * object identity keeps this as a visibility pass rather than a rewrite.
269
- */
270
- declare function filterHUDDrawByGroup(draw: HUDDraw | undefined, filter: HUDGroupFilter): HUDDraw | undefined;
271
- //#endregion
272
- //#region primitives/snap-guide.d.ts
273
- /**
274
- * Convert a `guide.SnapGuide` (the output of `guide.plot()`) into a
275
- * generic {@link HUDDraw} command list.
276
- *
277
- * `color`, when supplied, is applied as the per-item stroke override
278
- * for every emitted line, rule, and point. When absent, the HUD
279
- * canvas's current color is used.
280
- */
281
- declare function snapGuideToHUDDraw(sg: guide.SnapGuide | undefined, color?: string): HUDDraw | undefined;
282
- //#endregion
283
- //#region primitives/measurement-guide.d.ts
284
- /**
285
- * Convert a {@link Measurement} (the output of `measure()`) into a
286
- * generic {@link HUDDraw} command list.
287
- *
288
- * All coordinates are in **document space** — the HUD canvas applies
289
- * the viewport transform.
290
- *
291
- * Produces:
292
- * - Two stroke-only rects for the A and B bounding boxes
293
- * - One labelled guide line per non-zero distance (solid)
294
- * - One auxiliary line per non-zero side connecting the guide to B (dashed)
295
- *
296
- * If `color` is provided, every emitted line and rect carries that color so
297
- * the guides render distinctly from the canvas's chrome color. When used via
298
- * `surface.draw(extra)` (the host-fed-extras channel) this is required to
299
- * separate measurement from selection chrome on a shared canvas.
300
- */
301
- declare function measurementToHUDDraw(m: Measurement, color?: string): HUDDraw;
302
- //#endregion
303
- //#region primitives/marquee.d.ts
304
- /**
305
- * Convert two marquee corner points into a {@link HUDDraw} command list.
306
- *
307
- * All coordinates are in **document space**.
308
- *
309
- * Produces a single rectangle with a stroke outline and a semi-transparent fill.
310
- */
311
- declare function marqueeToHUDDraw(a: cmath.Vector2, b: cmath.Vector2): HUDDraw;
312
- //#endregion
313
- //#region primitives/lasso.d.ts
314
- /**
315
- * Convert a lasso point sequence into a {@link HUDDraw} command list.
316
- *
317
- * All coordinates are in **document space**.
318
- *
319
- * Produces a single polyline with a dashed stroke and a semi-transparent fill.
320
- */
321
- declare function lassoToHUDDraw(points: cmath.Vector2[]): HUDDraw | undefined;
322
- //#endregion
323
- //#region event/event.d.ts
324
- /** Modifier-key snapshot at the moment an event was produced. */
325
- interface Modifiers {
326
- shift: boolean;
327
- alt: boolean;
328
- meta: boolean;
329
- ctrl: boolean;
330
- }
331
- declare const NO_MODS: Modifiers;
332
- type PointerButton = "primary" | "secondary" | "middle";
333
- /**
334
- * Input event consumed by `Surface.dispatch`.
335
- *
336
- * All coordinates are **screen-space CSS pixels relative to the canvas**.
337
- * The surface owns the camera and converts to document-space internally.
338
- */
339
- type SurfaceEvent = {
340
- kind: "pointer_move";
341
- x: number;
342
- y: number;
343
- mods: Modifiers;
344
- } | {
345
- kind: "pointer_down";
346
- x: number;
347
- y: number;
348
- button: PointerButton;
349
- mods: Modifiers;
350
- } | {
351
- kind: "pointer_up";
352
- x: number;
353
- y: number;
354
- button: PointerButton;
355
- mods: Modifiers;
356
- } | {
357
- kind: "modifiers";
358
- mods: Modifiers;
359
- } | {
360
- kind: "wheel";
361
- x: number;
362
- y: number;
363
- dx: number;
364
- dy: number;
365
- mods: Modifiers;
366
- } | {
367
- kind: "key";
368
- phase: "down" | "up";
369
- code: string;
370
- mods: Modifiers;
371
- } | {
372
- kind: "blur";
373
- };
374
- /** Result of a `Surface.dispatch` call. */
375
- interface SurfaceResponse {
376
- needsRedraw: boolean;
377
- cursorChanged: boolean;
378
- hoverChanged: boolean;
379
- }
380
- //#endregion
381
- //#region event/shape.d.ts
382
- /**
383
- * Selection shape — what the chrome wraps.
384
- *
385
- * Hosts return a `SelectionShape` from `shapeOf(id)` so the HUD can lay out
386
- * the right kind of chrome:
387
- *
388
- * - **`rect`** — standard bounding box. Renders selection outline + 4 corner
389
- * knobs, with virtual edge / rotation hit regions.
390
- * - **`transformed`** — an axis-aligned local bbox PLUS a 2×3 affine matrix
391
- * mapping local-frame points into doc-space. Covers rotation, skew,
392
- * non-uniform scale, and mirror with one code path. `local.width × local.height`
393
- * are the artwork's true dims (read by the size badge and by `applyResize`
394
- * in local space). Identity matrix here is byte-equivalent to
395
- * `{ kind: "rect", rect: local }`.
396
- * - **`line`** — two-endpoint primitive (e.g. SVG `<line>`). Renders the
397
- * line segment + 2 endpoint knobs. No edge / rotation regions.
398
- * - **`unresolved`** — internal-only. Used inside `SelectionGroup` when the
399
- * host gave a flat `NodeId[]` to `setSelection` — the chrome builder
400
- * resolves the real shape by calling `shapeOf(id)` at frame build time.
401
- * Hosts never emit this; treating it as host-facing would be a bug.
402
- *
403
- * Future kinds (e.g. `polyline`, `ellipse_oriented`) can be added without
404
- * breaking the existing kinds — the chrome builder branches on `kind`.
405
- */
406
- type SelectionShape = {
407
- kind: "rect";
408
- rect: Rect;
409
- } | {
410
- kind: "transformed"; /** Local-frame AABB; width/height are artwork's true dims. */
411
- local: Rect; /** 2×3 affine mapping local-frame points → doc-space. */
412
- matrix: cmath.Transform;
413
- } | {
414
- kind: "line";
415
- p1: cmath.Vector2;
416
- p2: cmath.Vector2;
417
- } | {
418
- kind: "unresolved";
419
- id: string;
420
- };
421
- /**
422
- * A logical selection group. The HUD renders one chrome instance per group.
423
- *
424
- * The host pre-computes `shape` (typically the union of member bounds for
425
- * multi-node groups). Member `ids` are the gesture target — they all
426
- * translate/resize together as a unit.
427
- */
428
- interface SelectionGroup {
429
- ids: readonly NodeId[];
430
- shape: SelectionShape;
431
- }
432
- //#endregion
433
- //#region event/gesture.d.ts
434
- /**
435
- * Rect type local to the event/ layer.
436
- *
437
- * Mirrors `cmath.Rectangle` shape; declared here to keep `event/` free of
438
- * imports beyond `cmath` types.
439
- */
440
- interface Rect {
441
- x: number;
442
- y: number;
443
- width: number;
444
- height: number;
445
- }
446
- type NodeId = string;
447
- /**
448
- * Active interaction state for the surface.
449
- *
450
- * Coordinates inside each variant are documented per-field. The surface
451
- * stores anchor points in document-space (so they survive camera pans during
452
- * a gesture); incremental deltas are computed against the anchor each move.
453
- */
454
- type SurfaceGesture = {
455
- kind: "idle";
456
- } | {
457
- kind: "pan"; /** Last screen-space pointer position. */
458
- prev_screen: cmath.Vector2;
459
- } | {
460
- kind: "marquee"; /** Anchor (pointer-down) in document-space. */
461
- anchor_doc: cmath.Vector2; /** Current pointer in document-space. */
462
- current_doc: cmath.Vector2;
463
- } | {
464
- kind: "translate"; /** Selected ids at the start of the gesture. */
465
- ids: NodeId[]; /** Anchor (pointer-down) in document-space. */
466
- anchor_doc: cmath.Vector2; /** Last reported pointer in document-space. */
467
- last_doc: cmath.Vector2;
468
- } | {
469
- kind: "resize"; /** Member ids of the group being resized (1 or more). */
470
- ids: NodeId[]; /** Which handle the user grabbed. */
471
- direction: ResizeDirection;
472
- /**
473
- * Selection shape at gesture start. For `kind: "rect"` this is the
474
- * doc-space bbox; for `kind: "transformed"` it carries the local-frame
475
- * AABB + matrix so resize math runs in the rotated/skewed local frame.
476
- */
477
- initial_shape: SelectionShape; /** Anchor (pointer-down) in document-space. */
478
- anchor_doc: cmath.Vector2; /** Current shape during the gesture. Same kind as `initial_shape`. */
479
- current_shape: SelectionShape;
480
- } | {
481
- kind: "rotate";
482
- ids: NodeId[];
483
- corner: RotationCorner; /** Subject center in document-space. */
484
- center_doc: cmath.Vector2; /** Angle at gesture start (radians). */
485
- anchor_angle: number; /** Current angle (radians). */
486
- current_angle: number;
487
- /**
488
- * Selection's screen-space rotation at gesture start (radians).
489
- * Composed with `current_angle - anchor_angle` each frame to set
490
- * the rotate-cursor's `baseAngle` — so cursors on already-rotated
491
- * selections continue tilting correctly mid-gesture instead of
492
- * snapping back to 0 + delta.
493
- */
494
- initial_cursor_angle: number;
495
- } | {
496
- kind: "endpoint";
497
- id: NodeId;
498
- endpoint: "p1" | "p2"; /** Current endpoint position in document-space. */
499
- pos_doc: cmath.Vector2;
500
- };
501
- //#endregion
502
- //#region event/intent.d.ts
503
- /** "preview" is emitted on every gesture move; "commit" once on release. */
504
- type IntentPhase = "preview" | "commit";
505
- /**
506
- * Selection mode for `select` intents.
507
- *
508
- * - `replace` — clear selection, then select the given ids
509
- * - `add` — union into the current selection
510
- * - `toggle` — flip each given id's membership
511
- */
512
- type SelectMode = "replace" | "add" | "toggle";
513
- /**
514
- * Actionable change emitted by the surface. The host commits the intent
515
- * (wrapping in `history.preview` for `phase: "preview"`, finalizing for
516
- * `phase: "commit"`).
517
- *
518
- * The surface itself never mutates the document.
519
- */
520
- type Intent = {
521
- kind: "select";
522
- ids: NodeId[];
523
- mode: SelectMode;
524
- } | {
525
- kind: "deselect_all";
526
- } | {
527
- kind: "translate";
528
- ids: NodeId[]; /** Total delta in document-space, from gesture start. */
529
- dx: number;
530
- dy: number;
531
- phase: IntentPhase;
532
- } | {
533
- kind: "resize"; /** Member ids of the group being resized (1 or more). */
534
- ids: NodeId[];
535
- anchor: ResizeDirection;
536
- /**
537
- * Target rect in document-space. For `transformed` selections this
538
- * is the AABB of the new shape — preserved so axis-aligned hosts
539
- * that ignore `shape` keep working unchanged.
540
- */
541
- rect: Rect;
542
- /**
543
- * Full target shape. Present whenever the gesture produced one
544
- * (which is always, post-Commit 2 of the affine-first plan).
545
- * Hosts that handle rotated/sheared selections consume this
546
- * directly; legacy hosts can read `rect` and ignore `shape`.
547
- */
548
- shape?: SelectionShape;
549
- phase: IntentPhase;
550
- } | {
551
- kind: "rotate"; /** Member ids of the group being rotated (typically 1). */
552
- ids: NodeId[]; /** Target angle delta in radians (relative to gesture start). */
553
- angle: number;
554
- phase: IntentPhase;
555
- } | {
556
- kind: "marquee_select"; /** Marquee rect in document-space (normalized). */
557
- rect: Rect;
558
- additive: boolean;
559
- phase: IntentPhase;
560
- } | {
561
- kind: "set_endpoint"; /** Subject node id (line-shape selection). */
562
- id: NodeId; /** Which endpoint is being moved. */
563
- endpoint: "p1" | "p2"; /** Target position in document-space. */
564
- pos: cmath.Vector2;
565
- phase: IntentPhase;
566
- } | {
567
- kind: "enter_content_edit";
568
- id: NodeId;
569
- } | {
570
- kind: "cancel_gesture";
571
- };
572
- /** Callback the host implements to receive intents. */
573
- type IntentHandler = (intent: Intent) => void;
574
- //#endregion
575
- //#region event/transform.d.ts
576
- /**
577
- * Surface camera transform: axis-aligned (scale + translate only).
578
- *
579
- * Stored as a `cmath.Transform`:
580
- * `[[sx, 0, tx], [0, sy, ty]]`
581
- *
582
- * Off-diagonal components are ignored; the surface does not support rotation
583
- * or shear at the camera level.
584
- */
585
- type Transform = cmath.Transform;
586
- //#endregion
587
- //#region surface/style.d.ts
588
- /**
589
- * HUD style — colors, sizes, and offsets for the surface-owned chrome.
590
- *
591
- * All fields are optional; defaults follow.
592
- */
593
- interface HUDStyle {
594
- /** Primary chrome color (selection outline, handle border). */
595
- chromeColor: string;
596
- /** Secondary color used for hover outline (lighter than chrome). */
597
- hoverColor: string;
598
- /** Handle visual size in screen-px. */
599
- handleSize: number;
600
- /** Handle fill color. */
601
- handleFill: string;
602
- /** Handle stroke color. */
603
- handleStroke: string;
604
- /** Selection outline stroke width (in screen-px). */
605
- selectionOutlineWidth: number;
606
- /** Hover outline stroke width (in screen-px). Typically thicker than selection. */
607
- hoverOutlineWidth: number;
608
- /** Whether to render rotation handles in addition to resize handles. */
609
- showRotationHandles: boolean;
610
- }
611
- //#endregion
612
- //#region event/hit-regions.d.ts
613
- /**
614
- * Action a UI hit-region triggers when clicked.
615
- *
616
- * Each variant carries a snapshot of the relevant shape state at the time
617
- * the chrome was built — so the surface can start a gesture without an
618
- * extra round-trip to host providers. The chrome builder is the single
619
- * source of truth for "what does this hit region act on?"
620
- *
621
- * - `select_node` — user clicked on a node-representative UI region.
622
- * - `resize_handle` — one of 8 resize regions (4 corner knobs + 4 virtual
623
- * edges). Carries the group's member ids and the group's initial
624
- * `SelectionShape` — `rect` for axis-aligned groups, `transformed` for
625
- * rotated/sheared groups (so resize math runs in the local frame).
626
- * - `rotate_handle` — one of 4 virtual rotation regions outside the group's
627
- * corners. Carries the group's initial `SelectionShape` for center math
628
- * (pivot = center of `shapeBounds(initial_shape)`).
629
- * - `endpoint_handle` — endpoint of a line-shape selection. Carries the
630
- * line's current p1/p2 so dragging is relative to a stable snapshot.
631
- * - `translate_handle` — body region covering a selection group's bbox.
632
- * Pushed under the corner / edge / rotation regions so resize wins on
633
- * overlap. Lets the user grab any part of the selection chrome — including
634
- * transparent corners of a circle's bbox — to start a translate. Hud is
635
- * the event source once present.
636
- */
637
- type OverlayAction = {
638
- kind: "select_node";
639
- id: NodeId;
640
- } | {
641
- kind: "resize_handle";
642
- direction: ResizeDirection;
643
- ids: readonly NodeId[];
644
- initial_shape: SelectionShape;
645
- } | {
646
- kind: "rotate_handle";
647
- corner: RotationCorner;
648
- ids: readonly NodeId[];
649
- initial_shape: SelectionShape;
650
- } | {
651
- kind: "endpoint_handle";
652
- endpoint: "p1" | "p2";
653
- id: NodeId; /** Snapshot of the line endpoints in doc-space at chrome build time. */
654
- p1: [number, number];
655
- p2: [number, number];
656
- } | {
657
- kind: "translate_handle";
658
- ids: readonly NodeId[];
659
- };
660
- //#endregion
661
- //#region event/overlay.d.ts
662
- /**
663
- * Minimum hit-target size in screen-px.
664
- *
665
- * Visual knobs are typically 8px, but the hit region is 16px so users don't
666
- * need pixel-perfect aim. Matches `MIN_HIT_SIZE` in the Rust overlay.
667
- */
668
- declare const MIN_HIT_SIZE = 16;
669
- /**
670
- * Below this selection size (in screen-px on either axis), chrome is
671
- * suppressed — both the visual handles AND the hit regions. Matches
672
- * `MIN_HANDLES_VISIBLE_SIZE` in the Rust overlay.
673
- */
674
- declare const MIN_CHROME_VISIBLE_SIZE = 12;
675
- /**
676
- * A hit region for one overlay element. Always screen-space; either anchored
677
- * to a doc-space point (so the hit region tracks the document under camera
678
- * changes) or expressed as a pre-projected screen-space AABB (for things
679
- * like edge regions whose layout is fundamentally screen-space).
680
- */
681
- type HitShape =
682
- /**
683
- * Fixed screen-space rectangle, centered (or otherwise anchored) on a
684
- * doc-space point. The surface projects `anchor_doc` through the current
685
- * transform each frame; rect dimensions stay constant in CSS px.
686
- */
687
- {
688
- kind: "screen_rect_at_doc";
689
- anchor_doc: cmath.Vector2; /** Screen-space size in CSS px. */
690
- width: number;
691
- height: number; /** Which point of the rect sits on the anchor. Default: "center". */
692
- placement?: "center" | "tl" | "tr" | "bl" | "br";
693
- }
694
- /**
695
- * Screen-space AABB at pre-projected screen coordinates. Used for elements
696
- * whose layout the chrome builder already projected (e.g. edge strips).
697
- */
698
- | {
699
- kind: "screen_aabb";
700
- rect: Rect;
701
- }
702
- /**
703
- * Oriented bounding-box hit shape — an axis-aligned `rect` in a *shadow*
704
- * coordinate space, plus the affine that maps a screen-space pointer
705
- * INTO that space. The hit-test pipeline applies `inverse_transform` to
706
- * the pointer and then tests against `rect` with normal AABB containment.
707
- *
708
- * Used by transformed-chrome zones: the 9-slice runs in an axis-aligned
709
- * shadow rect centered at the chrome's screen center, and a rotation
710
- * (typically around the same center) maps shadow → screen. Storing the
711
- * inverse here lets hit-test stay exact at any rotation/skew without
712
- * inflating to an AABB-of-rotated-corners.
713
- */
714
- | {
715
- kind: "screen_obb";
716
- rect: Rect; /** screen → shadow. Applied to the pointer before AABB containment. */
717
- inverse_transform: cmath.Transform;
718
- };
719
- /**
720
- * Visual representation for one overlay element. Maps directly to the
721
- * primitive layer's `HUDDraw` entries — the surface fans these out into the
722
- * one merged `HUDDraw` it hands to `HUDCanvas.draw()` each frame.
723
- *
724
- * Virtual elements (rotation, side resize) omit `render`.
725
- */
726
- type RenderShape = /** Screen-space sized rect at doc anchor — e.g. resize knob. */{
727
- kind: "screen_rect";
728
- anchor_doc: cmath.Vector2;
729
- width: number;
730
- height: number;
731
- placement?: "center" | "tl" | "tr" | "bl" | "br";
732
- fill?: boolean;
733
- stroke?: boolean;
734
- fillColor?: string;
735
- strokeColor?: string;
736
- /**
737
- * Rotation around the rect's screen-space center, in radians (CCW).
738
- * Defaults to 0. Used for knobs/badges rendered for a transformed
739
- * selection so they rotate with the parent.
740
- */
741
- angle?: number;
742
- } /** Doc-space rect — e.g. selection outline, marquee. */ | {
743
- kind: "doc_rect";
744
- x: number;
745
- y: number;
746
- width: number;
747
- height: number;
748
- stroke?: boolean;
749
- fill?: boolean;
750
- fillOpacity?: number;
751
- dashed?: boolean;
752
- } /** Doc-space line — e.g. line-shape selection outline. */ | {
753
- kind: "doc_line";
754
- x1: number;
755
- y1: number;
756
- x2: number;
757
- y2: number;
758
- dashed?: boolean;
759
- };
760
- /**
761
- * One interactable element of overlay UI.
762
- *
763
- * Pairs (visual, event, action) into a single struct so the discipline of
764
- * "render box ≠ event box" is explicit. The chrome builder emits a list of
765
- * these per frame; the surface fans them into render commands and hit
766
- * regions.
767
- *
768
- * - **Virtual elements** (e.g. rotation handles, edge resize strips) omit
769
- * `render` — they exist only as hit regions.
770
- * - **Padded elements** have `hit` larger than the corresponding `render`
771
- * shape (e.g. 16px hit AABB around an 8px visual knob).
772
- *
773
- * TODO(rotation): when the first base-rotated overlay lands (e.g. an
774
- * in-canvas rotation pip), add an `orientation: "screen" | "base"` field.
775
- * Existing call sites default to `"screen"` and don't change.
776
- */
777
- interface OverlayElement {
778
- /** Stable semantic identifier. Format: `"<kind>[:<param>]"`. Examples:
779
- * `"translate"`, `"resize_handle:nw"`, `"resize_edge:n"`,
780
- * `"rotate:ne"`, `"endpoint:p1"`. Used by tests and debug tooling. */
781
- label: string;
782
- /** Semantic owner for group-level visibility policy. */
783
- group?: HUDSemanticGroup;
784
- action: OverlayAction;
785
- hit: HitShape;
786
- render?: RenderShape;
787
- /** Lower wins. See `HUDHitPriority` in `selection-controls.ts`. */
788
- priority: number;
789
- cursor?: CursorIcon;
790
- }
791
- //#endregion
792
- //#region surface/chrome.d.ts
793
- interface SurfaceChromeGroups {
794
- hover?: HUDSemanticGroup;
795
- selection?: HUDSemanticGroup;
796
- selectionControls?: HUDSemanticGroup;
797
- marquee?: HUDSemanticGroup;
798
- transformPreview?: HUDSemanticGroup;
799
- }
800
- //#endregion
801
- //#region surface/surface.d.ts
802
- interface SurfaceVisibilityContext {
803
- gesture: SurfaceGesture;
804
- }
805
- interface SurfaceVisibility {
806
- hidden?: Iterable<HUDSemanticGroup>;
807
- }
808
- type SurfaceVisibilityPolicy = (context: SurfaceVisibilityContext) => SurfaceVisibility | undefined;
809
- interface SurfaceOptions {
810
- /**
811
- * Content pick. Given a doc-space point, return the topmost node id under
812
- * the pointer, or `null`. Host wraps its scene query however it wants
813
- * (e.g. `elementFromPoint` + `data-id` for SVG-DOM hosts).
814
- */
815
- pick: (point_doc: [number, number]) => NodeId | null;
816
- /**
817
- * Selection shape for a node — what the chrome should wrap. Most nodes
818
- * return `{ kind: "rect", rect }`; vector lines return
819
- * `{ kind: "line", p1, p2 }`.
820
- */
821
- shapeOf: (id: NodeId) => SelectionShape | null;
822
- /** Surface emits intents the host commits. */
823
- onIntent: IntentHandler;
824
- /** Initial style (partial; merged with defaults). */
825
- style?: Partial<HUDStyle>;
826
- /** Initial readonly flag. Default `false`. */
827
- readonly?: boolean;
828
- /** Optional HUDCanvas color override. */
829
- color?: string;
830
- /**
831
- * Optional pixel-grid configuration. Drawn back-most in the HUD canvas
832
- * when `enabled` and the current zoom exceeds `zoomThreshold`. Hosts can
833
- * also call `surface.setPixelGrid(...)` later.
834
- */
835
- pixelGrid?: PixelGridConfig | null;
836
- /**
837
- * Optional semantic groups for surface-owned chrome. The HUD package does
838
- * not define a group vocabulary; hosts pass the strings they want to use.
839
- */
840
- groups?: SurfaceChromeGroups;
841
- /**
842
- * Host-owned visibility policy. Called per frame with the current surface
843
- * gesture; returned groups are filtered from surface chrome and host extras.
844
- */
845
- visibility?: SurfaceVisibilityPolicy;
846
- }
847
- /**
848
- * Top-level wired surface.
849
- *
850
- * Owns an internal `HUDCanvas`, a `SurfaceState` (gesture/hover/...) and
851
- * the host providers. On every `dispatch`, the state machine runs;
852
- * `draw` composes surface chrome + host-fed extras into a single canvas
853
- * paint.
854
- */
855
- declare class Surface {
856
- private hudCanvas;
857
- private state;
858
- private style;
859
- private opts;
860
- private colorOverride;
861
- private width;
862
- private height;
863
- private cursor_renderer;
864
- constructor(canvas: HTMLCanvasElement, options: SurfaceOptions);
865
- /** Configure / disable the back-most pixel-grid layer. */
866
- setPixelGrid(config: PixelGridConfig | null): void;
867
- /**
868
- * Update just the pixel grid's transform. Cheap to call per camera tick.
869
- * No-op when no pixel-grid config is set.
870
- */
871
- setPixelGridTransform(transform: Transform): void;
872
- setSize(w: number, h: number): void;
873
- setTransform(t: Transform): void;
874
- /**
875
- * Push a new selection from the host.
876
- *
877
- * Accepts either:
878
- * - `NodeId[]` — each id becomes its own single-member group, shape
879
- * resolved via `shapeOf(id)` by the chrome builder.
880
- * - `SelectionGroup[]` — pre-computed groups with their union shape.
881
- *
882
- * See `SurfaceState.setSelection` for details.
883
- */
884
- setSelection(input: readonly NodeId[] | readonly SelectionGroup[]): void;
885
- setStyle(partial: Partial<HUDStyle>): void;
886
- /**
887
- * Set or clear the host color override. `null` clears the override and
888
- * lets `style.chromeColor` win on the next paint.
889
- */
890
- setColor(color: string | null): void;
891
- setReadonly(v: boolean): void;
892
- /**
893
- * Set or clear a host-driven hover override.
894
- *
895
- * The surface tracks two hover sources:
896
- * - **Pointer pick** — what scene content is under the cursor (updated
897
- * automatically on `pointer_move`).
898
- * - **Host override** — what the host wants to show as hovered, e.g.
899
- * from a layers panel row mouseenter.
900
- *
901
- * The override (when non-null) wins. `hover()` returns the effective
902
- * value; chrome renders the effective value. Pass `null` to clear and
903
- * fall back to pointer pick.
904
- *
905
- * Returns the same response shape as `dispatch` so the host can react
906
- * to whether anything actually changed.
907
- */
908
- setHoverOverride(id: NodeId | null): SurfaceResponse;
909
- dispose(): void;
910
- dispatch(event: SurfaceEvent): SurfaceResponse;
911
- draw(extra?: HUDDraw): void;
912
- /** Convenience: clear the canvas (e.g. when the host stops the surface). */
913
- clear(): void;
914
- gesture(): SurfaceGesture;
915
- /**
916
- * The effective hover: host override (when set) wins over pointer pick.
917
- * Use this for chrome decisions and host-side reads.
918
- */
919
- hover(): NodeId | null;
920
- cursor(): CursorIcon;
921
- /**
922
- * Resolve the current cursor to a CSS `cursor:` value. Runs the
923
- * installed renderer (or the built-in `cursorToCss` if none installed).
924
- *
925
- * Host wires it like:
926
- *
927
- * const r = surface.dispatch(event);
928
- * if (r.cursorChanged) el.style.cursor = surface.cursorCss();
929
- *
930
- * Saves the host from re-importing `cursorToCss` after every dispatch
931
- * and gives one place to change behavior when a renderer is swapped in.
932
- */
933
- cursorCss(): string;
934
- /**
935
- * Install (or clear) a custom cursor renderer.
936
- *
937
- * `null` restores the built-in `cursorToCss` behavior (native CSS
938
- * keywords for every variant). Pass `cursors.defaultRenderer()` from
939
- * `@grida/hud/cursors` for the bundled SVG cursor set.
940
- *
941
- * Re-callable mid-session; the next `cursorCss()` reads the new value.
942
- */
943
- setCursorRenderer(fn: CursorRenderer | null): void;
944
- modifiers(): Modifiers;
945
- }
946
- //#endregion
947
- export { HUDCanvas as A, HUDRect as B, SurfaceEvent as C, measurementToHUDDraw as D, marqueeToHUDDraw as E, PixelGridConfig as F, HUDScreenRect as H, drawPixelGrid as I, HUDDraw as L, DEFAULT_PIXEL_GRID_COLOR as M, DEFAULT_PIXEL_GRID_STEPS as N, snapGuideToHUDDraw as O, DrawPixelGridParams as P, HUDLine as R, PointerButton as S, lassoToHUDDraw as T, HUDSemantic as U, HUDRule as V, HUDSemanticGroup as W, SurfaceGesture as _, SurfaceVisibilityPolicy as a, Modifiers as b, MIN_CHROME_VISIBLE_SIZE as c, RenderShape as d, HUDStyle as f, Rect as g, SelectMode as h, SurfaceVisibilityContext as i, HUDCanvasOptions as j, filterHUDDrawByGroup as k, MIN_HIT_SIZE as l, IntentPhase as m, SurfaceOptions as n, SurfaceChromeGroups as o, Intent as p, SurfaceVisibility as r, HitShape as s, Surface as t, OverlayElement as u, SelectionGroup as v, SurfaceResponse as w, NO_MODS as x, SelectionShape as y, HUDPolyline as z };