@anuma/sdk 1.0.0 → 1.1.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.
Files changed (87) hide show
  1. package/README.md +54 -53
  2. package/dist/api/spec.cjs +30 -0
  3. package/dist/api/spec.d.mts +3 -0
  4. package/dist/api/spec.d.ts +3 -0
  5. package/dist/api/spec.mjs +12 -0
  6. package/dist/chunk-4ETHWSXJ.mjs +25 -0
  7. package/dist/client/index.cjs +2344 -0
  8. package/dist/client/index.d.mts +10368 -0
  9. package/dist/client/index.d.ts +10368 -0
  10. package/dist/client/index.mjs +2168 -0
  11. package/dist/clientConfig-WITO4T6L.mjs +12 -0
  12. package/dist/constants/index.cjs +30 -0
  13. package/dist/constants/index.d.mts +20 -0
  14. package/dist/constants/index.d.ts +20 -0
  15. package/dist/constants/index.mjs +5 -0
  16. package/dist/design/index.cjs +1523 -0
  17. package/dist/design/index.d.mts +561 -0
  18. package/dist/design/index.d.ts +561 -0
  19. package/dist/design/index.mjs +1468 -0
  20. package/dist/expo/chunk-3HZFU3PV.mjs +5283 -0
  21. package/dist/expo/chunk-DFVCS3QQ.mjs +246 -0
  22. package/dist/{react/chunk-MJJIYFAX.mjs → expo/chunk-GYKJOWEO.mjs} +13 -1
  23. package/dist/expo/chunk-Y7XT2VNQ.mjs +646 -0
  24. package/dist/expo/clientConfig-VPOGQRNP.mjs +12 -0
  25. package/dist/expo/consolidate-HEMQGOUD.mjs +131 -0
  26. package/dist/expo/index.cjs +16904 -6750
  27. package/dist/expo/index.d.mts +5755 -1474
  28. package/dist/expo/index.d.ts +5755 -1474
  29. package/dist/expo/index.mjs +9718 -6182
  30. package/dist/expo/recall-MCB3TVBW.mjs +8 -0
  31. package/dist/index.cjs +2992 -42
  32. package/dist/index.d.mts +7719 -1835
  33. package/dist/index.d.ts +7719 -1835
  34. package/dist/index.mjs +2859 -39
  35. package/dist/next/index.cjs +6 -2
  36. package/dist/next/index.d.mts +24 -1
  37. package/dist/next/index.d.ts +24 -1
  38. package/dist/next/index.mjs +6 -2
  39. package/dist/pii/transformers.cjs +126 -0
  40. package/dist/pii/transformers.d.mts +103 -0
  41. package/dist/pii/transformers.d.ts +103 -0
  42. package/dist/pii/transformers.mjs +90 -0
  43. package/dist/react/chunk-DFVCS3QQ.mjs +246 -0
  44. package/dist/react/chunk-ELDXIXRE.mjs +5656 -0
  45. package/dist/{expo/chunk-MJJIYFAX.mjs → react/chunk-GYKJOWEO.mjs} +13 -1
  46. package/dist/react/chunk-TP6CJZ25.mjs +645 -0
  47. package/dist/react/clientConfig-VPOGQRNP.mjs +12 -0
  48. package/dist/react/consolidate-IJH7LD2T.mjs +131 -0
  49. package/dist/react/index.cjs +25683 -11143
  50. package/dist/react/index.d.mts +7399 -1393
  51. package/dist/react/index.d.ts +7399 -1393
  52. package/dist/react/index.mjs +16709 -9196
  53. package/dist/react/recall-CSOMIT4H.mjs +8 -0
  54. package/dist/server/chunk-FMGZTVXQ.mjs +62 -0
  55. package/dist/server/chunk-G3DC7QBH.mjs +19457 -0
  56. package/dist/server/chunk-PXN5Q3MD.mjs +643 -0
  57. package/dist/server/chunk-RCCKRTBI.mjs +230 -0
  58. package/dist/server/clientConfig-GMADOBPK.mjs +12 -0
  59. package/dist/server/consolidate-A2ZIX5WF.mjs +131 -0
  60. package/dist/server/index.cjs +32161 -0
  61. package/dist/server/index.d.mts +7319 -0
  62. package/dist/server/index.d.ts +7319 -0
  63. package/dist/server/index.mjs +11217 -0
  64. package/dist/server/recall-LFYTTIIY.mjs +8 -0
  65. package/dist/tools/index.cjs +12682 -34
  66. package/dist/tools/index.d.mts +2733 -18
  67. package/dist/tools/index.d.ts +2733 -18
  68. package/dist/tools/index.mjs +12565 -33
  69. package/dist/utils/index.cjs +165 -0
  70. package/dist/utils/index.d.mts +14 -0
  71. package/dist/utils/index.d.ts +14 -0
  72. package/dist/utils/index.mjs +145 -0
  73. package/dist/vercel/index.d.mts +0 -3
  74. package/dist/vercel/index.d.ts +0 -3
  75. package/package.json +127 -16
  76. package/dist/expo/chunk-LJYAMK62.mjs +0 -25
  77. package/dist/expo/chunk-PJCZO4BQ.mjs +0 -25
  78. package/dist/expo/clientConfig-2MI4KULF.mjs +0 -10
  79. package/dist/expo/clientConfig-QDAXFL3W.mjs +0 -10
  80. package/dist/expo/clientConfig-R4IOW7I2.mjs +0 -10
  81. package/dist/react/chunk-LJYAMK62.mjs +0 -25
  82. package/dist/react/chunk-PJCZO4BQ.mjs +0 -25
  83. package/dist/react/clientConfig-2MI4KULF.mjs +0 -10
  84. package/dist/react/clientConfig-QDAXFL3W.mjs +0 -10
  85. package/dist/react/clientConfig-R4IOW7I2.mjs +0 -10
  86. package/dist/tools/chunk-KDFGY4SK.mjs +0 -13
  87. package/dist/tools/clientConfig-RMDOT5YM.mjs +0 -10
