@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.
- package/README.md +54 -53
- package/dist/api/spec.cjs +30 -0
- package/dist/api/spec.d.mts +3 -0
- package/dist/api/spec.d.ts +3 -0
- package/dist/api/spec.mjs +12 -0
- package/dist/chunk-4ETHWSXJ.mjs +25 -0
- package/dist/client/index.cjs +2344 -0
- package/dist/client/index.d.mts +10368 -0
- package/dist/client/index.d.ts +10368 -0
- package/dist/client/index.mjs +2168 -0
- package/dist/clientConfig-WITO4T6L.mjs +12 -0
- package/dist/constants/index.cjs +30 -0
- package/dist/constants/index.d.mts +20 -0
- package/dist/constants/index.d.ts +20 -0
- package/dist/constants/index.mjs +5 -0
- package/dist/design/index.cjs +1523 -0
- package/dist/design/index.d.mts +561 -0
- package/dist/design/index.d.ts +561 -0
- package/dist/design/index.mjs +1468 -0
- package/dist/expo/chunk-3HZFU3PV.mjs +5283 -0
- package/dist/expo/chunk-DFVCS3QQ.mjs +246 -0
- package/dist/{react/chunk-MJJIYFAX.mjs → expo/chunk-GYKJOWEO.mjs} +13 -1
- package/dist/expo/chunk-Y7XT2VNQ.mjs +646 -0
- package/dist/expo/clientConfig-VPOGQRNP.mjs +12 -0
- package/dist/expo/consolidate-HEMQGOUD.mjs +131 -0
- package/dist/expo/index.cjs +16904 -6750
- package/dist/expo/index.d.mts +5755 -1474
- package/dist/expo/index.d.ts +5755 -1474
- package/dist/expo/index.mjs +9718 -6182
- package/dist/expo/recall-MCB3TVBW.mjs +8 -0
- package/dist/index.cjs +2992 -42
- package/dist/index.d.mts +7719 -1835
- package/dist/index.d.ts +7719 -1835
- package/dist/index.mjs +2859 -39
- package/dist/next/index.cjs +6 -2
- package/dist/next/index.d.mts +24 -1
- package/dist/next/index.d.ts +24 -1
- package/dist/next/index.mjs +6 -2
- package/dist/pii/transformers.cjs +126 -0
- package/dist/pii/transformers.d.mts +103 -0
- package/dist/pii/transformers.d.ts +103 -0
- package/dist/pii/transformers.mjs +90 -0
- package/dist/react/chunk-DFVCS3QQ.mjs +246 -0
- package/dist/react/chunk-ELDXIXRE.mjs +5656 -0
- package/dist/{expo/chunk-MJJIYFAX.mjs → react/chunk-GYKJOWEO.mjs} +13 -1
- package/dist/react/chunk-TP6CJZ25.mjs +645 -0
- package/dist/react/clientConfig-VPOGQRNP.mjs +12 -0
- package/dist/react/consolidate-IJH7LD2T.mjs +131 -0
- package/dist/react/index.cjs +25683 -11143
- package/dist/react/index.d.mts +7399 -1393
- package/dist/react/index.d.ts +7399 -1393
- package/dist/react/index.mjs +16709 -9196
- package/dist/react/recall-CSOMIT4H.mjs +8 -0
- package/dist/server/chunk-FMGZTVXQ.mjs +62 -0
- package/dist/server/chunk-G3DC7QBH.mjs +19457 -0
- package/dist/server/chunk-PXN5Q3MD.mjs +643 -0
- package/dist/server/chunk-RCCKRTBI.mjs +230 -0
- package/dist/server/clientConfig-GMADOBPK.mjs +12 -0
- package/dist/server/consolidate-A2ZIX5WF.mjs +131 -0
- package/dist/server/index.cjs +32161 -0
- package/dist/server/index.d.mts +7319 -0
- package/dist/server/index.d.ts +7319 -0
- package/dist/server/index.mjs +11217 -0
- package/dist/server/recall-LFYTTIIY.mjs +8 -0
- package/dist/tools/index.cjs +12682 -34
- package/dist/tools/index.d.mts +2733 -18
- package/dist/tools/index.d.ts +2733 -18
- package/dist/tools/index.mjs +12565 -33
- package/dist/utils/index.cjs +165 -0
- package/dist/utils/index.d.mts +14 -0
- package/dist/utils/index.d.ts +14 -0
- package/dist/utils/index.mjs +145 -0
- package/dist/vercel/index.d.mts +0 -3
- package/dist/vercel/index.d.ts +0 -3
- package/package.json +127 -16
- package/dist/expo/chunk-LJYAMK62.mjs +0 -25
- package/dist/expo/chunk-PJCZO4BQ.mjs +0 -25
- package/dist/expo/clientConfig-2MI4KULF.mjs +0 -10
- package/dist/expo/clientConfig-QDAXFL3W.mjs +0 -10
- package/dist/expo/clientConfig-R4IOW7I2.mjs +0 -10
- package/dist/react/chunk-LJYAMK62.mjs +0 -25
- package/dist/react/chunk-PJCZO4BQ.mjs +0 -25
- package/dist/react/clientConfig-2MI4KULF.mjs +0 -10
- package/dist/react/clientConfig-QDAXFL3W.mjs +0 -10
- package/dist/react/clientConfig-R4IOW7I2.mjs +0 -10
- package/dist/tools/chunk-KDFGY4SK.mjs +0 -13
- 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 };
|