@weasel-js/core 1.4.2 → 1.4.4
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/CHANGELOG.md +742 -0
- package/dist/{DrawCommand-Ch8W63oQ.d.ts → DrawCommand-CD-ug3d9.d.ts} +45 -1
- package/dist/{pointSnapToGrid-Dmthv94u.d.ts → autoPoseDescriptor-DF1SnnSx.d.ts} +18 -3
- package/dist/{stroke-Dq8nL4A2.d.ts → builtins-BXFBXegF.d.ts} +251 -102
- package/dist/{chunk-Y5KS66G7.js → chunk-2VXGHUVL.js} +4 -4
- package/dist/{chunk-Y5KS66G7.js.map → chunk-2VXGHUVL.js.map} +1 -1
- package/dist/{chunk-2KKYDDDD.js → chunk-4RJP2N2L.js} +43 -21
- package/dist/chunk-4RJP2N2L.js.map +1 -0
- package/dist/{chunk-TORKRNFY.js → chunk-BL65SHCX.js} +83 -8
- package/dist/chunk-BL65SHCX.js.map +1 -0
- package/dist/{chunk-MXFSHJOM.js → chunk-D4AAWR64.js} +4 -4
- package/dist/{chunk-MXFSHJOM.js.map → chunk-D4AAWR64.js.map} +1 -1
- package/dist/{chunk-ZB7UYJVG.js → chunk-DOBSZOPR.js} +3 -3
- package/dist/{chunk-ZB7UYJVG.js.map → chunk-DOBSZOPR.js.map} +1 -1
- package/dist/{chunk-BLJNRMKB.js → chunk-PRGBGMH3.js} +3 -3
- package/dist/{chunk-BLJNRMKB.js.map → chunk-PRGBGMH3.js.map} +1 -1
- package/dist/{chunk-KIVJXUYE.js → chunk-R3AWPTLZ.js} +2779 -1660
- package/dist/chunk-R3AWPTLZ.js.map +1 -0
- package/dist/{chunk-67KE7SDP.js → chunk-T3UQ3F6R.js} +4 -4
- package/dist/{chunk-67KE7SDP.js.map → chunk-T3UQ3F6R.js.map} +1 -1
- package/dist/{chunk-SBFC6J3C.js → chunk-WPM42WJP.js} +3 -3
- package/dist/{chunk-SBFC6J3C.js.map → chunk-WPM42WJP.js.map} +1 -1
- package/dist/clipboard.d.ts +2 -2
- package/dist/clipboard.js +3 -3
- package/dist/clone.d.ts +2 -2
- package/dist/clone.js +3 -3
- package/dist/{geometry-Dtt_k6Dq.d.ts → geometry-6fCNhAux.d.ts} +3 -3
- package/dist/{grid-nnXU4VjN.d.ts → grid-0Pbn5B2C.d.ts} +1 -14
- package/dist/index.d.ts +330 -288
- package/dist/index.js +9 -9
- package/dist/insert.d.ts +3 -3
- package/dist/insert.js +1 -1
- package/dist/move.d.ts +4 -4
- package/dist/move.js +3 -3
- package/dist/{options-CdFl510T.d.ts → options-DbYLImvq.d.ts} +1 -1
- package/dist/{registry-BwY_DxJM.d.ts → registry-BY-wI9gm.d.ts} +98 -8
- package/dist/renderer.d.ts +32 -4
- package/dist/renderer.js +9 -9
- package/dist/resize.d.ts +5 -4
- package/dist/resize.js +2 -2
- package/dist/routing.d.ts +6 -6
- package/dist/routing.js +1 -1
- package/dist/{types-XBcDEp3Y.d.ts → types-DEALFt5F.d.ts} +10 -6
- package/dist/{types-DIQAisSG.d.ts → types-bcc7jcUy.d.ts} +116 -15
- package/dist/{types-miXHGrZM.d.ts → types-ei3UMl9R.d.ts} +1 -1
- package/package.json +9 -9
- package/dist/chunk-2KKYDDDD.js.map +0 -1
- package/dist/chunk-KIVJXUYE.js.map +0 -1
- package/dist/chunk-TORKRNFY.js.map +0 -1
|
@@ -73,6 +73,31 @@ type NodeId = string & {
|
|
|
73
73
|
};
|
|
74
74
|
/** Brand a string as a NodeId. */
|
|
75
75
|
declare const asNodeId: (s: string) => NodeId;
|
|
76
|
+
/**
|
|
77
|
+
* One dependency, as a derivation reads it: the node itself and the pose it is
|
|
78
|
+
* painted at.
|
|
79
|
+
*
|
|
80
|
+
* The node comes along because a connector legitimately reads more than a box
|
|
81
|
+
* — an edge that thickens with its endpoint's weight, or routes only to nodes
|
|
82
|
+
* on a given layer, is answering off `data` and `layer`. The scene already
|
|
83
|
+
* invalidates dependents on `kit:setData` and `kit:setLayer` for exactly that,
|
|
84
|
+
* so handing over only the pose made the invalidation pay for a read nothing
|
|
85
|
+
* could perform.
|
|
86
|
+
*/
|
|
87
|
+
interface DerivedDep<TPose> {
|
|
88
|
+
node: Node<unknown, string, TPose>;
|
|
89
|
+
/** Its override when it has one, else its own derived pose, else the pose
|
|
90
|
+
* the document stores — `effectivePose`, the same answer the renderer uses. */
|
|
91
|
+
pose: TPose;
|
|
92
|
+
/** The path it derives, or `null` when it derives none. Read it to place
|
|
93
|
+
* something *along* a dependency rather than beside it — a label on a
|
|
94
|
+
* routed edge, a tick on a curve.
|
|
95
|
+
*
|
|
96
|
+
* Resolved on first read and memoized on the dependency, so a route costs
|
|
97
|
+
* the same whether one node reads it or five, and a dependency nobody asks
|
|
98
|
+
* about costs nothing. */
|
|
99
|
+
readonly path: Path | null;
|
|
100
|
+
}
|
|
76
101
|
interface NodeBase<TData, TLayer extends string, TPose> {
|
|
77
102
|
id: NodeId;
|
|
78
103
|
layer: TLayer;
|
|
@@ -81,10 +106,28 @@ interface NodeBase<TData, TLayer extends string, TPose> {
|
|
|
81
106
|
parent: NodeId | null;
|
|
82
107
|
/** Nodes whose poses this node's geometry is computed from. Fixed at add
|
|
83
108
|
* time. Absent or empty means the node's geometry is authored, which is the
|
|
84
|
-
* normal case.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
*
|
|
109
|
+
* normal case.
|
|
110
|
+
*
|
|
111
|
+
* `'children'` means "my own children, in child order" — a container that
|
|
112
|
+
* hugs its contents, which a fixed id list cannot express because
|
|
113
|
+
* reparenting would have to maintain it. The two forms differ in lifetime
|
|
114
|
+
* as well as in membership: deleting a node deletes everything that names
|
|
115
|
+
* it in `dependsOn`, but a container outlives the children it derives
|
|
116
|
+
* from — an emptied group is still a group. */
|
|
117
|
+
dependsOn?: readonly NodeId[] | 'children';
|
|
118
|
+
/** Whether the hit-test walk can return this node. Default `true`; `false`
|
|
119
|
+
* makes it transparent to picking, so a click on it lands on whatever is
|
|
120
|
+
* behind — normally its own container.
|
|
121
|
+
*
|
|
122
|
+
* For content a container owns rather than content the user selects on its
|
|
123
|
+
* own: the label inside a box, a body's rows. Without it the innermost hit
|
|
124
|
+
* wins, and dragging a labeled box moves the label out of the box. It does
|
|
125
|
+
* not hide the node from anything else — it still paints, still clips, and
|
|
126
|
+
* a consumer can still select it by id. */
|
|
127
|
+
pickable?: boolean;
|
|
128
|
+
/** Computes this node's path from its dependencies, in `dependsOn` order —
|
|
129
|
+
* each one the node and the pose it is painted at.
|
|
130
|
+
* A dependency that has been removed arrives as `undefined`.
|
|
88
131
|
* Returning `null` means "nothing to draw right now". Re-evaluated when a
|
|
89
132
|
* dependency's world pose changes, never authored. Absolute-pose `Scene`
|
|
90
133
|
* makes that the dependency's own pose, and an ancestor's move reaches it as
|
|
@@ -92,7 +135,21 @@ interface NodeBase<TData, TLayer extends string, TPose> {
|
|
|
92
135
|
* `node` is deliberately widened: naming `TData`/`TLayer` here puts them in
|
|
93
136
|
* a contravariant position, making `Scene` invariant in both and breaking
|
|
94
137
|
* assignment kit-wide. The cost is that a `derivePath` casts to read `node.data`. */
|
|
95
|
-
derivePath?: (node: Node<unknown, string, TPose>, deps: readonly (TPose | undefined)[]) => Path | null;
|
|
138
|
+
derivePath?: (node: Node<unknown, string, TPose>, deps: readonly (DerivedDep<TPose> | undefined)[]) => Path | null;
|
|
139
|
+
/** Computes this node's pose from its dependencies' poses, the same way
|
|
140
|
+
* `derivePath` computes its path — same `dependsOn` list, same widened
|
|
141
|
+
* `node`, same registry-keyed serialization.
|
|
142
|
+
*
|
|
143
|
+
* Where a derived path is resolved at paint time and reaches only the
|
|
144
|
+
* painter, a derived pose is what the node *is* at: it feeds bounds,
|
|
145
|
+
* hit-testing, selection chrome, snapping and layout, and every reader
|
|
146
|
+
* gets it through `effectivePose`. A pose override still wins over it —
|
|
147
|
+
* that is what lets a drag preview a node the document says is elsewhere.
|
|
148
|
+
*
|
|
149
|
+
* Returning `null` means "I have nothing to derive from right now", and
|
|
150
|
+
* the node falls back to its authored `pose`. `setPose` on a derived node
|
|
151
|
+
* still writes that authored pose; it is simply not what anything reads. */
|
|
152
|
+
derivePose?: (node: Node<unknown, string, TPose>, deps: readonly (DerivedDep<TPose> | undefined)[]) => TPose | null;
|
|
96
153
|
}
|
|
97
154
|
/** A node with no children — a shape, a label, an image. */
|
|
98
155
|
interface LeafNode<TData, TLayer extends string, TPose = RectPose> extends NodeBase<TData, TLayer, TPose> {
|
|
@@ -144,14 +201,18 @@ interface AddNodeSpec<TData, TLayer extends string, TPose = RectPose> {
|
|
|
144
201
|
index?: number;
|
|
145
202
|
/** Explicit id wins over the Scene's `generateId` and the kit default. */
|
|
146
203
|
id?: NodeId;
|
|
204
|
+
/** Mirrors `SceneNode.pickable`. Omit for the default, which is pickable. */
|
|
205
|
+
pickable?: boolean;
|
|
147
206
|
/** Only meaningful when `kind === 'container'`. Attach a clip-path function
|
|
148
207
|
* to the node; ignored for leaves. Mirrors `ContainerNode.clipFromPose`. */
|
|
149
208
|
clipFromPose?: (pose: TPose) => Path | null;
|
|
150
209
|
/** Mirrors `SceneNode.dependsOn`. */
|
|
151
|
-
dependsOn?: readonly NodeId[];
|
|
210
|
+
dependsOn?: readonly NodeId[] | 'children';
|
|
152
211
|
/** Mirrors `SceneNode.derivePath`. Taken as a live function; its registry key is
|
|
153
212
|
* looked up from it, never passed in. */
|
|
154
|
-
derivePath?: (node: Node<unknown, string, TPose>, deps: readonly (TPose | undefined)[]) => Path | null;
|
|
213
|
+
derivePath?: (node: Node<unknown, string, TPose>, deps: readonly (DerivedDep<TPose> | undefined)[]) => Path | null;
|
|
214
|
+
/** Mirrors `SceneNode.derivePose`, on the same terms as `derivePath`. */
|
|
215
|
+
derivePose?: (node: Node<unknown, string, TPose>, deps: readonly (DerivedDep<TPose> | undefined)[]) => TPose | null;
|
|
155
216
|
}
|
|
156
217
|
/** A custom scene mutation registered with `Scene.registerOp`: how to apply
|
|
157
218
|
* it and how to undo it. The pair is what makes it participate in history. */
|
|
@@ -177,13 +238,23 @@ interface AddLayerSpec<TLayer extends string> {
|
|
|
177
238
|
/** Render-stack position. Default: top of stack (highest render index). */
|
|
178
239
|
index?: number;
|
|
179
240
|
}
|
|
241
|
+
/** One layer as it appears in a snapshot. A snapshot written before layer
|
|
242
|
+
* kind was serialized carries neither field, and loads as a system layer —
|
|
243
|
+
* which is what every layer in such a snapshot was. */
|
|
244
|
+
interface SerializedLayer<TLayer extends string> extends SystemLayerSpec<TLayer> {
|
|
245
|
+
kind?: 'system' | 'user';
|
|
246
|
+
/** Present on a user layer, absent on a system one. */
|
|
247
|
+
name?: string;
|
|
248
|
+
}
|
|
180
249
|
/** JSON-serializable shape of a Scene's current state. Produced by
|
|
181
250
|
* `scene.toJSON()`; consumed by `sceneFromJSON()`. Function fields
|
|
182
251
|
* (e.g., `clipFromPose`) appear as string keys (`clipFromPoseKey`) and
|
|
183
252
|
* are resolved through `SceneRegistry` at load time. */
|
|
184
253
|
interface SerializedScene<TData, TLayer extends string, TPose> {
|
|
185
254
|
version: 1;
|
|
186
|
-
|
|
255
|
+
/** The whole layer stack, system and user alike — the name predates user
|
|
256
|
+
* layers and is kept so existing snapshots still parse. */
|
|
257
|
+
systemLayers: readonly SerializedLayer<TLayer>[];
|
|
187
258
|
nodes: readonly SerializedNode<TData, TLayer, TPose>[];
|
|
188
259
|
}
|
|
189
260
|
/** JSON-serializable shape of a single node. Mirrors `AddNodeSpec` but
|
|
@@ -199,10 +270,15 @@ interface SerializedNode<TData, TLayer extends string, TPose> {
|
|
|
199
270
|
/** Registry key for the container's clip-path factory.
|
|
200
271
|
* Containers only; omitted when the container has no clip. */
|
|
201
272
|
clipFromPoseKey?: string;
|
|
202
|
-
/** Ids this node's geometry derives from
|
|
203
|
-
|
|
273
|
+
/** Ids this node's geometry derives from, or `'children'`. Omitted when it
|
|
274
|
+
* derives from nothing. */
|
|
275
|
+
dependsOn?: readonly string[] | 'children';
|
|
276
|
+
/** Mirrors `SceneNode.pickable`. Omitted when the node is pickable. */
|
|
277
|
+
pickable?: boolean;
|
|
204
278
|
/** Registry key for the node's `derivePath` function. Omitted when it has none. */
|
|
205
279
|
derivePathKey?: string;
|
|
280
|
+
/** Registry key for the node's `derivePose` function. Omitted when it has none. */
|
|
281
|
+
derivePoseKey?: string;
|
|
206
282
|
}
|
|
207
283
|
/** Per-scene registry mapping string keys to live function references.
|
|
208
284
|
* Passed to `createScene({ ..., registry })` and `sceneFromJSON(json, { registry })`.
|
|
@@ -211,7 +287,9 @@ interface SceneRegistry<TPose> {
|
|
|
211
287
|
/** Maps registry keys to `clipFromPose` factory functions for container nodes. */
|
|
212
288
|
clipFromPose?: Readonly<Record<string, (pose: TPose) => Path | null>>;
|
|
213
289
|
/** Maps registry keys to `derivePath` functions for nodes with `dependsOn`. */
|
|
214
|
-
derivePath?: Readonly<Record<string, (node: Node<unknown, string, TPose>, deps: readonly (TPose | undefined)[]) => Path | null>>;
|
|
290
|
+
derivePath?: Readonly<Record<string, (node: Node<unknown, string, TPose>, deps: readonly (DerivedDep<TPose> | undefined)[]) => Path | null>>;
|
|
291
|
+
/** Maps registry keys to `derivePose` functions for nodes with `dependsOn`. */
|
|
292
|
+
derivePose?: Readonly<Record<string, (node: Node<unknown, string, TPose>, deps: readonly (DerivedDep<TPose> | undefined)[]) => TPose | null>>;
|
|
215
293
|
}
|
|
216
294
|
/** Options for `useScene` — the layers the scene has, what it starts out
|
|
217
295
|
* holding, and how its history behaves. */
|
|
@@ -264,9 +342,10 @@ interface UseSceneOptions<TData, TLayer extends string, TPose = RectPose> {
|
|
|
264
342
|
*/
|
|
265
343
|
interface PoseOverride<TPose> {
|
|
266
344
|
/** Replaces the node's document pose everywhere the render and hit-test
|
|
267
|
-
* paths read one, including the clip a container derives from its pose
|
|
268
|
-
* Resolved by `effectivePose` — reading
|
|
269
|
-
* paths came to disagree about where a
|
|
345
|
+
* paths read one, including the clip a container derives from its pose, and
|
|
346
|
+
* winning over a `derivePose`. Resolved by `effectivePose` — reading
|
|
347
|
+
* `node.pose` directly is how those paths came to disagree about where a
|
|
348
|
+
* node is. */
|
|
270
349
|
pose?: TPose;
|
|
271
350
|
/** Multiplied into the node's painted alpha, on top of any `alphaFor`. */
|
|
272
351
|
alpha?: number;
|
|
@@ -341,6 +420,15 @@ interface Scene<TData, TLayer extends string, TPose = RectPose> {
|
|
|
341
420
|
* which is what makes it safe to pass a whole selection. Throws if any id is
|
|
342
421
|
* not in the scene; an empty list does nothing and records no step. */
|
|
343
422
|
removeMany(ids: readonly NodeId[]): void;
|
|
423
|
+
/** Every node {@link removeMany} would take if given `ids`: each id, its
|
|
424
|
+
* subtree, everything deriving from any of those, and those nodes' subtrees
|
|
425
|
+
* in turn. The authoritative answer — the cascade relations live here, and
|
|
426
|
+
* a caller reconstructing them from `dependsOn` and `children` drifts the
|
|
427
|
+
* moment a third one is added. Ids not in the scene come back unchanged.
|
|
428
|
+
*
|
|
429
|
+
* A set, not a restore order: a dependent can be reached before the parent
|
|
430
|
+
* it sits under, which is also in the closure. */
|
|
431
|
+
removalClosure(ids: readonly NodeId[]): readonly NodeId[];
|
|
344
432
|
update(id: NodeId, patch: {
|
|
345
433
|
data: TData;
|
|
346
434
|
}): void;
|
|
@@ -356,6 +444,19 @@ interface Scene<TData, TLayer extends string, TPose = RectPose> {
|
|
|
356
444
|
* - **No-op elision** — setting the layer a node already has does nothing
|
|
357
445
|
* and pushes **no** history entry. */
|
|
358
446
|
setLayer(id: NodeId, layer: TLayer): void;
|
|
447
|
+
/** Point `id` at a different set of dependencies — retargeting an edge's end
|
|
448
|
+
* onto another node, or switching a container between an id list and
|
|
449
|
+
* `'children'`. Recorded as one undoable step.
|
|
450
|
+
*
|
|
451
|
+
* Order is significant: a derivation reads its dependencies positionally,
|
|
452
|
+
* so `[b, a]` is not `[a, b]`. Ids need not be in the scene — a declared
|
|
453
|
+
* dependency that appears later invalidates the node when it does, the same
|
|
454
|
+
* as one declared at `add`. Passing `undefined` drops the declaration, after
|
|
455
|
+
* which nothing cascades the node away.
|
|
456
|
+
*
|
|
457
|
+
* **No-op elision** — declaring what the node already declares does nothing
|
|
458
|
+
* and pushes no history entry. */
|
|
459
|
+
setDependsOn(id: NodeId, dependsOn: readonly NodeId[] | 'children' | undefined): void;
|
|
359
460
|
/** Reparent `id` under `parent` (or to a root when `parent` is `null`) at
|
|
360
461
|
* `index` within the new sibling list, appending when `index` is omitted.
|
|
361
462
|
* Siblings are reindexed. Recorded as one undoable step.
|
|
@@ -490,4 +591,4 @@ interface Scene<TData, TLayer extends string, TPose = RectPose> {
|
|
|
490
591
|
getVersion(): number;
|
|
491
592
|
}
|
|
492
593
|
|
|
493
|
-
export { type AddLayerSpec as A, type ContainerNode as C, type LayerRecord as L, type NodeId as N, type
|
|
594
|
+
export { type AddLayerSpec as A, type ContainerNode as C, type DerivedDep as D, type LayerRecord as L, type NodeId as N, type PoseOverrides as P, type RectPose as R, type Scene as S, type UseSceneOptions as U, type Node as a, type SerializedScene as b, type SceneRegistry as c, type RegisteredOp as d, type AddNodeSpec as e, type LeafNode as f, type PoseOverride as g, type SerializedNode as h, type SystemLayerRecord as i, type SystemLayerSpec as j, type UserLayerRecord as k, asNodeId as l };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Op } from '@weasel-js/history';
|
|
2
|
-
import { S as SnapTarget, M as MoveAdapter, I as InsertAdapter } from './types-
|
|
2
|
+
import { S as SnapTarget, M as MoveAdapter, I as InsertAdapter } from './types-DEALFt5F.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Snapshot of modifier-key state at gesture dispatch.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weasel-js/core",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.4",
|
|
4
4
|
"description": "Domain-agnostic 2D scene graph primitives for React: viewport math, drag/resize/insert/clone interactions, layered canvas rendering.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "orochi235",
|
|
@@ -72,16 +72,16 @@
|
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|
|
74
74
|
"react": ">=18",
|
|
75
|
-
"@weasel-js/font": "1.4.
|
|
75
|
+
"@weasel-js/font": "1.4.4"
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
78
|
-
"@weasel-js/cursor": "1.4.
|
|
79
|
-
"@weasel-js/geom": "1.4.
|
|
80
|
-
"@weasel-js/gestures": "1.4.
|
|
81
|
-
"@weasel-js/history": "1.4.
|
|
82
|
-
"@weasel-js/modes": "1.4.
|
|
83
|
-
"@weasel-js/paint": "1.4.
|
|
84
|
-
"@weasel-js/text": "1.4.
|
|
78
|
+
"@weasel-js/cursor": "1.4.4",
|
|
79
|
+
"@weasel-js/geom": "1.4.4",
|
|
80
|
+
"@weasel-js/gestures": "1.4.4",
|
|
81
|
+
"@weasel-js/history": "1.4.4",
|
|
82
|
+
"@weasel-js/modes": "1.4.4",
|
|
83
|
+
"@weasel-js/paint": "1.4.4",
|
|
84
|
+
"@weasel-js/text": "1.4.4",
|
|
85
85
|
"earcut": "2.2.4",
|
|
86
86
|
"polygon-clipping": "^0.15.7"
|
|
87
87
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/ops/registry.ts","../src/core/ops/slot.ts","../src/core/ops/create.ts","../src/core/ops/delete.ts"],"names":[],"mappings":"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","file":"chunk-2KKYDDDD.js","sourcesContent":["// Op-factory registry — pairs a stable `name` (stamped onto each op by its\n// factory) with a `(args) => Op` rebuilder so a `{ name, args }` snapshot\n// can be reconstituted into a live `Op` whose `apply`/`invert` closures\n// behave identically to the original.\n//\n// The kit calls `registerOpFactory` at module-init time for every built-in\n// factory (see `./create.ts`, `./delete.ts`, `./transform.ts`, etc.).\n// Consumers wanting their custom ops to survive `History.serialize()` /\n// `History.restore()` register their factories at app bootstrap, before\n// `createHistory` is called.\n\nimport type { Op } from './types';\n\ntype Builder = (args: unknown) => Op;\n\nconst FACTORIES = new Map<string, Builder>();\n\n/** Register an op-factory builder under a stable name. The builder receives\n * the serialized `args` and must return an `Op` equivalent to one freshly\n * constructed at the original call site (`apply` and `invert` behave the\n * same). Idempotent on the same `(name, build)` pair; a second registration\n * with a different builder silently overwrites — kit modules import each\n * factory exactly once at init, so this never races in practice. */\nexport function registerOpFactory<A>(name: string, build: (args: A) => Op): void {\n FACTORIES.set(name, build as Builder);\n}\n\n/** Rebuild an op from its serialized `(name, args)`. Returns `null` when\n * `name` isn't registered — callers handle this (typically by substituting\n * a no-op placeholder so stack ordering survives across kit version skew). */\nexport function rebuildOp(name: string, args: unknown): Op | null {\n const f = FACTORIES.get(name);\n return f ? f(args) : null;\n}\n\n/** Test-only: drop all registrations. Not exported from the kit barrel — the\n * built-in factories self-register at module init, so production code never\n * needs to clear the table. Exposed for unit tests that want to assert the\n * unknown-name fallback path. */\nexport function _clearOpFactoriesForTest(): void {\n FACTORIES.clear();\n}\n\n/** Every op name currently registered, in registration order. The kit's\n * built-ins self-register at module init, so importing the barrel is enough\n * to see all of them; consumer factories appear once their module has run. */\nexport function registeredOpNames(): readonly string[] {\n return [...FACTORIES.keys()];\n}\n","/**\n * A node's position among its siblings, recorded so an op can put it back.\n *\n * Recorded as an anchor (`before`) plus an ordinal (`index`). The anchor is\n * the source of truth: `History` replays a batch's inverses in reverse order,\n * so an ordinal captured before the mutation is replayed against a list that\n * has not been refilled yet and clamps to the end, while \"put this back before\n * `e`\" stays true however much else has already moved. The ordinal is the\n * fallback for the cases the anchor cannot cover — an adapter with no\n * `getChildren` seam, or an anchor that is itself gone by replay time.\n */\n\n/** Sibling-order read seam. Optional everywhere: a flat adapter has none. */\nexport interface OrderedReader {\n getChildren?(parentId: string | null): string[];\n}\n\nexport interface Slot {\n /** Ordinal among the siblings. `-1` means \"nowhere in particular\". */\n index: number;\n /** Id of the sibling that followed at capture time; `null` when the node\n * was last. Absent when nothing observed it — `null` survives JSON and\n * `undefined` does not, so the two stay distinguishable across a\n * serialize/restore round trip. */\n before?: string | null;\n}\n\n/** Observe `id`'s slot among `parentId`'s children. `null` when the adapter\n * has no ordering seam or `id` isn't there — callers keep whatever weaker\n * slot they already had rather than overwriting it with a guess. */\nexport function captureSlot(\n a: OrderedReader,\n parentId: string | null,\n id: string,\n): Slot | null {\n const siblings = a.getChildren?.(parentId);\n if (!siblings) return null;\n const i = siblings.indexOf(id);\n if (i < 0) return null;\n return { index: i, before: i + 1 < siblings.length ? siblings[i + 1]! : null };\n}\n\n/**\n * Index `slot` denotes in `siblings`, which must NOT contain the node being\n * placed. `undefined` means \"no position\" — the caller appends, or lets the\n * adapter's default placement stand.\n */\nexport function resolveSlot(\n siblings: readonly string[] | undefined,\n slot: Slot,\n): number | undefined {\n if (slot.before === null) return undefined;\n if (slot.before !== undefined && siblings) {\n const at = siblings.indexOf(slot.before);\n if (at >= 0) return at;\n }\n return slot.index >= 0 ? slot.index : undefined;\n}\n\n/** Normalize the `index` sugar every op factory accepts into a `Slot`. */\nexport function slotFromIndex(index: number | undefined): Slot {\n return { index: index ?? -1 };\n}\n\n/** The parent a node claims. Nodes in a flat adapter have none. */\nexport function parentOf(node: object): string | null {\n return ((node as { parent?: unknown }).parent ?? null) as string | null;\n}\n","import type { Op } from './types';\nimport { createDeleteOp } from './delete';\nimport { registerOpFactory } from './registry';\nimport { parentOf, resolveSlot, slotFromIndex, type OrderedReader, type Slot } from './slot';\n\ninterface InsertAdapter<TNode> extends OrderedReader {\n /** When `index` is supplied, insert at that position; adapters that ignore\n * the second arg (or don't expose it) fall back to their default\n * placement (typically append-to-end). */\n insertNode(node: TNode, index?: number): void;\n}\n\n/** Type alias for ops produced by `createInsertOp`. Carries no extra type info today;\n * exists so consumers can name the op type when needed. */\nexport type InsertOp = Op;\n\n/** @internal */\ninterface InsertArgs<TNode extends { id: string }> {\n node: TNode;\n label?: string;\n /** Sibling ordinal to insert at. Sugar for `slot: { index }` — the weaker\n * of the two forms, and all a fresh-insert callsite can know. */\n index?: number;\n /** Full slot, anchor included. Produced only by `captureSlot` (via a\n * delete op's `invert`), and supersedes `index` when present. */\n slot?: Slot;\n}\n\n/** Op: insert `node` into the scene; inverts to a delete of the same id. */\nexport function createInsertOp<TNode extends { id: string }>(args: InsertArgs<TNode>): InsertOp {\n const { node, label } = args;\n const slot: Slot = args.slot ?? slotFromIndex(args.index);\n return {\n name: 'insert',\n args: { node, label, slot },\n label,\n apply(adapter) {\n const a = adapter as InsertAdapter<TNode>;\n const parentId = parentOf(node);\n a.insertNode(node, resolveSlot(a.getChildren?.(parentId), slot));\n },\n invert() {\n return createDeleteOp({ node, label, slot });\n },\n };\n}\n\nregisterOpFactory<InsertArgs<{ id: string }>>('insert', (args) => createInsertOp(args));\n","import type { Op } from './types';\nimport { createInsertOp } from './create';\nimport { registerOpFactory } from './registry';\nimport { captureSlot, parentOf, slotFromIndex, type OrderedReader, type Slot } from './slot';\n\ninterface DeleteAdapter<TNode> extends OrderedReader {\n removeNode(id: string): void;\n /** Subtree reads, used to snapshot descendants before the cascade. Optional\n * — a flat adapter has neither, and a delete there is a single node. */\n getNode?(id: string): TNode | undefined;\n}\n\ninterface InsertAdapter<TNode> {\n insertNode(node: TNode, index?: number): void;\n getNode?(id: string): TNode | undefined;\n}\n\n/** A captured node plus its slot in its own parent's child list. */\ninterface Placed<TNode> {\n node: TNode;\n index: number;\n}\n\n/** @internal */\ninterface DeleteArgs<TNode extends { id: string }> {\n node: TNode;\n label?: string;\n /** Sibling ordinal the node occupies at the moment of delete. Sugar for\n * `slot: { index }`, and only a seed: `apply` re-observes the node's full\n * slot through the adapter and that observation supersedes this. Kept\n * because it is the whole story for a flat adapter with no `getChildren`,\n * where nothing can be observed. */\n index?: number;\n /** Full slot, anchor included. Written by `apply`; supersedes `index`. */\n slot?: Slot;\n /** Descendants of `node`, preorder (parents before children). `removeNode`\n * cascades the whole subtree, so an inverse that re-inserts `node` alone\n * brings the container back empty and drops every child on the floor.\n * Captured on apply; mirrored into the serialized args so a rebuilt op\n * can invert without having run. */\n descendants?: Placed<TNode>[];\n}\n\n/** Walk `rootId`'s descendants preorder. Returns `null` when the adapter\n * can't enumerate children, which leaves any previously captured snapshot\n * in place rather than clobbering it with an empty one. */\nfunction captureDescendants<TNode extends { id: string }>(\n a: DeleteAdapter<TNode>,\n rootId: string,\n): Placed<TNode>[] | null {\n const { getChildren, getNode } = a;\n if (!getChildren || !getNode) return null;\n const out: Placed<TNode>[] = [];\n const walk = (parentId: string): void => {\n const kids = getChildren.call(a, parentId);\n for (let i = 0; i < kids.length; i++) {\n const child = getNode.call(a, kids[i]);\n if (!child) continue;\n out.push({ node: child, index: i });\n walk(kids[i]);\n }\n };\n walk(rootId);\n return out;\n}\n\n/** Op: remove `node` and its subtree from the scene; inverts to a re-insert\n * of the whole subtree at its captured slots. */\nexport function createDeleteOp<TNode extends { id: string }>(args: DeleteArgs<TNode>): Op {\n const { node, label } = args;\n let slot: Slot = args.slot ?? slotFromIndex(args.index);\n const argsForSerial: DeleteArgs<TNode> = { node, label, slot, descendants: args.descendants };\n let captured: Placed<TNode>[] = args.descendants ?? [];\n return {\n name: 'delete',\n args: argsForSerial,\n label,\n apply(adapter) {\n const a = adapter as DeleteAdapter<TNode>;\n const observed = captureSlot(a, parentOf(node), node.id);\n if (observed) {\n slot = observed;\n argsForSerial.slot = observed;\n }\n const snapshot = captureDescendants(a, node.id);\n if (snapshot) {\n captured = snapshot;\n argsForSerial.descendants = snapshot;\n }\n a.removeNode(node.id);\n },\n invert() {\n const subtree = captured;\n const restoredSlot = slot;\n const reinsert = createInsertOp<TNode>({ node, label, slot: restoredSlot });\n if (subtree.length === 0) return reinsert;\n return {\n label,\n apply(adapter) {\n reinsert.apply(adapter);\n const a = adapter as InsertAdapter<TNode>;\n for (const entry of subtree) {\n // A flat adapter's removeNode doesn't cascade, so its children\n // were never gone — re-inserting them would duplicate them.\n if (a.getNode?.(entry.node.id)) continue;\n a.insertNode(entry.node, entry.index);\n }\n },\n invert: () =>\n createDeleteOp<TNode>({ node, label, slot: restoredSlot, descendants: subtree }),\n };\n },\n };\n}\n\nregisterOpFactory<DeleteArgs<{ id: string }>>('delete', (args) => createDeleteOp(args));\n"]}
|