@@ -0,0 +1,561 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ /**
4
+ * Anuma JSX AST — the in-memory model for slides and app mockups.
5
+ *
6
+ * Every artifact is a tree of `AnumaNode` values rooted at an `<Anuma.Deck>`
7
+ * (slides) or `<Anuma.Screen>` (apps — Stage 6+). There are no typed
8
+ * per-tag interfaces; the tree is tag-agnostic. Consumers walk the tree
9
+ * and dispatch on `node.tag`.
10
+ *
11
+ * Vocabulary (checked at parse time):
12
+ * - Containers: `<Anuma.Deck>`, `<Anuma.Slide>`, `<Anuma.Group>`.
13
+ * - Leaves: `<Anuma.Text>`, `<Anuma.Image>`, `<Anuma.Rect>`, `<Anuma.Circle>`,
14
+ * `<Anuma.Line>`, `<Anuma.Icon>`.
15
+ *
16
+ * All coordinates are container-relative pixels — the slide canvas is
17
+ * 960×540 px, any child's `x`/`y`/`w`/`h` are pixels measured from the
18
+ * parent's top-left. Containers may opt into a flex layout with
19
+ * `layout="row" | "column"` plus `gap` / `padding` / `justify` / `align`;
20
+ * children inside a flex container skip `x`/`y` and flow instead.
21
+ *
22
+ * Numeric attrs are JSX expressions (`x={96}`), strings are quoted
23
+ * literals, booleans use `{true}`/`{false}` (or bare `hidden` for true).
24
+ * Dynamic expressions (`{someVar}`) are rejected in this version.
25
+ *
26
+ * @module tools/slides/jsx
27
+ */
28
+ /** Scalar value an attribute can hold. */
29
+ type AttrScalar = string | number | boolean;
30
+ /**
31
+ * An object-valued attribute — used primarily for `style={{}}`. Keys are
32
+ * CSS property names in camelCase (`fontSize`, `borderRadius`, …); values
33
+ * are scalars.
34
+ */
35
+ type AttrObject = Record<string, AttrScalar>;
36
+ type AttrValue = AttrScalar | AttrObject;
37
+ type AnumaChild = AnumaNode | string;
38
+ /**
39
+ * A node in the Anuma tree. `tag` is the local name after `Anuma.` (e.g.
40
+ * `"Text"`, `"Slide"`). Children are other nodes for containers, or a
41
+ * single string (the body text) for `<Anuma.Text>`.
42
+ */
43
+ interface AnumaNode {
44
+ tag: string;
45
+ attrs: Record<string, AttrValue>;
46
+ children: AnumaChild[];
47
+ }
48
+
49
+ /**
50
+ * Pure AST-mutation helpers for the design sandbox. The interaction
51
+ * layer determines INTENT (move this absolute element by [dx,dy], drop
52
+ * this flex child at index N) and these helpers produce the next
53
+ * AnumaNode tree. No DOM access here.
54
+ */
55
+
56
+ type LayoutMode = "flex-row" | "flex-column" | "absolute";
57
+ /** Tags that act as containers (not selectable / draggable themselves). */
58
+ declare const NON_SELECTABLE_TAGS: ReadonlySet<string>;
59
+ /**
60
+ * Determine how a node is positioned within its parent. A node whose
61
+ * parent has a `layout` attr of "row" or "column" is a flex child.
62
+ * Anything else (or top-level) is absolute.
63
+ */
64
+ declare function getLayoutModeOf(deck: AnumaNode, childId: string): LayoutMode;
65
+ /** True if the node's parent uses a flex layout. */
66
+ declare function isFlexChild(deck: AnumaNode, childId: string): boolean;
67
+ /**
68
+ * Where a dragged element will land on release. Computed each pointer
69
+ * move from the element under the cursor (via elementsFromPoint) and
70
+ * the target container's layout mode.
71
+ * - flex: insert at `dropIndex` in target's children, strip x/y.
72
+ * - absolute: insert into target with x/y/w/h in target's local
73
+ * space. We carry w/h here (not just x/y) because flex children
74
+ * don't store an explicit width — flex stretches them along the
75
+ * cross axis. When such a child becomes absolute it needs explicit
76
+ * dimensions or it collapses to content size; the caller measures
77
+ * the element's current visual gBCR and passes those in.
78
+ */
79
+ type DropTarget = {
80
+ kind: "flex";
81
+ parentId: string;
82
+ dropIndex: number;
83
+ } | {
84
+ kind: "absolute";
85
+ parentId: string;
86
+ x: number;
87
+ y: number;
88
+ w: number;
89
+ h: number;
90
+ };
91
+ /** Layout mode of a container node based on its `layout` attr. */
92
+ declare function containerLayoutMode(node: AnumaNode): LayoutMode;
93
+ /**
94
+ * Commit a drag. Handles every case in one function so the caller
95
+ * doesn't have to branch on same-parent vs. cross-parent vs. mode-flip:
96
+ * - flex → flex (same parent): reorder within parent
97
+ * - flex → flex (different parent): move + reindex
98
+ * - flex → absolute: move + add x/y at landing position
99
+ * - absolute → absolute (same parent): just update x/y
100
+ * - absolute → absolute (different parent): move + update x/y
101
+ * - absolute → flex: move + strip x/y, reindex
102
+ */
103
+ declare function commitDrop(deck: AnumaNode, elementId: string, target: DropTarget): AnumaNode;
104
+ /** Bounds in slide-px, in the element's parent's local coordinate space. */
105
+ type ResizeBounds = {
106
+ x: number;
107
+ y: number;
108
+ w: number;
109
+ h: number;
110
+ };
111
+ /** Eight cardinal/intercardinal handle positions around a selected element. */
112
+ type ResizeHandle = "nw" | "n" | "ne" | "e" | "se" | "s" | "sw" | "w";
113
+ /**
114
+ * Direction of each handle in slide-px space:
115
+ * dx = +1 if the handle is on the east edge, -1 west, 0 along x.
116
+ * dy = +1 if the handle is on the south edge, -1 north, 0 along y.
117
+ */
118
+ declare const RESIZE_HANDLE_DIRS: Record<ResizeHandle, {
119
+ dx: -1 | 0 | 1;
120
+ dy: -1 | 0 | 1;
121
+ }>;
122
+ /** CSS cursor value for each handle, so the user gets the right grab affordance. */
123
+ declare const RESIZE_HANDLE_CURSORS: Record<ResizeHandle, string>;
124
+ /**
125
+ * Commit a resize.
126
+ * - Absolute children: write x/y/w/h (full resize freedom).
127
+ * - Flex column children: write h only. The container's layout
128
+ * determines position and the cross-axis (width) auto-stretches;
129
+ * writing an explicit w would lock the child to its current
130
+ * stretched width and break that auto-fill behavior the next time
131
+ * the container resizes.
132
+ * - Flex row children: symmetrically, write w only.
133
+ *
134
+ * The interaction layer paired with this only exposes the main-axis
135
+ * handle (s for column, e for row) on flex children, so the cross-axis
136
+ * dimension never gets touched in practice. This commit shape is the
137
+ * defensive backstop in case future call sites take a different path.
138
+ */
139
+ declare function commitResize(deck: AnumaNode, elementId: string, bounds: ResizeBounds): AnumaNode;
140
+ /**
141
+ * Commit a rotation. Writes `attrs.rotation` (degrees). The runtime
142
+ * applies it as `transform: rotate(...)` regardless of layout mode,
143
+ * so this works identically for absolute and flex children.
144
+ */
145
+ declare function commitRotation(deck: AnumaNode, elementId: string, rotation: number): AnumaNode;
146
+ /**
147
+ * True if all the given ids are absolute children of the same parent
148
+ * (and that parent isn't a flex container). This is the V0 condition
149
+ * for multi-drag: translating N elements by the same delta only makes
150
+ * sense when they share a coordinate frame (same parent's local space)
151
+ * and have x/y to translate (absolute, not flex). Mixed-parent or
152
+ * flex-included selections fall back to single-element drag.
153
+ */
154
+ declare function canMultiDrag(deck: AnumaNode, ids: string[]): boolean;
155
+ /**
156
+ * Translate every element in `elementIds` by `delta` (in their parent's
157
+ * local slide-px). Used for multi-drag commits. Elements without
158
+ * explicit attrs.x/y (flex children) are silently skipped — they
159
+ * shouldn't be in the set anyway per the canMultiDrag gate, but the
160
+ * defensive skip keeps a stray flex child from breaking the commit.
161
+ */
162
+ declare function commitMultiDrag(deck: AnumaNode, elementIds: readonly string[], delta: {
163
+ x: number;
164
+ y: number;
165
+ }): AnumaNode;
166
+ /**
167
+ * Replace a Text node's children with a single string. The runtime
168
+ * stores text as one or more string children of an `<Anuma.Text>`
169
+ * node; we collapse to a single string on commit (which preserves
170
+ * line breaks in the string itself).
171
+ *
172
+ * No-op if the node isn't found, or if the supplied text matches the
173
+ * existing content (avoids spurious AST diffs / re-renders for
174
+ * commits triggered by blur with no actual change).
175
+ */
176
+ declare function commitTextChange(deck: AnumaNode, elementId: string, newText: string): AnumaNode;
177
+ /**
178
+ * Read the plain-text content of a node by joining its string children.
179
+ * Non-string children (nested AnumaNodes) are skipped; for simple Text
180
+ * elements that's the entire content.
181
+ */
182
+ declare function getTextContent(node: AnumaNode): string;
183
+
184
+ /**
185
+ * For a flex parent, compute which insertion slot the pointer is over.
186
+ * Reads child rects from the DOM (gBCR).
187
+ *
188
+ * Returns the index in `parent.children` AFTER removing the dragged
189
+ * child (so 0 = before all remaining siblings, N = after all of them).
190
+ */
191
+ declare function computeFlexDropTarget(stage: HTMLElement, parentId: string, draggedId: string, axis: "row" | "column", pointerClient: {
192
+ x: number;
193
+ y: number;
194
+ }, scale: number): {
195
+ index: number;
196
+ bounds: {
197
+ x: number;
198
+ y: number;
199
+ w: number;
200
+ h: number;
201
+ } | null;
202
+ };
203
+ /**
204
+ * Find the deepest container under the cursor that's not the dragged
205
+ * element or one of its descendants. Uses `elementsFromPoint` so the
206
+ * walk respects visual layering (the dragged element's translate makes
207
+ * it the topmost hit; we skip it and fall through).
208
+ */
209
+ declare function findContainerAt(stage: HTMLElement, draggedId: string, pointerClient: {
210
+ x: number;
211
+ y: number;
212
+ }): HTMLElement | null;
213
+ /**
214
+ * Resolve the cursor into a DropTarget. The container under the cursor
215
+ * decides the drop's kind: a flex container produces a flex drop with
216
+ * a computed insertion index; an absolute container produces an
217
+ * absolute drop with x/y/w/h in container-local slide-px.
218
+ *
219
+ * For absolute drops, w/h come from the AST's intrinsic dims (not
220
+ * gBCR) — gBCR is the AABB and would inflate dimensions for rotated
221
+ * elements, accumulating scale across drops.
222
+ */
223
+ declare function resolveDropTarget(deck: AnumaNode, stage: HTMLElement, draggedId: string, pointerClient: {
224
+ x: number;
225
+ y: number;
226
+ }, scale: number): {
227
+ drop: DropTarget;
228
+ indicatorBounds: {
229
+ x: number;
230
+ y: number;
231
+ w: number;
232
+ h: number;
233
+ } | null;
234
+ } | null;
235
+
236
+ /**
237
+ * Walk the click path and return the first descendant of `stage` that
238
+ * carries a `data-id` and a non-container `data-anuma-tag`.
239
+ * `composedPath` works in both light and shadow DOM.
240
+ */
241
+ declare function findElementIdFromEvent(e: {
242
+ nativeEvent: Event;
243
+ } | Event, root: HTMLElement | null): string | null;
244
+ /**
245
+ * Walk the click path and return the resize-handle direction (if any)
246
+ * the pointer landed on. Handles carry `data-resize-handle="<dir>"`.
247
+ * Pointer-down on a handle is unambiguously a resize; check this
248
+ * BEFORE the element-selection walk.
249
+ */
250
+ declare function findResizeHandleFromEvent(e: PointerEvent): ResizeHandle | null;
251
+ /** True if the pointer-down hit the rotate handle (`data-rotate-handle="true"`). */
252
+ declare function findRotateHandleFromEvent(e: PointerEvent): boolean;
253
+ /**
254
+ * Find every selectable element whose CENTER falls within the given
255
+ * client-px rect. Used by marquee selection.
256
+ *
257
+ * Center-in-rect (rather than rect-overlaps-element) matches Figma:
258
+ * dragging the marquee over a sliver of an element doesn't select it,
259
+ * but dragging across its center does.
260
+ */
261
+ declare function findElementsInRect(stage: HTMLElement, rect: {
262
+ left: number;
263
+ right: number;
264
+ top: number;
265
+ bottom: number;
266
+ }): string[];
267
+
268
+ /**
269
+ * Axis-aligned bounds of an element relative to the stage origin, in
270
+ * slide-px. Edges + centers so we can pair-test all 9 alignment
271
+ * candidates per axis (left↔left, left↔center, left↔right, ...).
272
+ */
273
+ type SnapBounds = {
274
+ left: number;
275
+ right: number;
276
+ top: number;
277
+ bottom: number;
278
+ cx: number;
279
+ cy: number;
280
+ };
281
+ /**
282
+ * A snap line to render. `axis: 'x'` is a vertical line at slide-x =
283
+ * `position`; `axis: 'y'` is horizontal at slide-y = `position`. The
284
+ * `start`/`end` range is along the OTHER axis, chosen to cover the
285
+ * dragged group + the matched sibling.
286
+ */
287
+ type SnapGuide = {
288
+ axis: "x" | "y";
289
+ position: number;
290
+ start: number;
291
+ end: number;
292
+ };
293
+ /** Unrotated bounds + rotation of an element, in slide-px. */
294
+ type SelectionBounds = {
295
+ x: number;
296
+ y: number;
297
+ w: number;
298
+ h: number;
299
+ rotation: number;
300
+ };
301
+ /** SelectionBounds plus the element's id. Used for multi-selection lists. */
302
+ type IdBounds = SelectionBounds & {
303
+ id: string;
304
+ };
305
+ /**
306
+ * In-flight gesture state. Phases:
307
+ * - 'pending': pointer is down but hasn't crossed the drag threshold.
308
+ * `elementId === null` means the pointer-down landed on empty
309
+ * canvas; promotion takes us to 'marquee'. Non-null elementId
310
+ * promotes to 'dragging'.
311
+ * - 'marquee': empty-canvas drag is now sweeping a selection rect.
312
+ * - 'dragging': element drag is active. Drop target is recomputed
313
+ * each pointer-move from what's under the cursor.
314
+ * - 'resizing': pointer-down was on a resize handle.
315
+ * - 'rotating': pointer-down was on the rotate handle.
316
+ */
317
+ type Gesture = {
318
+ phase: "pending";
319
+ elementId: string | null;
320
+ startClient: {
321
+ x: number;
322
+ y: number;
323
+ };
324
+ shiftKey: boolean;
325
+ } | {
326
+ phase: "marquee";
327
+ startClient: {
328
+ x: number;
329
+ y: number;
330
+ };
331
+ currentClient: {
332
+ x: number;
333
+ y: number;
334
+ };
335
+ additive: boolean;
336
+ initialSelection: ReadonlySet<string>;
337
+ } | {
338
+ phase: "dragging";
339
+ elementId: string;
340
+ elementIds: string[];
341
+ startClient: {
342
+ x: number;
343
+ y: number;
344
+ };
345
+ delta: {
346
+ x: number;
347
+ y: number;
348
+ };
349
+ drop: DropTarget | null;
350
+ indicatorBounds: {
351
+ x: number;
352
+ y: number;
353
+ w: number;
354
+ h: number;
355
+ } | null;
356
+ startGroupBounds: SnapBounds | null;
357
+ guides: SnapGuide[];
358
+ } | {
359
+ phase: "resizing";
360
+ elementId: string;
361
+ handle: ResizeHandle;
362
+ startClient: {
363
+ x: number;
364
+ y: number;
365
+ };
366
+ startBounds: ResizeBounds;
367
+ startCenter: {
368
+ x: number;
369
+ y: number;
370
+ };
371
+ rotation: number;
372
+ layoutMode: "absolute" | "flex-row" | "flex-column";
373
+ currentBounds: ResizeBounds;
374
+ } | {
375
+ phase: "rotating";
376
+ elementId: string;
377
+ centerClient: {
378
+ x: number;
379
+ y: number;
380
+ };
381
+ startAngle: number;
382
+ startRotation: number;
383
+ currentRotation: number;
384
+ };
385
+
386
+ type CanvasOverlaysProps = {
387
+ /** Current gesture (drives marquee/guides/drop-indicator visibility). */
388
+ gesture: Gesture | null;
389
+ /** Single primary selection (when 1 selected). Null for 0 or >1. */
390
+ selectedId: string | null;
391
+ /** Full selection (drives multi-outline rendering). */
392
+ selectedIds: ReadonlySet<string>;
393
+ /** Bounds of the primary selection. Provided by useSelectionBounds. */
394
+ selectionBounds: SelectionBounds | null;
395
+ /** Bounds of every selected element. Provided by useMultiSelectionBounds. */
396
+ multiBounds: IdBounds[];
397
+ /** Editing target id (suppresses overlays so the edit overlay is unobstructed). */
398
+ editingId?: string | null;
399
+ /** AST root — needed to resolve the selected element's layout mode. */
400
+ deck: AnumaNode;
401
+ /** Slide-px → client-px scale. */
402
+ scale: number;
403
+ };
404
+ /**
405
+ * Renders all canvas overlays: selection outline + handles, multi-
406
+ * selection outlines, marquee, snap guides, flex drop indicator.
407
+ *
408
+ * Drawn in slide-px and scaled with the stage. Sits OUTSIDE the
409
+ * scaled stage in the same wrapper, so its coords are post-scale
410
+ * (multiplied by `scale` here). The marquee uses `position: fixed`
411
+ * since gesture coords are clientX/Y (viewport-relative).
412
+ */
413
+ declare function CanvasOverlays({ gesture, selectedId, selectedIds, selectionBounds, multiBounds, editingId, deck, scale, }: CanvasOverlaysProps): react_jsx_runtime.JSX.Element;
414
+
415
+ /**
416
+ * Read an element's gBCR-derived bounds in slide-px relative to the
417
+ * stage origin. `scale` converts client-px → slide-px (the AST stores
418
+ * positions in slide-px).
419
+ */
420
+ declare function getStageRelativeBounds(el: HTMLElement, stage: HTMLElement, scale: number): SnapBounds;
421
+ /** Outer envelope of multiple bounds — the dragged group's AABB. */
422
+ declare function unionBounds(arr: readonly SnapBounds[]): SnapBounds | null;
423
+ declare function computeSnap(start: SnapBounds, rawDelta: {
424
+ x: number;
425
+ y: number;
426
+ }, others: readonly SnapBounds[], thresholdSlide: number): {
427
+ delta: {
428
+ x: number;
429
+ y: number;
430
+ };
431
+ guides: SnapGuide[];
432
+ };
433
+
434
+ type UseCanvasGesturesOpts = {
435
+ stageRef: React.RefObject<HTMLElement | null>;
436
+ deck: AnumaNode;
437
+ setDeck: (updater: AnumaNode | ((d: AnumaNode) => AnumaNode)) => void;
438
+ /** Slide-px to client-px scale. Pointer math divides client deltas by this. */
439
+ scale: number;
440
+ /** Suppress all gestures (e.g. magic-select active in slide editor). */
441
+ disabled?: boolean;
442
+ /** Controlled selection. When omitted, the hook manages internal state. */
443
+ selectedIds?: ReadonlySet<string>;
444
+ onSelectionChange?: (ids: ReadonlySet<string>) => void;
445
+ /** Called on double-click of a Text element. Host enters edit mode. */
446
+ onTextDoubleClick?: (elementId: string) => void;
447
+ /**
448
+ * Predicate for "should we ignore this pointer-down?" — used by hosts
449
+ * with their own overlays (e.g. the text-edit contenteditable). When
450
+ * true, the gesture handler bails immediately.
451
+ */
452
+ shouldIgnorePointerDown?: (e: React.PointerEvent<HTMLElement>) => boolean;
453
+ /**
454
+ * Called synchronously inside `onPointerDown` BEFORE any gesture
455
+ * starts, after `shouldIgnorePointerDown` returned false. Hosts use
456
+ * this to commit a pending text edit (so click-elsewhere ends the
457
+ * edit and starts a fresh gesture in the same event).
458
+ */
459
+ beforePointerDown?: () => void;
460
+ };
461
+ type UseCanvasGesturesReturn = {
462
+ selectedIds: ReadonlySet<string>;
463
+ /** Lone selected id when size===1, else null. */
464
+ selectedId: string | null;
465
+ setSelectedIds: (ids: ReadonlySet<string> | ((prev: ReadonlySet<string>) => ReadonlySet<string>)) => void;
466
+ gesture: Gesture | null;
467
+ /** Deck with in-flight resize/rotate patched for live rendering. */
468
+ renderedDeck: AnumaNode;
469
+ onPointerDown: (e: React.PointerEvent<HTMLElement>) => void;
470
+ onPointerMove: (e: React.PointerEvent<HTMLElement>) => void;
471
+ onPointerUp: (e: React.PointerEvent<HTMLElement>) => void;
472
+ onPointerCancel: (e: React.PointerEvent<HTMLElement>) => void;
473
+ onDoubleClick: (e: React.MouseEvent<HTMLElement>) => void;
474
+ };
475
+ /**
476
+ * Pointer-driven gesture state machine for an Anuma canvas.
477
+ *
478
+ * Handles: click-to-select, shift-click multi-select, marquee
479
+ * selection, drag (single + multi, cross-parent + flex-reorder),
480
+ * resize (8-handle for absolute, main-axis-only for flex), rotate,
481
+ * snap-to-edges, double-click-to-edit-text.
482
+ *
483
+ * Per-phase transition logic lives in `./transitions.ts`; this hook
484
+ * is the React glue (state, handlers, effects).
485
+ *
486
+ * Host responsibilities:
487
+ * - Render the slide tree (`renderedDeck`) into a stage element.
488
+ * - Wire `onPointerDown/Move/Up/Cancel` on a wrapper around the
489
+ * stage (the stage itself has `transform: scale()` which would
490
+ * distort pointer math).
491
+ * - Wire `onDoubleClick` on the same wrapper.
492
+ * - Render selection / handles / marquee / guides overlays
493
+ * (typically via `<CanvasOverlays>`).
494
+ */
495
+ declare function useCanvasGestures(opts: UseCanvasGesturesOpts): UseCanvasGesturesReturn;
496
+
497
+ type UseCanvasKeyboardOpts = {
498
+ setDeck: (updater: AnumaNode | ((d: AnumaNode) => AnumaNode)) => void;
499
+ selectedIds: ReadonlySet<string>;
500
+ setSelectedIds: (ids: ReadonlySet<string>) => void;
501
+ /** Suppress all shortcuts (e.g. while inline-editing text). */
502
+ disabled?: boolean;
503
+ /** Optional undo/redo. When omitted, Cmd+Z is a no-op. */
504
+ undo?: () => void;
505
+ redo?: () => void;
506
+ };
507
+ declare function useCanvasKeyboard(opts: UseCanvasKeyboardOpts): void;
508
+
509
+ type UseDeckHistoryReturn = {
510
+ deck: AnumaNode;
511
+ setDeck: (updater: AnumaNode | ((d: AnumaNode) => AnumaNode)) => void;
512
+ undo: () => void;
513
+ redo: () => void;
514
+ canUndo: boolean;
515
+ canRedo: boolean;
516
+ undoCount: number;
517
+ redoCount: number;
518
+ };
519
+ /**
520
+ * Drop-in replacement for `useState<AnumaNode>` that records every
521
+ * commit on an undo stack. Gesture handlers don't change — they still
522
+ * call `setDeck(d => commitX(d, ...))`. The hook intercepts each
523
+ * commit to push the new tree onto a history array.
524
+ *
525
+ * Forking on a commit truncates the redo branch — standard editor
526
+ * convention. If the updater returns the same reference it received
527
+ * (no-op commit), no snapshot is recorded.
528
+ *
529
+ * Limitations (V0):
530
+ * - Each commit is its own undo step. Holding shift+arrow nudges
531
+ * creates one entry per keypress; coalescing into a single drag
532
+ * entry is future polish.
533
+ * - History is per-mount. No persistence across page reloads.
534
+ */
535
+ declare function useDeckHistory(initial: AnumaNode): UseDeckHistoryReturn;
536
+
537
+ /**
538
+ * Read the unrotated bounds + rotation of the selected element, in
539
+ * slide-px relative to the stage origin.
540
+ *
541
+ * Recovery from gBCR: the runtime rotates around the element's center
542
+ * (transform-origin: 50% 50%), so AABB.center == element.center. We
543
+ * take the center from gBCR and the intrinsic w/h from `attrs`. The
544
+ * unrotated top-left is then center − (w/2, h/2). Falls back to gBCR
545
+ * dims for elements with no explicit attrs.w/h (auto-stretched flex
546
+ * children).
547
+ *
548
+ * useLayoutEffect (not useEffect) so the rect update runs between
549
+ * React's commit and the browser paint — without it, after a gesture
550
+ * commit there's one frame where the AST has updated but `bounds` is
551
+ * still stale (visible as the outline flickering at the old position).
552
+ */
553
+ declare function useSelectionBounds(stageRef: React.RefObject<HTMLElement | null>, selectedId: string | null, deck: AnumaNode, scale: number): SelectionBounds | null;
554
+ /**
555
+ * Bounds for every element in `selectedIds`. Same recovery logic as
556
+ * useSelectionBounds, just iterated. Returns `{ id, ...SelectionBounds }`
557
+ * per id so callers can render an outline per selection.
558
+ */
559
+ declare function useMultiSelectionBounds(stageRef: React.RefObject<HTMLElement | null>, selectedIds: ReadonlySet<string>, deck: AnumaNode, scale: number): IdBounds[];
560
+
561
+ export { CanvasOverlays, type CanvasOverlaysProps, type DropTarget, type Gesture, type IdBounds, type LayoutMode, NON_SELECTABLE_TAGS, RESIZE_HANDLE_CURSORS, RESIZE_HANDLE_DIRS, type ResizeBounds, type ResizeHandle, type SelectionBounds, type SnapBounds, type SnapGuide, type UseCanvasGesturesOpts, type UseCanvasGesturesReturn, type UseCanvasKeyboardOpts, type UseDeckHistoryReturn, canMultiDrag, commitDrop, commitMultiDrag, commitResize, commitRotation, commitTextChange, computeFlexDropTarget, computeSnap, containerLayoutMode, findContainerAt, findElementIdFromEvent, findElementsInRect, findResizeHandleFromEvent, findRotateHandleFromEvent, getLayoutModeOf, getStageRelativeBounds, getTextContent, isFlexChild, resolveDropTarget, unionBounds, useCanvasGestures, useCanvasKeyboard, useDeckHistory, useMultiSelectionBounds, useSelectionBounds };