@weasel-js/core 1.4.3 → 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 +367 -0
- package/dist/{pointSnapToGrid-D1bRdq-j.d.ts → autoPoseDescriptor-DF1SnnSx.d.ts} +18 -3
- package/dist/{builtins-CtGORLCG.d.ts → builtins-BXFBXegF.d.ts} +200 -100
- package/dist/{chunk-ORZNKVXM.js → chunk-2VXGHUVL.js} +3 -3
- package/dist/{chunk-ORZNKVXM.js.map → chunk-2VXGHUVL.js.map} +1 -1
- package/dist/{chunk-LEURURX3.js → chunk-BL65SHCX.js} +17 -5
- package/dist/chunk-BL65SHCX.js.map +1 -0
- 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-WYPSGIYS.js → chunk-R3AWPTLZ.js} +1773 -1209
- package/dist/chunk-R3AWPTLZ.js.map +1 -0
- 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/clone.d.ts +2 -2
- package/dist/{geometry-CHR36Ub_.d.ts → geometry-6fCNhAux.d.ts} +1 -1
- package/dist/{grid-Bw8cSde-.d.ts → grid-0Pbn5B2C.d.ts} +1 -14
- package/dist/index.d.ts +231 -74
- package/dist/index.js +5 -5
- package/dist/insert.d.ts +3 -3
- package/dist/insert.js +1 -1
- package/dist/move.d.ts +4 -4
- package/dist/move.js +2 -2
- package/dist/{options-DYfUlZxk.d.ts → options-DbYLImvq.d.ts} +1 -1
- package/dist/{registry-DnoTWGED.d.ts → registry-BY-wI9gm.d.ts} +4 -4
- package/dist/renderer.d.ts +2 -2
- package/dist/renderer.js +5 -5
- package/dist/resize.d.ts +5 -4
- package/dist/resize.js +2 -2
- package/dist/routing.d.ts +5 -5
- package/dist/routing.js +1 -1
- package/dist/{types-CoVTbo_y.d.ts → types-DEALFt5F.d.ts} +1 -6
- package/dist/{types-H7o6MaPo.d.ts → types-bcc7jcUy.d.ts} +96 -14
- package/dist/{types-BJqsTlXl.d.ts → types-ei3UMl9R.d.ts} +1 -1
- package/package.json +9 -9
- package/dist/chunk-LEURURX3.js.map +0 -1
- package/dist/chunk-WYPSGIYS.js.map +0 -1
|
@@ -218,6 +218,23 @@ declare class GLImageCache {
|
|
|
218
218
|
private readonly gl;
|
|
219
219
|
private readonly minification;
|
|
220
220
|
private readonly map;
|
|
221
|
+
/**
|
|
222
|
+
* MAG_FILTER each texture currently carries, so a redundant write can be
|
|
223
|
+
* skipped.
|
|
224
|
+
*
|
|
225
|
+
* The batch sets this per flush — the same bitmap can be drawn at both
|
|
226
|
+
* filters in one frame, and the value has to be right at the draw rather than
|
|
227
|
+
* at upload. Filtering is state on the *texture object*, though, not on the
|
|
228
|
+
* unit, so re-asserting a value it already has is a write to a live texture
|
|
229
|
+
* for no reason. On a large sheet that is not free: a consumer's wall samples
|
|
230
|
+
* a 5652px-square atlas, 122MB resident, and measured `sampling: 'nearest'`
|
|
231
|
+
* costing up to 8x `'linear'` there — where the linear pass re-asserts the
|
|
232
|
+
* upload default and the nearest pass changes state on every draw.
|
|
233
|
+
*
|
|
234
|
+
* Whether that is the cause is unproven (see `docs/TODO.md`), but the write
|
|
235
|
+
* was redundant either way.
|
|
236
|
+
*/
|
|
237
|
+
private readonly magFilters;
|
|
221
238
|
/** `minification` selects the MIN_FILTER strategy for uploaded textures.
|
|
222
239
|
* `'linear'` (default) is the screen path's existing behavior. `'mipmap'`
|
|
223
240
|
* generates mipmaps and filters LINEAR_MIPMAP_LINEAR — required for
|
|
@@ -227,13 +244,21 @@ declare class GLImageCache {
|
|
|
227
244
|
constructor(gl: WebGL2RenderingContext, minification?: ImageMinification);
|
|
228
245
|
upload(key: object, source: TexSource, repetition?: PatternRepetition): WebGLTexture;
|
|
229
246
|
bind(key: object, unit: number): void;
|
|
247
|
+
/** Set `key`'s MAG_FILTER, skipping the call when it already holds that
|
|
248
|
+
* value. Its texture must be bound to the active unit — callers pair this
|
|
249
|
+
* with `bind`. */
|
|
250
|
+
setMagFilter(key: object, filter: number): void;
|
|
230
251
|
}
|
|
231
252
|
|
|
232
253
|
/**
|
|
233
|
-
* CPU gradient-ramp builder + GL
|
|
254
|
+
* CPU gradient-ramp builder + the GL texture every baked ramp lives in.
|
|
234
255
|
*
|
|
235
|
-
* Each unique stop list is
|
|
236
|
-
*
|
|
256
|
+
* Each unique stop list is baked once into a 256-texel strip and written to its
|
|
257
|
+
* own **row** of one RGBA texture, keyed by `JSON.stringify(stops)`. One
|
|
258
|
+
* texture rather than one per ramp is what lets a gradient take a batch texture
|
|
259
|
+
* slot the way a bitmap or a font atlas does: every gradient in a frame samples
|
|
260
|
+
* the same unit, so a run does not break per gradient. The atlas is
|
|
261
|
+
* GL-context-bound; discard and recreate on context loss.
|
|
237
262
|
*
|
|
238
263
|
* Output convention §2: texels are stored as straight RGBA. The fragment
|
|
239
264
|
* shader applies premultiplication before writing outColor.
|
|
@@ -242,25 +267,66 @@ declare class GLImageCache {
|
|
|
242
267
|
/** Bake gradient stops into a 256-entry RGBA lookup strip, which the shader
|
|
243
268
|
* samples instead of evaluating stops per fragment. */
|
|
244
269
|
declare function buildGradientRamp(stops: GradStop[]): Uint8ClampedArray;
|
|
245
|
-
declare class
|
|
270
|
+
declare class GradientRampAtlas {
|
|
246
271
|
private readonly gl;
|
|
247
|
-
|
|
272
|
+
/** Row per stop list, in least-recently-used order: a hit re-inserts, so the
|
|
273
|
+
* first entry is the row a full atlas recycles. */
|
|
274
|
+
private readonly rowByKey;
|
|
275
|
+
private texture;
|
|
276
|
+
private rows;
|
|
277
|
+
/** Mirror of the texture, so growth can respecify it without re-baking every
|
|
278
|
+
* ramp it already holds. */
|
|
279
|
+
private pixels;
|
|
248
280
|
private totalQueries;
|
|
249
281
|
private cacheHits;
|
|
250
282
|
constructor(gl: WebGL2RenderingContext);
|
|
251
|
-
|
|
252
|
-
|
|
283
|
+
/** Rows the atlas currently holds — its texture height. */
|
|
284
|
+
get height(): number;
|
|
285
|
+
/**
|
|
286
|
+
* Bake `stops` into a row and return it, reusing the row an identical stop
|
|
287
|
+
* list already holds.
|
|
288
|
+
*
|
|
289
|
+
* **A returned row outlives the frame only while the atlas has room.** Past
|
|
290
|
+
* `RAMP_ATLAS_MAX_ROWS` an upload recycles the least recently used row, which
|
|
291
|
+
* rewrites texels a row handed out earlier still names. Nothing that defers
|
|
292
|
+
* its draw past this call may hold a row across another `upload` without
|
|
293
|
+
* arranging to be flushed first.
|
|
294
|
+
*/
|
|
295
|
+
upload(stops: GradStop[]): number;
|
|
296
|
+
/**
|
|
297
|
+
* Whether uploading `stops` would move where existing rows sit — by growing
|
|
298
|
+
* the atlas, which changes every row's `v`, or by recycling one, which
|
|
299
|
+
* rewrites its texels.
|
|
300
|
+
*
|
|
301
|
+
* Anything holding a row past this call asks first and gets itself out of
|
|
302
|
+
* the way, because neither can be undone once it has happened.
|
|
303
|
+
*/
|
|
304
|
+
wouldReshape(stops: GradStop[]): boolean;
|
|
305
|
+
/**
|
|
306
|
+
* The `v` a row is sampled at — its center.
|
|
307
|
+
*
|
|
308
|
+
* The center is load-bearing: the atlas filters LINEAR, so a `v` anywhere
|
|
309
|
+
* else blends the ramp beside it into this one. At the center the neighbor's
|
|
310
|
+
* weight is exactly zero.
|
|
311
|
+
*/
|
|
312
|
+
rowV(row: number): number;
|
|
313
|
+
bind(unit: number): void;
|
|
253
314
|
hitRate(): number;
|
|
254
315
|
resetStats(): void;
|
|
255
316
|
/**
|
|
256
|
-
* Delete
|
|
257
|
-
* `WeaselRenderer.dispose()`.
|
|
258
|
-
*
|
|
259
|
-
* `upload()` call and already gone by the time a ramp is cached. The
|
|
260
|
-
* cache is unusable but refillable afterward (stats are left as-is; call
|
|
317
|
+
* Delete the atlas texture and forget every row in it. Called by
|
|
318
|
+
* `WeaselRenderer.dispose()`. The CPU mirror goes with it, so the atlas is
|
|
319
|
+
* unusable but refillable afterward (stats are left as-is; call
|
|
261
320
|
* `resetStats()` separately if desired).
|
|
262
321
|
*/
|
|
263
322
|
free(): void;
|
|
323
|
+
/** The row the next ramp is written to: a free one, a taller atlas, or the
|
|
324
|
+
* least recently used row of a full one. */
|
|
325
|
+
private claimRow;
|
|
326
|
+
/** Grow to `rows`, keeping every row at the index it already had — a row
|
|
327
|
+
* index is a stable name, and callers hold them. */
|
|
328
|
+
private resize;
|
|
329
|
+
private writeRow;
|
|
264
330
|
}
|
|
265
331
|
|
|
266
332
|
/** Row-major 4×5 color matrix identity. */
|
|
@@ -302,33 +368,72 @@ declare class GroupState {
|
|
|
302
368
|
}
|
|
303
369
|
|
|
304
370
|
/**
|
|
305
|
-
* Growable vertex staging for
|
|
371
|
+
* Growable vertex staging for a run of solid-fill geometry, image quads and
|
|
372
|
+
* glyphs.
|
|
306
373
|
*
|
|
307
374
|
* Geometry only: `draw.ts` owns when a run starts, what breaks it, and the
|
|
308
|
-
* uniforms the flush draws under.
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
375
|
+
* uniforms the flush draws under.
|
|
376
|
+
*
|
|
377
|
+
* **One batch for all three, because a page interleaves them.** A grid of
|
|
378
|
+
* thumbnails is a ground rect under an atlas quad under a caption, per cell,
|
|
379
|
+
* and while each kind staged separately every one had to drain the others
|
|
380
|
+
* before it could stage — so a shape that batches perfectly in any one half
|
|
381
|
+
* alone paid a flush per command. Everything a vertex needs to say which it is
|
|
382
|
+
* fits in the same vertex: solids carry the UV of a 1x1 white texel and are
|
|
383
|
+
* their own color, quads carry their atlas UV and a white color, glyphs carry
|
|
384
|
+
* a font atlas UV, their text color, and a paint mode saying the texel is a
|
|
385
|
+
* distance field rather than a color. Gradients join as a fourth off the ramp
|
|
386
|
+
* atlas — a linear one without a mode of its own, since (ramp position, row) is
|
|
387
|
+
* what the plain mode already samples, and a radial or conic one with a mode
|
|
388
|
+
* that says its UV is a gradient-space coordinate to take a `length` or an
|
|
389
|
+
* `atan` of. See `shaders/batchFill.ts`.
|
|
390
|
+
*
|
|
391
|
+
* Colors ride the vertices because shapes in a run differ in color and a merged
|
|
392
|
+
* draw has one set of uniforms — and so does the model transform, applied here
|
|
393
|
+
* rather than uploaded, so shapes under different transforms still share a
|
|
394
|
+
* draw. The texture rides them too, as a slot index into the units the flush
|
|
395
|
+
* binds: slot 0 is the white texel every solid samples, and `draw.ts` hands out
|
|
396
|
+
* the rest per bitmap. A run holds as many bitmaps as there are slots, so an
|
|
397
|
+
* atlas is what makes a wall of one sheet coalesce and a handful of loose
|
|
398
|
+
* bitmaps no longer breaks a run per command.
|
|
313
399
|
*/
|
|
314
400
|
|
|
315
|
-
|
|
401
|
+
/**
|
|
402
|
+
* A gradient vertex's `a_uv`, each channel affine in the coordinates the
|
|
403
|
+
* geometry arrives in: `u = ux*x + uy*y + u0`, and the same for `v`.
|
|
404
|
+
*
|
|
405
|
+
* One shape for all three gradients. A linear one's `v` row is the constant
|
|
406
|
+
* atlas row and its `u` row is the ramp position; a radial or conic one's two
|
|
407
|
+
* rows are the gradient-space coordinate, and the row rides `a_post` instead.
|
|
408
|
+
*/
|
|
409
|
+
interface GradientUV {
|
|
410
|
+
ux: number;
|
|
411
|
+
uy: number;
|
|
412
|
+
u0: number;
|
|
413
|
+
vx: number;
|
|
414
|
+
vy: number;
|
|
415
|
+
v0: number;
|
|
416
|
+
}
|
|
417
|
+
declare class DrawBatch {
|
|
316
418
|
private readonly gl;
|
|
317
419
|
private readonly aPos;
|
|
318
420
|
private readonly aColor;
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
private readonly
|
|
322
|
-
|
|
323
|
-
|
|
421
|
+
private readonly aUv;
|
|
422
|
+
private readonly aPost;
|
|
423
|
+
private readonly aSlot;
|
|
424
|
+
/** One ring per tier, cycled per flush. Slots are created on first use, so a
|
|
425
|
+
* renderer that flushes rarely allocates as few as it flushes. */
|
|
426
|
+
private readonly rings;
|
|
427
|
+
private readonly nextInRing;
|
|
428
|
+
/** The same, for flushes past the largest tier; these sets grow to fit. */
|
|
324
429
|
private readonly largeRing;
|
|
325
430
|
private nextLarge;
|
|
326
431
|
private verts;
|
|
327
432
|
private idx;
|
|
328
433
|
private nVerts;
|
|
329
434
|
private nIdx;
|
|
330
|
-
/** Whether the staged run is
|
|
331
|
-
*
|
|
435
|
+
/** Whether the staged run is quads alone, so its indices are the canonical
|
|
436
|
+
* pattern and a slot already holding that pattern needs no upload. */
|
|
332
437
|
private pureRects;
|
|
333
438
|
constructor(gl: WebGL2RenderingContext, prog: ShaderProgram);
|
|
334
439
|
get length(): number;
|
|
@@ -340,6 +445,62 @@ declare class SolidBatch {
|
|
|
340
445
|
* cover it and the batch draws at `u_model` identity.
|
|
341
446
|
*/
|
|
342
447
|
pushRect(x: number, y: number, w: number, h: number, m: Mat3, r: number, g: number, b: number, a: number): void;
|
|
448
|
+
/**
|
|
449
|
+
* Append one image quad: the destination rect `(x, y, w, h)` mapped through
|
|
450
|
+
* `m`, sampling `(u0, v0)`-`(u1, v1)`, every corner carrying `post` as the
|
|
451
|
+
* after-the-color-matrix alpha factor.
|
|
452
|
+
*
|
|
453
|
+
* Corners wind top-left, top-right, bottom-right, bottom-left with UVs
|
|
454
|
+
* following — the same winding `pushRect` uses, which is what lets a run of
|
|
455
|
+
* mixed rects and quads keep the canonical index pattern. The flips a command
|
|
456
|
+
* asks for are already in the `u` / `v` the caller passes.
|
|
457
|
+
*
|
|
458
|
+
* `slot` is the texture unit the corners sample, which `draw.ts` assigns per
|
|
459
|
+
* bitmap within the run.
|
|
460
|
+
*/
|
|
461
|
+
pushQuad(x: number, y: number, w: number, h: number, m: Mat3, u0: number, v0: number, u1: number, v1: number, post: number, slot: number): void;
|
|
462
|
+
/**
|
|
463
|
+
* Append one glyph quad: the box `(x0, y0)`-`(x1, y1)` mapped through `m`,
|
|
464
|
+
* sampling `(u0, v0)`-`(u1, v1)` of the font atlas at `slot`, painted in
|
|
465
|
+
* `rgba`.
|
|
466
|
+
*
|
|
467
|
+
* `mode` says which channels of that atlas carry the distance field, and
|
|
468
|
+
* rides the vertices packed into the slot — so a run off a baked MSDF atlas
|
|
469
|
+
* and one off the runtime canvas bake still share a draw. The synthetic-bold
|
|
470
|
+
* threshold does not: it is `u_synthBold`, and `draw.ts` breaks the run when
|
|
471
|
+
* it changes.
|
|
472
|
+
*
|
|
473
|
+
* **A synthetic oblique is sheared here rather than in the shader.** The old
|
|
474
|
+
* text program carried the baseline per vertex and skewed against
|
|
475
|
+
* `u_synthItalic`; the batch places its own corners, so the shear is one
|
|
476
|
+
* multiply while they are being placed, and `tanItalic` is 0 for an upright
|
|
477
|
+
* face. It has to happen before `m`, which is where the shader had it too.
|
|
478
|
+
*
|
|
479
|
+
* Corners wind top-left, top-right, bottom-right, bottom-left — `pushQuad`'s
|
|
480
|
+
* winding, which is what lets a run of mixed rects, image quads and glyphs
|
|
481
|
+
* keep the canonical index pattern.
|
|
482
|
+
*/
|
|
483
|
+
pushGlyph(x0: number, y0: number, x1: number, y1: number, baselineY: number, tanItalic: number, m: Mat3, u0: number, v0: number, u1: number, v1: number, r: number, g: number, b: number, a: number, slot: number, mode: number): void;
|
|
484
|
+
/**
|
|
485
|
+
* Append one rect filled by a gradient: the corners of `(x, y, w, h)` mapped
|
|
486
|
+
* through `m`, sampling the ramp atlas at `slot`.
|
|
487
|
+
*
|
|
488
|
+
* `uv` gives each channel of `a_uv` as an affine function of the coordinates
|
|
489
|
+
* the corners arrive in, which is what lets a gradient ride the vertices at
|
|
490
|
+
* all: a linear one's ramp position is affine in position outright, and a
|
|
491
|
+
* radial or conic one's gradient-space *coordinate* is, even though the ramp
|
|
492
|
+
* position it yields is not. Either way the rasterizer's interpolation across
|
|
493
|
+
* a triangle is exact. `mode` says which of the two the fragment shader is
|
|
494
|
+
* looking at, and `post` carries the atlas row for the modes that read it —
|
|
495
|
+
* see `shaders/batchFill.ts`.
|
|
496
|
+
*
|
|
497
|
+
* Values outside 0..1 are the sampler's business; the atlas clamps to the
|
|
498
|
+
* edge texel, which is what the gradient shader's own `clamp` did.
|
|
499
|
+
*/
|
|
500
|
+
pushGradientRect(x: number, y: number, w: number, h: number, m: Mat3, uv: GradientUV, post: number, slot: number, mode: number, r: number, g: number, b: number, a: number): void;
|
|
501
|
+
/** `pushMesh` for a mesh filled by a gradient — see `pushGradientRect` for
|
|
502
|
+
* what `uv`, `post` and `mode` carry. */
|
|
503
|
+
pushGradientMesh(mesh: Mesh, m: Mat3, uv: GradientUV, post: number, slot: number, mode: number, r: number, g: number, b: number, a: number): void;
|
|
343
504
|
/**
|
|
344
505
|
* Append a tessellated mesh through `m`, all vertices carrying `rgba`. The
|
|
345
506
|
* mesh's own indices are rebased onto the staged vertices, which is why the
|
|
@@ -351,6 +512,9 @@ declare class SolidBatch {
|
|
|
351
512
|
uploadAndBind(): number;
|
|
352
513
|
reset(): void;
|
|
353
514
|
dispose(): void;
|
|
515
|
+
private writeVertex;
|
|
516
|
+
/** Two triangles over the four corners just written. */
|
|
517
|
+
private pushQuadIndices;
|
|
354
518
|
/** Grow the CPU arrays so `vertices` / `indices` more fit. */
|
|
355
519
|
private reserve;
|
|
356
520
|
private nextRingSlot;
|
|
@@ -359,67 +523,6 @@ declare class SolidBatch {
|
|
|
359
523
|
private deleteSet;
|
|
360
524
|
}
|
|
361
525
|
|
|
362
|
-
/**
|
|
363
|
-
* Growable vertex staging for consecutive image quads.
|
|
364
|
-
*
|
|
365
|
-
* Geometry only: `draw.ts` owns when a run starts, what breaks it, and the
|
|
366
|
-
* uniforms the flush draws under. The counterpart to `SolidBatch`, and the same
|
|
367
|
-
* two tricks — the model transform is applied here rather than uploaded, so
|
|
368
|
-
* quads under different group transforms still share a draw, and opacity rides
|
|
369
|
-
* the vertices, so quads under different opacities do too. What it cannot
|
|
370
|
-
* absorb is the texture: a batch samples one, which is why an atlas is what
|
|
371
|
-
* makes a large run coalesce at all.
|
|
372
|
-
*
|
|
373
|
-
* Every run is quads, so a slot's index buffer is written once at creation and
|
|
374
|
-
* never again: the pattern for N quads is a prefix of the pattern for any
|
|
375
|
-
* larger N, so the pattern for a slot's capacity serves every flush it takes.
|
|
376
|
-
* `SolidBatch` carries meshes, whose indices are rebased per flush, and has to
|
|
377
|
-
* re-upload; this does not.
|
|
378
|
-
*/
|
|
379
|
-
|
|
380
|
-
declare class ImageBatch {
|
|
381
|
-
private readonly gl;
|
|
382
|
-
private readonly aPos;
|
|
383
|
-
private readonly aUv;
|
|
384
|
-
private readonly aOpacity;
|
|
385
|
-
/** One ring per tier, cycled per flush. Slots are created on first use, so a
|
|
386
|
-
* renderer that flushes rarely allocates as few as it flushes. */
|
|
387
|
-
private readonly rings;
|
|
388
|
-
private readonly nextInRing;
|
|
389
|
-
/** For flushes past the largest tier; these sets grow to fit. */
|
|
390
|
-
private readonly largeRing;
|
|
391
|
-
private nextLarge;
|
|
392
|
-
private verts;
|
|
393
|
-
private nQuads;
|
|
394
|
-
constructor(gl: WebGL2RenderingContext, prog: ShaderProgram);
|
|
395
|
-
/** Indices staged — what a caller passes to `drawElements`. */
|
|
396
|
-
get length(): number;
|
|
397
|
-
get quads(): number;
|
|
398
|
-
/** Whether staging one more quad would put the run past the per-flush cap. */
|
|
399
|
-
wouldOverflow(): boolean;
|
|
400
|
-
/**
|
|
401
|
-
* Append one image quad: the destination rect `(x, y, w, h)` mapped through
|
|
402
|
-
* `m`, sampling `(u0, v0)`-`(u1, v1)`, every corner carrying `opacity`.
|
|
403
|
-
*
|
|
404
|
-
* An affine maps a rect to a parallelogram, so two triangles still cover it
|
|
405
|
-
* and the batch draws at `u_model` identity. Corners wind top-left,
|
|
406
|
-
* top-right, bottom-right, bottom-left, with UVs following — the flips a
|
|
407
|
-
* command asks for are already in the `u`/`v` the caller passes.
|
|
408
|
-
*/
|
|
409
|
-
pushQuad(x: number, y: number, w: number, h: number, m: Mat3, u0: number, v0: number, u1: number, v1: number, opacity: number): void;
|
|
410
|
-
/** Upload the staged geometry into the next set of buffers and bind its VAO.
|
|
411
|
-
* Returns the index count for the caller's `drawElements`. */
|
|
412
|
-
uploadAndBind(): number;
|
|
413
|
-
reset(): void;
|
|
414
|
-
dispose(): void;
|
|
415
|
-
/** Grow the CPU arrays so one more quad fits. */
|
|
416
|
-
private reserve;
|
|
417
|
-
private nextRingSlot;
|
|
418
|
-
private nextLargeSlot;
|
|
419
|
-
private createSet;
|
|
420
|
-
private deleteSet;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
526
|
/** How to construct a `WeaselRenderer`: the GL context or canvas to draw
|
|
424
527
|
* into, the output size, and the quality knobs that separate screen
|
|
425
528
|
* rendering from print. */
|
|
@@ -486,21 +589,21 @@ declare class WeaselRenderer {
|
|
|
486
589
|
private readonly gl;
|
|
487
590
|
private pathFill;
|
|
488
591
|
private pathFillVColor;
|
|
489
|
-
private textSdf;
|
|
490
|
-
private textSdfR8;
|
|
491
592
|
private imageFill;
|
|
492
|
-
private
|
|
593
|
+
private batchFill;
|
|
493
594
|
private gradFill;
|
|
494
595
|
private patternFill;
|
|
495
596
|
private meshCache;
|
|
496
597
|
private textureCache;
|
|
497
598
|
private imageCache;
|
|
498
|
-
private
|
|
599
|
+
private gradRamps;
|
|
499
600
|
private programRegistry;
|
|
500
601
|
private quadVbo;
|
|
501
602
|
private quadIbo;
|
|
502
|
-
private
|
|
503
|
-
|
|
603
|
+
private drawBatch;
|
|
604
|
+
/** 1x1 white, so a batch flush sampling no image still samples something —
|
|
605
|
+
* see `shaders/batchFill.ts`. */
|
|
606
|
+
private whiteTexture;
|
|
504
607
|
private readonly groupState;
|
|
505
608
|
private widthCss;
|
|
506
609
|
private heightCss;
|
|
@@ -560,7 +663,7 @@ declare class WeaselRenderer {
|
|
|
560
663
|
* Scope: built-in shader programs, any consumer-registered programs, the
|
|
561
664
|
* shared quad/rect geometry, any in-flight transient meshes, and the
|
|
562
665
|
* enumerable Map-keyed caches (`GLTextureCache` atlas/image textures,
|
|
563
|
-
* `
|
|
666
|
+
* `GradientRampAtlas`'s ramp texture) ARE freed.
|
|
564
667
|
*
|
|
565
668
|
* NOT freed: `GLImageCache` (bitmap/pattern textures) and `GLMeshCache`'s
|
|
566
669
|
* persistent per-Path mesh cache are keyed by `WeakMap`, not enumerable,
|
|
@@ -596,18 +699,15 @@ declare class WeaselRenderer {
|
|
|
596
699
|
/** @internal */ _gl(): WebGL2RenderingContext;
|
|
597
700
|
/** @internal */ _pathFill(): ShaderProgram;
|
|
598
701
|
/** @internal */ _pathFillVColor(): ShaderProgram;
|
|
599
|
-
/** @internal */
|
|
600
|
-
/** @internal */ _imageBatch(): ImageBatch;
|
|
601
|
-
/** @internal */ _textSdf(): ShaderProgram;
|
|
602
|
-
/** @internal */ _textSdfR8(): ShaderProgram;
|
|
702
|
+
/** @internal */ _drawBatch(): DrawBatch;
|
|
603
703
|
/** @internal */ _imageFill(): ShaderProgram;
|
|
604
|
-
/** @internal */
|
|
704
|
+
/** @internal */ _batchFill(): ShaderProgram;
|
|
605
705
|
/** @internal */ _gradFill(): ShaderProgram;
|
|
606
706
|
/** @internal */ _patternFill(): ShaderProgram;
|
|
607
707
|
/** @internal */ _meshCache(): GLMeshCache;
|
|
608
708
|
/** @internal */ _textureCache(): GLTextureCache;
|
|
609
709
|
/** @internal */ _imageCache(): GLImageCache;
|
|
610
|
-
/** @internal */
|
|
710
|
+
/** @internal */ _gradRamps(): GradientRampAtlas;
|
|
611
711
|
/** @internal */ _groupState(): GroupState;
|
|
612
712
|
/** @internal */ _widthCss(): number;
|
|
613
713
|
/** @internal */ _heightCss(): number;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RECT_ORIGIN_PROJECTION } from './chunk-
|
|
1
|
+
import { RECT_ORIGIN_PROJECTION } from './chunk-WPM42WJP.js';
|
|
2
2
|
import { registerOpFactory, captureSlot, slotFromIndex, resolveSlot } from './chunk-4RJP2N2L.js';
|
|
3
3
|
|
|
4
4
|
// src/core/ops/transform.ts
|
|
@@ -142,5 +142,5 @@ function snap(strategy, opts = {}) {
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
export { createReparentOp, createTransformOp, deleteScratch, getScratch, scratchKey, setScratch, snap };
|
|
145
|
-
//# sourceMappingURL=chunk-
|
|
146
|
-
//# sourceMappingURL=chunk-
|
|
145
|
+
//# sourceMappingURL=chunk-2VXGHUVL.js.map
|
|
146
|
+
//# sourceMappingURL=chunk-2VXGHUVL.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/ops/transform.ts","../src/core/ops/reparent.ts","../src/interactions/scratchKey.ts","../src/interactions/gestures/shared/snap.ts"],"names":[],"mappings":"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","file":"chunk-ORZNKVXM.js","sourcesContent":["import type { Op } from './types';\nimport { registerOpFactory } from './registry';\n\ninterface TransformAdapter<TPose> {\n setPose(id: string, pose: TPose): void;\n}\n\n/** @internal */\ninterface TransformArgs<TPose> {\n id: string;\n from: TPose;\n to: TPose;\n label?: string;\n coalesceKey?: string;\n}\n\n/** Op: set an object's pose, inverting back to `from`. `coalesceKey` defaults to\n * `transform:${id}` so successive pose writes to the same node (drag-move,\n * resize, repeated nudges) merge into one undo entry within the history's\n * coalesce window. Pass an explicit key (or a unique one) to opt out. */\nexport function createTransformOp<TPose>(args: TransformArgs<TPose>): Op {\n const { id, from, to, label, coalesceKey = `transform:${id}` } = args;\n return {\n name: 'transform',\n args: { id, from, to, label, coalesceKey },\n label,\n coalesceKey,\n apply(adapter) {\n // Always call setPose so consumers that inspect op behavior (tests,\n // overlays) see a consistent \"this op writes (id, to)\" signal. When\n // from and to are structurally identical, additionally report a\n // no-op to history so the entry can be skipped from the undo stack.\n // setPose(id, to) is idempotent in this branch — adapters write the\n // same pose they already had.\n (adapter as TransformAdapter<TPose>).setPose(id, to);\n if (poseShallowEqual(from, to)) return false;\n return undefined;\n },\n invert() {\n return createTransformOp<TPose>({ id, from: to, to: from, label, coalesceKey });\n },\n };\n}\n\nregisterOpFactory<TransformArgs<unknown>>('transform', (args) => createTransformOp(args));\n\nfunction poseShallowEqual<TPose>(a: TPose, b: TPose): boolean {\n if (a === b) return true;\n if (a === null || b === null || typeof a !== 'object' || typeof b !== 'object') return false;\n const ka = Object.keys(a as object);\n const kb = Object.keys(b as object);\n if (ka.length !== kb.length) return false;\n for (const k of ka) {\n if ((a as Record<string, unknown>)[k] !== (b as Record<string, unknown>)[k]) return false;\n }\n return true;\n}\n","import type { Op } from './types';\nimport { registerOpFactory } from './registry';\nimport { captureSlot, resolveSlot, slotFromIndex, type OrderedReader, type Slot } from './slot';\n\ninterface ReparentAdapter extends OrderedReader {\n setParent(id: string, parentId: string | null): void;\n /** Sibling-order write, paired with `getChildren`. Optional: an adapter\n * without both silently ignores slots and appends. */\n setChildOrder?(parentId: string | null, ids: string[]): void;\n}\n\n/** @internal */\ninterface ReparentArgs {\n id: string;\n fromParentId: string | null;\n toParentId: string | null;\n /** Sibling ordinal under the old parent. Sugar for `fromSlot: { index }`,\n * and only a seed: `apply` re-observes the node's full slot through the\n * adapter and that observation supersedes this. */\n fromIndex?: number;\n /** Sibling ordinal under the new parent. Sugar for `toSlot: { index }`.\n * Omit to append. */\n toIndex?: number;\n /** Slot under the old parent, anchor included. `invert()` forwards it as\n * the destination slot; without it undo re-parents with no position and\n * the node lands last, quietly changing paint order. */\n fromSlot?: Slot;\n /** Slot under the new parent, anchor included. Omit to append. */\n toSlot?: Slot;\n label?: string;\n coalesceKey?: string;\n}\n\n/** Move `id` to `slot` among `parentId`'s children. No-op when the adapter\n * has no ordering seam, when `id` isn't there, or when it's already in\n * place. */\nfunction placeAt(\n a: ReparentAdapter,\n parentId: string | null,\n id: string,\n slot: Slot,\n): void {\n if (!a.getChildren || !a.setChildOrder) return;\n const current = a.getChildren(parentId);\n const from = current.indexOf(id);\n if (from < 0) return;\n const next = current.slice();\n next.splice(from, 1);\n const resolved = resolveSlot(next, slot);\n const to = resolved === undefined ? next.length : Math.max(0, Math.min(resolved, next.length));\n if (from === to) return;\n next.splice(to, 0, id);\n a.setChildOrder(parentId, next);\n}\n\n/** Present when the caller named a destination at all; `undefined` leaves\n * placement to the adapter. */\nfunction optionalSlot(slot: Slot | undefined, index: number | undefined): Slot | undefined {\n if (slot) return slot;\n return index === undefined ? undefined : slotFromIndex(index);\n}\n\n/**\n * Op: change `id`'s parent and sibling slot, inverting back to the prior\n * parent and slot.\n *\n * `coalesceKey` defaults to `reparent:${id}` so successive reparents of the\n * same id batch-merge cleanly.\n */\nexport function createReparentOp(args: ReparentArgs): Op {\n const {\n id, fromParentId, toParentId,\n label = 'Reparent', coalesceKey = `reparent:${id}`,\n } = args;\n let fromSlot = optionalSlot(args.fromSlot, args.fromIndex);\n const toSlot = optionalSlot(args.toSlot, args.toIndex);\n const argsForSerial: ReparentArgs = {\n id, fromParentId, toParentId, fromSlot, toSlot, label, coalesceKey,\n };\n return {\n name: 'reparent',\n args: argsForSerial,\n label,\n coalesceKey,\n apply(adapter) {\n const a = adapter as ReparentAdapter;\n const observed = captureSlot(a, fromParentId, id);\n if (observed) {\n fromSlot = observed;\n argsForSerial.fromSlot = observed;\n }\n a.setParent(id, toParentId);\n if (toSlot) placeAt(a, toParentId, id, toSlot);\n },\n invert() {\n return createReparentOp({\n id,\n fromParentId: toParentId,\n toParentId: fromParentId,\n fromSlot: toSlot,\n toSlot: fromSlot,\n label,\n coalesceKey,\n });\n },\n };\n}\n\nregisterOpFactory<ReparentArgs>('reparent', (args) => createReparentOp(args));\n","/**\n * Typed keys for the gesture/behavior scratch store.\n *\n * Gestures expose `ctx.scratch: Record<string, unknown>` as a per-gesture\n * mutable bag that behaviors use to stash state across `onDown` / `onMove`\n * / `onEnd` calls. The store is `unknown`-valued because behaviors are\n * tool-agnostic — a single behavior may run under different tools with\n * different scratch shapes. Without a typing convention every read site\n * has to `(ctx.scratch as <some shape>)[KEY]`, and a writer changing the\n * shape can silently break readers with no compile-time signal.\n *\n * `ScratchKey<T>` is a phantom-typed string that carries its payload type\n * via a branded interface. `getScratch` and `setScratch` use the brand to\n * narrow / constrain at the typing layer; at runtime the key is just its\n * underlying string and the store remains a plain `Record`.\n *\n * @example\n * ```ts\n * // Module-level (or behavior-level) constant, shared by writer + reader:\n * const LASSO_VERTICES = scratchKey<readonly { x: number; y: number }[]>('lasso.vertices');\n *\n * // Writer (a tool's onMove):\n * setScratch(ctx.scratch, LASSO_VERTICES, vertices);\n *\n * // Reader (a behavior's onEnd):\n * const vertices = getScratch(ctx.scratch, LASSO_VERTICES) ?? [];\n * // ^? readonly { x: number; y: number }[] | undefined\n * ```\n */\n\n/** Phantom-typed key for a scratch slot. The `__scratchKeyPayload` phantom\n * field carries the value type without occupying any runtime memory. */\nexport interface ScratchKey<T> {\n readonly name: string;\n /** Phantom — never read at runtime. Only present so TypeScript can\n * recover `T` at use sites. */\n readonly __scratchKeyPayload?: (_: T) => T;\n}\n\n/** Compatible scratch store shape. The kit's gesture context's `scratch`\n * field already satisfies this; consumers don't need to construct one. */\nexport type ScratchStore = Record<string, unknown>;\n\n/**\n * Make a typed scratch key. Use module-level constants so the same key\n * is shared by writer and reader.\n *\n * Namespace the name by behavior or feature to avoid collisions\n * (`'behavior.field'` is a good convention). Two keys with the same\n * `name` refer to the same slot at runtime — the type parameter is a\n * compile-time contract, not a uniqueness guarantee.\n */\nexport function scratchKey<T>(name: string): ScratchKey<T> {\n return { name };\n}\n\n/** Read a typed slot from the scratch store. Returns `undefined` if\n * nothing has been written under the key. */\nexport function getScratch<T>(\n store: ScratchStore,\n key: ScratchKey<T>,\n): T | undefined {\n return store[key.name] as T | undefined;\n}\n\n/** Write a value to a typed slot. Overwrites whatever was there. */\nexport function setScratch<T>(\n store: ScratchStore,\n key: ScratchKey<T>,\n value: T,\n): void {\n store[key.name] = value;\n}\n\n/** Remove a slot from the scratch store. Returns `true` if it was set,\n * `false` if it wasn't. */\nexport function deleteScratch<T>(\n store: ScratchStore,\n key: ScratchKey<T>,\n): boolean {\n if (Object.prototype.hasOwnProperty.call(store, key.name)) {\n delete store[key.name];\n return true;\n }\n return false;\n}\n","import type { MoveBehavior, ModifierState, SnapStrategy } from '../types';\nimport {\n RECT_ORIGIN_PROJECTION,\n type OriginProjection,\n} from './strategies/grid';\n\ntype ModKey = keyof ModifierState;\n\n/**\n * Generic snap behavior factory: wraps a `SnapStrategy` and returns a\n * `MoveBehavior` whose `onMove` returns a uniform `GroupTransform`.\n *\n * Implementation:\n * 1. Compute the primary's proposed pose by applying the gesture's\n * `transform` (translate) to `origin.get(primaryId)`.\n * 2. Call `strategy.snap(proposed)`. If null, no-op.\n * 3. Derive the snapped delta via the `OriginProjection`:\n * dx = origin(snapped).x - origin(originPose).x\n * ...so the gesture applies the same delta to every dragged id.\n *\n * The pose-shape-aware delta extraction lives here — gestures stay pose-shape\n * agnostic. Default projection is `{x, y}`-bearing (rect / path / polygon).\n * Pass `origin` for exotic poses.\n */\nexport function snap<TPose>(\n strategy: SnapStrategy<TPose>,\n opts: { bypassKey?: ModKey; origin?: OriginProjection<TPose> } = {},\n): MoveBehavior<TPose> {\n const { bypassKey } = opts;\n const proj: OriginProjection<TPose> = opts.origin\n ?? (RECT_ORIGIN_PROJECTION as unknown as OriginProjection<TPose>);\n return {\n onMove(ctx, transform) {\n if (bypassKey && ctx.modifiers[bypassKey]) return;\n // The new contract: behaviors see a GroupTransform, not a pose. But\n // strategies still operate on poses. Reconstruct the proposed pose\n // by applying the (translate) transform to the primary's origin.\n if (transform.kind !== 'translate') return;\n const primaryId = ctx.draggedIds[0];\n if (primaryId === undefined) return;\n const originPose = ctx.origin.get(primaryId);\n if (originPose === undefined) return;\n const proposed = proj.translate(originPose, transform.dx, transform.dy);\n const snapped = strategy.snap(proposed, ctx);\n if (snapped === null) return;\n const o0 = proj.getOrigin(originPose);\n const o1 = proj.getOrigin(snapped);\n return {\n transform: { kind: 'translate', dx: o1.x - o0.x, dy: o1.y - o0.y },\n };\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/ops/transform.ts","../src/core/ops/reparent.ts","../src/interactions/scratchKey.ts","../src/interactions/gestures/shared/snap.ts"],"names":[],"mappings":"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","file":"chunk-2VXGHUVL.js","sourcesContent":["import type { Op } from './types';\nimport { registerOpFactory } from './registry';\n\ninterface TransformAdapter<TPose> {\n setPose(id: string, pose: TPose): void;\n}\n\n/** @internal */\ninterface TransformArgs<TPose> {\n id: string;\n from: TPose;\n to: TPose;\n label?: string;\n coalesceKey?: string;\n}\n\n/** Op: set an object's pose, inverting back to `from`. `coalesceKey` defaults to\n * `transform:${id}` so successive pose writes to the same node (drag-move,\n * resize, repeated nudges) merge into one undo entry within the history's\n * coalesce window. Pass an explicit key (or a unique one) to opt out. */\nexport function createTransformOp<TPose>(args: TransformArgs<TPose>): Op {\n const { id, from, to, label, coalesceKey = `transform:${id}` } = args;\n return {\n name: 'transform',\n args: { id, from, to, label, coalesceKey },\n label,\n coalesceKey,\n apply(adapter) {\n // Always call setPose so consumers that inspect op behavior (tests,\n // overlays) see a consistent \"this op writes (id, to)\" signal. When\n // from and to are structurally identical, additionally report a\n // no-op to history so the entry can be skipped from the undo stack.\n // setPose(id, to) is idempotent in this branch — adapters write the\n // same pose they already had.\n (adapter as TransformAdapter<TPose>).setPose(id, to);\n if (poseShallowEqual(from, to)) return false;\n return undefined;\n },\n invert() {\n return createTransformOp<TPose>({ id, from: to, to: from, label, coalesceKey });\n },\n };\n}\n\nregisterOpFactory<TransformArgs<unknown>>('transform', (args) => createTransformOp(args));\n\nfunction poseShallowEqual<TPose>(a: TPose, b: TPose): boolean {\n if (a === b) return true;\n if (a === null || b === null || typeof a !== 'object' || typeof b !== 'object') return false;\n const ka = Object.keys(a as object);\n const kb = Object.keys(b as object);\n if (ka.length !== kb.length) return false;\n for (const k of ka) {\n if ((a as Record<string, unknown>)[k] !== (b as Record<string, unknown>)[k]) return false;\n }\n return true;\n}\n","import type { Op } from './types';\nimport { registerOpFactory } from './registry';\nimport { captureSlot, resolveSlot, slotFromIndex, type OrderedReader, type Slot } from './slot';\n\ninterface ReparentAdapter extends OrderedReader {\n setParent(id: string, parentId: string | null): void;\n /** Sibling-order write, paired with `getChildren`. Optional: an adapter\n * without both silently ignores slots and appends. */\n setChildOrder?(parentId: string | null, ids: string[]): void;\n}\n\n/** @internal */\ninterface ReparentArgs {\n id: string;\n fromParentId: string | null;\n toParentId: string | null;\n /** Sibling ordinal under the old parent. Sugar for `fromSlot: { index }`,\n * and only a seed: `apply` re-observes the node's full slot through the\n * adapter and that observation supersedes this. */\n fromIndex?: number;\n /** Sibling ordinal under the new parent. Sugar for `toSlot: { index }`.\n * Omit to append. */\n toIndex?: number;\n /** Slot under the old parent, anchor included. `invert()` forwards it as\n * the destination slot; without it undo re-parents with no position and\n * the node lands last, quietly changing paint order. */\n fromSlot?: Slot;\n /** Slot under the new parent, anchor included. Omit to append. */\n toSlot?: Slot;\n label?: string;\n coalesceKey?: string;\n}\n\n/** Move `id` to `slot` among `parentId`'s children. No-op when the adapter\n * has no ordering seam, when `id` isn't there, or when it's already in\n * place. */\nfunction placeAt(\n a: ReparentAdapter,\n parentId: string | null,\n id: string,\n slot: Slot,\n): void {\n if (!a.getChildren || !a.setChildOrder) return;\n const current = a.getChildren(parentId);\n const from = current.indexOf(id);\n if (from < 0) return;\n const next = current.slice();\n next.splice(from, 1);\n const resolved = resolveSlot(next, slot);\n const to = resolved === undefined ? next.length : Math.max(0, Math.min(resolved, next.length));\n if (from === to) return;\n next.splice(to, 0, id);\n a.setChildOrder(parentId, next);\n}\n\n/** Present when the caller named a destination at all; `undefined` leaves\n * placement to the adapter. */\nfunction optionalSlot(slot: Slot | undefined, index: number | undefined): Slot | undefined {\n if (slot) return slot;\n return index === undefined ? undefined : slotFromIndex(index);\n}\n\n/**\n * Op: change `id`'s parent and sibling slot, inverting back to the prior\n * parent and slot.\n *\n * `coalesceKey` defaults to `reparent:${id}` so successive reparents of the\n * same id batch-merge cleanly.\n */\nexport function createReparentOp(args: ReparentArgs): Op {\n const {\n id, fromParentId, toParentId,\n label = 'Reparent', coalesceKey = `reparent:${id}`,\n } = args;\n let fromSlot = optionalSlot(args.fromSlot, args.fromIndex);\n const toSlot = optionalSlot(args.toSlot, args.toIndex);\n const argsForSerial: ReparentArgs = {\n id, fromParentId, toParentId, fromSlot, toSlot, label, coalesceKey,\n };\n return {\n name: 'reparent',\n args: argsForSerial,\n label,\n coalesceKey,\n apply(adapter) {\n const a = adapter as ReparentAdapter;\n const observed = captureSlot(a, fromParentId, id);\n if (observed) {\n fromSlot = observed;\n argsForSerial.fromSlot = observed;\n }\n a.setParent(id, toParentId);\n if (toSlot) placeAt(a, toParentId, id, toSlot);\n },\n invert() {\n return createReparentOp({\n id,\n fromParentId: toParentId,\n toParentId: fromParentId,\n fromSlot: toSlot,\n toSlot: fromSlot,\n label,\n coalesceKey,\n });\n },\n };\n}\n\nregisterOpFactory<ReparentArgs>('reparent', (args) => createReparentOp(args));\n","/**\n * Typed keys for the gesture/behavior scratch store.\n *\n * Gestures expose `ctx.scratch: Record<string, unknown>` as a per-gesture\n * mutable bag that behaviors use to stash state across `onDown` / `onMove`\n * / `onEnd` calls. The store is `unknown`-valued because behaviors are\n * tool-agnostic — a single behavior may run under different tools with\n * different scratch shapes. Without a typing convention every read site\n * has to `(ctx.scratch as <some shape>)[KEY]`, and a writer changing the\n * shape can silently break readers with no compile-time signal.\n *\n * `ScratchKey<T>` is a phantom-typed string that carries its payload type\n * via a branded interface. `getScratch` and `setScratch` use the brand to\n * narrow / constrain at the typing layer; at runtime the key is just its\n * underlying string and the store remains a plain `Record`.\n *\n * @example\n * ```ts\n * // Module-level (or behavior-level) constant, shared by writer + reader:\n * const LASSO_VERTICES = scratchKey<readonly { x: number; y: number }[]>('lasso.vertices');\n *\n * // Writer (a tool's onMove):\n * setScratch(ctx.scratch, LASSO_VERTICES, vertices);\n *\n * // Reader (a behavior's onEnd):\n * const vertices = getScratch(ctx.scratch, LASSO_VERTICES) ?? [];\n * // ^? readonly { x: number; y: number }[] | undefined\n * ```\n */\n\n/** Phantom-typed key for a scratch slot. The `__scratchKeyPayload` phantom\n * field carries the value type without occupying any runtime memory. */\nexport interface ScratchKey<T> {\n readonly name: string;\n /** Phantom — never read at runtime. Only present so TypeScript can\n * recover `T` at use sites. */\n readonly __scratchKeyPayload?: (_: T) => T;\n}\n\n/** Compatible scratch store shape. The kit's gesture context's `scratch`\n * field already satisfies this; consumers don't need to construct one. */\nexport type ScratchStore = Record<string, unknown>;\n\n/**\n * Make a typed scratch key. Use module-level constants so the same key\n * is shared by writer and reader.\n *\n * Namespace the name by behavior or feature to avoid collisions\n * (`'behavior.field'` is a good convention). Two keys with the same\n * `name` refer to the same slot at runtime — the type parameter is a\n * compile-time contract, not a uniqueness guarantee.\n */\nexport function scratchKey<T>(name: string): ScratchKey<T> {\n return { name };\n}\n\n/** Read a typed slot from the scratch store. Returns `undefined` if\n * nothing has been written under the key. */\nexport function getScratch<T>(\n store: ScratchStore,\n key: ScratchKey<T>,\n): T | undefined {\n return store[key.name] as T | undefined;\n}\n\n/** Write a value to a typed slot. Overwrites whatever was there. */\nexport function setScratch<T>(\n store: ScratchStore,\n key: ScratchKey<T>,\n value: T,\n): void {\n store[key.name] = value;\n}\n\n/** Remove a slot from the scratch store. Returns `true` if it was set,\n * `false` if it wasn't. */\nexport function deleteScratch<T>(\n store: ScratchStore,\n key: ScratchKey<T>,\n): boolean {\n if (Object.prototype.hasOwnProperty.call(store, key.name)) {\n delete store[key.name];\n return true;\n }\n return false;\n}\n","import type { MoveBehavior, ModifierState, SnapStrategy } from '../types';\nimport {\n RECT_ORIGIN_PROJECTION,\n type OriginProjection,\n} from './strategies/grid';\n\ntype ModKey = keyof ModifierState;\n\n/**\n * Generic snap behavior factory: wraps a `SnapStrategy` and returns a\n * `MoveBehavior` whose `onMove` returns a uniform `GroupTransform`.\n *\n * Implementation:\n * 1. Compute the primary's proposed pose by applying the gesture's\n * `transform` (translate) to `origin.get(primaryId)`.\n * 2. Call `strategy.snap(proposed)`. If null, no-op.\n * 3. Derive the snapped delta via the `OriginProjection`:\n * dx = origin(snapped).x - origin(originPose).x\n * ...so the gesture applies the same delta to every dragged id.\n *\n * The pose-shape-aware delta extraction lives here — gestures stay pose-shape\n * agnostic. Default projection is `{x, y}`-bearing (rect / path / polygon).\n * Pass `origin` for exotic poses.\n */\nexport function snap<TPose>(\n strategy: SnapStrategy<TPose>,\n opts: { bypassKey?: ModKey; origin?: OriginProjection<TPose> } = {},\n): MoveBehavior<TPose> {\n const { bypassKey } = opts;\n const proj: OriginProjection<TPose> = opts.origin\n ?? (RECT_ORIGIN_PROJECTION as unknown as OriginProjection<TPose>);\n return {\n onMove(ctx, transform) {\n if (bypassKey && ctx.modifiers[bypassKey]) return;\n // The new contract: behaviors see a GroupTransform, not a pose. But\n // strategies still operate on poses. Reconstruct the proposed pose\n // by applying the (translate) transform to the primary's origin.\n if (transform.kind !== 'translate') return;\n const primaryId = ctx.draggedIds[0];\n if (primaryId === undefined) return;\n const originPose = ctx.origin.get(primaryId);\n if (originPose === undefined) return;\n const proposed = proj.translate(originPose, transform.dx, transform.dy);\n const snapped = strategy.snap(proposed, ctx);\n if (snapped === null) return;\n const o0 = proj.getOrigin(originPose);\n const o1 = proj.getOrigin(snapped);\n return {\n transform: { kind: 'translate', dx: o1.x - o0.x, dy: o1.y - o0.y },\n };\n },\n };\n}\n"]}
|
|
@@ -9,12 +9,24 @@ function DepRegistryProvider({ children }) {
|
|
|
9
9
|
const sourcesRef = useRef(/* @__PURE__ */ new Map());
|
|
10
10
|
const registry = useMemo(() => ({
|
|
11
11
|
register: (name, source) => {
|
|
12
|
-
|
|
12
|
+
const key = name;
|
|
13
|
+
const entry = source;
|
|
14
|
+
const stack = sourcesRef.current.get(key);
|
|
15
|
+
if (stack) stack.push(entry);
|
|
16
|
+
else sourcesRef.current.set(key, [entry]);
|
|
17
|
+
let released = false;
|
|
13
18
|
return () => {
|
|
14
|
-
|
|
19
|
+
if (released) return;
|
|
20
|
+
released = true;
|
|
21
|
+
const cur = sourcesRef.current.get(key);
|
|
22
|
+
if (!cur) return;
|
|
23
|
+
const i = cur.lastIndexOf(entry);
|
|
24
|
+
if (i === -1) return;
|
|
25
|
+
cur.splice(i, 1);
|
|
26
|
+
if (cur.length === 0) sourcesRef.current.delete(key);
|
|
15
27
|
};
|
|
16
28
|
},
|
|
17
|
-
get: (name) => sourcesRef.current.get(name)?.()
|
|
29
|
+
get: (name) => sourcesRef.current.get(name)?.at(-1)?.()
|
|
18
30
|
}), []);
|
|
19
31
|
return /* @__PURE__ */ jsx(DepRegistryContext.Provider, { value: registry, children });
|
|
20
32
|
}
|
|
@@ -557,5 +569,5 @@ function reportRouteConflicts(scopes, warn = (m) => console.warn(m)) {
|
|
|
557
569
|
}
|
|
558
570
|
|
|
559
571
|
export { ActionDisabledReason, ActionsProvider, ActionsScope, DepRegistryProvider, PREDICATE_TARGET, actionBindings, buildDepsFromRequires, buildRouteRegistry, evaluateEnabled, findConflicts, reportRouteConflicts, routeGestureForSpecKind, routesForSpec, useAction, useActionsRegistry, useDepRegistry, useDepSource, useOptionalDepRegistry };
|
|
560
|
-
//# sourceMappingURL=chunk-
|
|
561
|
-
//# sourceMappingURL=chunk-
|
|
572
|
+
//# sourceMappingURL=chunk-BL65SHCX.js.map
|
|
573
|
+
//# sourceMappingURL=chunk-BL65SHCX.js.map
|