@weasel-js/core 1.4.0 → 1.4.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.
@@ -1,4 +1,4 @@
1
- import { ActionDisabledReason, useOptionalDepRegistry, DepRegistryProvider, useActionsRegistry, ActionsProvider, useDepRegistry, reportRouteConflicts, buildDepsFromRequires, useDepSource, actionBindings } from './chunk-UTIFY2UU.js';
1
+ import { ActionDisabledReason, useOptionalDepRegistry, DepRegistryProvider, useActionsRegistry, ActionsProvider, useDepRegistry, reportRouteConflicts, buildDepsFromRequires, useDepSource, actionBindings } from './chunk-TORKRNFY.js';
2
2
  import { scratchKey, createTransformOp, snap, getScratch, createReparentOp, setScratch } from './chunk-Y5KS66G7.js';
3
3
  import { fixedCornerOf, DEFAULT_RESIZE_BEHAVIORS, CORNER_ANCHORS, cornerPoint } from './chunk-BLJNRMKB.js';
4
4
  import { AUTO_POSE_DESCRIPTOR, resolveUnit, polylineFromPoints, starPath, regularPolygonPath, linePath, ellipsePath, rectPath, polygonFromPoints, PATH_M, PATH_L, PATH_Z, RECT_POSE_DESCRIPTOR, pathPoseDescriptor, withinPxRadius, boundsOfPath, PATH_Q, PATH_C, DEFAULT_FLATTEN_TOLERANCE, flattenQuadratic, flattenCubic, PathBuilder, pointInPath, strokeHitTest, transformPath, translatePath, pxExtent, remapRotatedLeaf, withinPxBox, flattenCubicWithArcLen, flattenQuadraticWithArcLen, PATH_CMD_LENGTHS, pointToGridCell } from './chunk-SBFC6J3C.js';
@@ -25,7 +25,7 @@ import { createHistory } from '@weasel-js/history';
25
25
  export * from '@weasel-js/history';
26
26
 
27
27
  // src/version.ts
28
- var VERSION = "1.4.0" ;
28
+ var VERSION = "1.4.1" ;
29
29
 
30
30
  // src/features/grid/roundToCell.ts
31
31
  function roundToCell(value, cellSize) {
@@ -258,6 +258,44 @@ void main() {
258
258
  }
259
259
  `
260
260
  );
261
+ var IMAGE_VOPACITY_VERT_SRC = (
262
+ /* glsl */
263
+ `#version 300 es
264
+ in vec2 a_position;
265
+ in vec2 a_uv;
266
+ in float a_opacity;
267
+ uniform mat3 u_proj;
268
+ uniform mat3 u_model;
269
+ out vec2 v_uv;
270
+ out float v_opacity;
271
+ void main() {
272
+ vec3 screen = u_model * vec3(a_position, 1.0);
273
+ vec3 clip = u_proj * vec3(screen.xy, 1.0);
274
+ gl_Position = vec4(clip.xy, 0.0, 1.0);
275
+ v_uv = a_uv;
276
+ v_opacity = a_opacity;
277
+ }
278
+ `
279
+ );
280
+ var IMAGE_VOPACITY_FRAG_SRC = (
281
+ /* glsl */
282
+ `#version 300 es
283
+ precision highp float;
284
+ in vec2 v_uv;
285
+ in float v_opacity;
286
+ uniform sampler2D u_sampler;
287
+ uniform float u_alpha;
288
+ uniform mat4 u_colorMatrix;
289
+ uniform vec4 u_colorBias;
290
+ out vec4 outColor;
291
+ void main() {
292
+ vec4 texel = texture(u_sampler, v_uv);
293
+ vec4 mapped = clamp(u_colorMatrix * texel + u_colorBias, 0.0, 1.0);
294
+ float a = mapped.a * v_opacity * u_alpha;
295
+ outColor = vec4(mapped.rgb * a, a);
296
+ }
297
+ `
298
+ );
261
299
  var IMAGE_FILL_UNIFORMS = [
262
300
  "u_proj",
263
301
  "u_model",
@@ -268,6 +306,15 @@ var IMAGE_FILL_UNIFORMS = [
268
306
  "u_colorBias"
269
307
  ];
270
308
  var IMAGE_FILL_ATTRIBUTES = ["a_position", "a_uv"];
309
+ var IMAGE_VOPACITY_UNIFORMS = [
310
+ "u_proj",
311
+ "u_model",
312
+ "u_sampler",
313
+ "u_alpha",
314
+ "u_colorMatrix",
315
+ "u_colorBias"
316
+ ];
317
+ var IMAGE_VOPACITY_ATTRIBUTES = ["a_position", "a_uv", "a_opacity"];
271
318
 
272
319
  // src/renderer/shaders/gradFill.ts
273
320
  var GRAD_VERT_SRC = (
@@ -1517,6 +1564,9 @@ function _resetPaintKindsForTests() {
1517
1564
  seedBuiltins();
1518
1565
  bumpNodeMemoGeneration();
1519
1566
  }
1567
+
1568
+ // src/renderer/DrawCommand.ts
1569
+ var SPRITE_STRIDE = 9;
1520
1570
  function tessellate(path, opts = {}) {
1521
1571
  if (path.kind === "rect") return tessellateRect(path);
1522
1572
  return tessellatePolygon(path, opts);
@@ -3241,6 +3291,210 @@ var SolidBatch = class {
3241
3291
  }
3242
3292
  };
3243
3293
 
3294
+ // src/renderer/imageBatch.ts
3295
+ var MAX_IMAGE_VERTICES_PER_BATCH = 32768;
3296
+ var FLOATS_PER_VERTEX2 = 5;
3297
+ var INITIAL_QUADS = 64;
3298
+ var TIERS = [
3299
+ { quads: 1, slots: 64 },
3300
+ { quads: 16, slots: 64 },
3301
+ { quads: 256, slots: 16 }
3302
+ ];
3303
+ TIERS[0].slots;
3304
+ var IMAGE_LARGE_RING_SIZE = 4;
3305
+ function doubledTo2(from, need) {
3306
+ let capacity = from;
3307
+ while (capacity < need) capacity *= 2;
3308
+ return capacity;
3309
+ }
3310
+ function quadIndices(quads) {
3311
+ const out = new Uint32Array(quads * 6);
3312
+ for (let q = 0; q < quads; q++) {
3313
+ const b = q * 4;
3314
+ const j = q * 6;
3315
+ out[j] = b;
3316
+ out[j + 1] = b + 1;
3317
+ out[j + 2] = b + 2;
3318
+ out[j + 3] = b;
3319
+ out[j + 4] = b + 2;
3320
+ out[j + 5] = b + 3;
3321
+ }
3322
+ return out;
3323
+ }
3324
+ var ImageBatch = class {
3325
+ gl;
3326
+ aPos;
3327
+ aUv;
3328
+ aOpacity;
3329
+ /** One ring per tier, cycled per flush. Slots are created on first use, so a
3330
+ * renderer that flushes rarely allocates as few as it flushes. */
3331
+ rings = TIERS.map((t) => new Array(t.slots).fill(void 0));
3332
+ nextInRing = TIERS.map(() => 0);
3333
+ /** For flushes past the largest tier; these sets grow to fit. */
3334
+ largeRing = new Array(IMAGE_LARGE_RING_SIZE).fill(void 0);
3335
+ nextLarge = 0;
3336
+ verts = new Float32Array(INITIAL_QUADS * 4 * FLOATS_PER_VERTEX2);
3337
+ nQuads = 0;
3338
+ constructor(gl, prog) {
3339
+ const aPos = prog.attribute("a_position");
3340
+ const aUv = prog.attribute("a_uv");
3341
+ const aOpacity = prog.attribute("a_opacity");
3342
+ if (aPos === void 0 || aUv === void 0 || aOpacity === void 0) {
3343
+ throw new Error(
3344
+ "ImageBatch: vertex-opacity program is missing a_position / a_uv / a_opacity"
3345
+ );
3346
+ }
3347
+ this.gl = gl;
3348
+ this.aPos = aPos;
3349
+ this.aUv = aUv;
3350
+ this.aOpacity = aOpacity;
3351
+ }
3352
+ /** Indices staged — what a caller passes to `drawElements`. */
3353
+ get length() {
3354
+ return this.nQuads * 6;
3355
+ }
3356
+ get quads() {
3357
+ return this.nQuads;
3358
+ }
3359
+ /** Whether staging one more quad would put the run past the per-flush cap. */
3360
+ wouldOverflow() {
3361
+ return (this.nQuads + 1) * 4 > MAX_IMAGE_VERTICES_PER_BATCH;
3362
+ }
3363
+ /**
3364
+ * Append one image quad: the destination rect `(x, y, w, h)` mapped through
3365
+ * `m`, sampling `(u0, v0)`-`(u1, v1)`, every corner carrying `opacity`.
3366
+ *
3367
+ * An affine maps a rect to a parallelogram, so two triangles still cover it
3368
+ * and the batch draws at `u_model` identity. Corners wind top-left,
3369
+ * top-right, bottom-right, bottom-left, with UVs following — the flips a
3370
+ * command asks for are already in the `u`/`v` the caller passes.
3371
+ */
3372
+ pushQuad(x, y, w, h, m, u0, v0, u1, v1, opacity) {
3373
+ this.reserve();
3374
+ const v = this.verts;
3375
+ let i = this.nQuads * 4 * FLOATS_PER_VERTEX2;
3376
+ const ma = m[0], mb = m[1], mc = m[3], md = m[4], mtx = m[6], mty = m[7];
3377
+ const x1 = x + w;
3378
+ const y1 = y + h;
3379
+ const ax = ma * x + mc * y + mtx, ay = mb * x + md * y + mty;
3380
+ const bx = ma * x1 + mc * y + mtx, by = mb * x1 + md * y + mty;
3381
+ const cx = ma * x1 + mc * y1 + mtx, cy = mb * x1 + md * y1 + mty;
3382
+ const dx = ma * x + mc * y1 + mtx, dy = mb * x + md * y1 + mty;
3383
+ v[i++] = ax;
3384
+ v[i++] = ay;
3385
+ v[i++] = u0;
3386
+ v[i++] = v0;
3387
+ v[i++] = opacity;
3388
+ v[i++] = bx;
3389
+ v[i++] = by;
3390
+ v[i++] = u1;
3391
+ v[i++] = v0;
3392
+ v[i++] = opacity;
3393
+ v[i++] = cx;
3394
+ v[i++] = cy;
3395
+ v[i++] = u1;
3396
+ v[i++] = v1;
3397
+ v[i++] = opacity;
3398
+ v[i++] = dx;
3399
+ v[i++] = dy;
3400
+ v[i++] = u0;
3401
+ v[i++] = v1;
3402
+ v[i++] = opacity;
3403
+ this.nQuads += 1;
3404
+ }
3405
+ /** Upload the staged geometry into the next set of buffers and bind its VAO.
3406
+ * Returns the index count for the caller's `drawElements`. */
3407
+ uploadAndBind() {
3408
+ const gl = this.gl;
3409
+ const tier = TIERS.findIndex((t) => this.nQuads <= t.quads);
3410
+ const set = tier < 0 ? this.nextLargeSlot() : this.nextRingSlot(tier);
3411
+ gl.bindVertexArray(set.vao);
3412
+ gl.bindBuffer(gl.ARRAY_BUFFER, set.vbo);
3413
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verts, 0, this.nQuads * 4 * FLOATS_PER_VERTEX2);
3414
+ return this.nQuads * 6;
3415
+ }
3416
+ reset() {
3417
+ this.nQuads = 0;
3418
+ }
3419
+ dispose() {
3420
+ for (const set of [...this.rings.flat(), ...this.largeRing]) {
3421
+ if (set) this.deleteSet(set);
3422
+ }
3423
+ for (const ring of this.rings) ring.fill(void 0);
3424
+ this.largeRing.fill(void 0);
3425
+ }
3426
+ /** Grow the CPU arrays so one more quad fits. */
3427
+ reserve() {
3428
+ const need = (this.nQuads + 1) * 4 * FLOATS_PER_VERTEX2;
3429
+ if (need <= this.verts.length) return;
3430
+ const grown = new Float32Array(doubledTo2(this.verts.length, need));
3431
+ grown.set(this.verts);
3432
+ this.verts = grown;
3433
+ }
3434
+ nextRingSlot(tier) {
3435
+ const ring = this.rings[tier];
3436
+ const slot = this.nextInRing[tier];
3437
+ this.nextInRing[tier] = (slot + 1) % ring.length;
3438
+ const existing = ring[slot];
3439
+ if (existing) return existing;
3440
+ const set = this.createSet(TIERS[tier].quads);
3441
+ ring[slot] = set;
3442
+ return set;
3443
+ }
3444
+ nextLargeSlot() {
3445
+ const gl = this.gl;
3446
+ const slot = this.nextLarge;
3447
+ this.nextLarge = (slot + 1) % IMAGE_LARGE_RING_SIZE;
3448
+ let set = this.largeRing[slot];
3449
+ if (!set) {
3450
+ set = this.createSet(doubledTo2(TIERS[TIERS.length - 1].quads, this.nQuads));
3451
+ this.largeRing[slot] = set;
3452
+ return set;
3453
+ }
3454
+ if (this.nQuads > set.quadCapacity) {
3455
+ set.quadCapacity = doubledTo2(set.quadCapacity, this.nQuads);
3456
+ gl.bindVertexArray(set.vao);
3457
+ gl.bindBuffer(gl.ARRAY_BUFFER, set.vbo);
3458
+ gl.bufferData(
3459
+ gl.ARRAY_BUFFER,
3460
+ set.quadCapacity * 4 * FLOATS_PER_VERTEX2 * 4,
3461
+ gl.DYNAMIC_DRAW
3462
+ );
3463
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, set.ibo);
3464
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, quadIndices(set.quadCapacity), gl.STATIC_DRAW);
3465
+ gl.bindVertexArray(null);
3466
+ }
3467
+ return set;
3468
+ }
3469
+ createSet(quads) {
3470
+ const gl = this.gl;
3471
+ const vao = gl.createVertexArray();
3472
+ const vbo = gl.createBuffer();
3473
+ const ibo = gl.createBuffer();
3474
+ if (!vao || !vbo || !ibo) throw new Error("ImageBatch: failed to create GL objects");
3475
+ const stride = FLOATS_PER_VERTEX2 * 4;
3476
+ gl.bindVertexArray(vao);
3477
+ gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
3478
+ gl.bufferData(gl.ARRAY_BUFFER, quads * 4 * stride, gl.DYNAMIC_DRAW);
3479
+ gl.enableVertexAttribArray(this.aPos);
3480
+ gl.vertexAttribPointer(this.aPos, 2, gl.FLOAT, false, stride, 0);
3481
+ gl.enableVertexAttribArray(this.aUv);
3482
+ gl.vertexAttribPointer(this.aUv, 2, gl.FLOAT, false, stride, 8);
3483
+ gl.enableVertexAttribArray(this.aOpacity);
3484
+ gl.vertexAttribPointer(this.aOpacity, 1, gl.FLOAT, false, stride, 16);
3485
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
3486
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, quadIndices(quads), gl.STATIC_DRAW);
3487
+ gl.bindVertexArray(null);
3488
+ return { vao, vbo, ibo, quadCapacity: quads };
3489
+ }
3490
+ deleteSet(set) {
3491
+ const gl = this.gl;
3492
+ gl.deleteBuffer(set.vbo);
3493
+ gl.deleteBuffer(set.ibo);
3494
+ gl.deleteVertexArray(set.vao);
3495
+ }
3496
+ };
3497
+
3244
3498
  // src/renderer/draw.ts
3245
3499
  var FRAME_UPLOADS = /* @__PURE__ */ new WeakMap();
3246
3500
  var FRAME_PROJ = /* @__PURE__ */ new WeakMap();
@@ -3335,13 +3589,14 @@ function dispatch(ctx, cmd) {
3335
3589
  case "path":
3336
3590
  return drawPath(ctx, cmd);
3337
3591
  case "text":
3338
- flushSolids(ctx);
3592
+ flushBatches(ctx);
3339
3593
  return drawText(ctx, cmd);
3340
3594
  case "image":
3341
- flushSolids(ctx);
3342
3595
  return drawImage(ctx, cmd);
3596
+ case "sprites":
3597
+ return drawSprites(ctx, cmd);
3343
3598
  case "shader":
3344
- flushSolids(ctx);
3599
+ flushBatches(ctx);
3345
3600
  return drawShader(ctx, cmd);
3346
3601
  }
3347
3602
  }
@@ -3518,6 +3773,7 @@ function canBatchMesh(mesh) {
3518
3773
  return !mesh.requiresStencil && mesh.vertices.length >> 1 <= MAX_BATCHED_MESH_VERTICES;
3519
3774
  }
3520
3775
  function stageSolid(ctx, vertices) {
3776
+ flushImages(ctx);
3521
3777
  if (ctx.solidState !== void 0 && !stagedStateIsLive(ctx, ctx.solidState)) flushSolids(ctx);
3522
3778
  if (ctx.solidBatch.wouldOverflow(vertices)) flushSolids(ctx);
3523
3779
  if (ctx.solidState === void 0) {
@@ -3561,7 +3817,7 @@ function tryStageSolid(ctx, mesh, paint) {
3561
3817
  pushMesh(ctx, mesh, paint);
3562
3818
  return true;
3563
3819
  }
3564
- flushSolids(ctx);
3820
+ flushBatches(ctx);
3565
3821
  return false;
3566
3822
  }
3567
3823
  function flushSolids(ctx) {
@@ -3582,6 +3838,56 @@ function flushSolids(ctx) {
3582
3838
  batch.reset();
3583
3839
  ctx.solidState = void 0;
3584
3840
  }
3841
+ function stagedImageStateIsLive(ctx, staged, image, sampling) {
3842
+ if (staged.image !== image) return false;
3843
+ if (staged.sampling !== sampling) return false;
3844
+ if (staged.clipDepth !== ctx.clipDepth) return false;
3845
+ const colorMatrix = ctx.state.colorMatrix;
3846
+ return staged.colorMatrix === colorMatrix || sameValues(staged.colorMatrix, colorMatrix);
3847
+ }
3848
+ function stageImage(ctx, image, sampling) {
3849
+ flushSolids(ctx);
3850
+ if (ctx.imageState !== void 0 && !stagedImageStateIsLive(ctx, ctx.imageState, image, sampling)) {
3851
+ flushImages(ctx);
3852
+ }
3853
+ if (ctx.imageBatch.wouldOverflow()) flushImages(ctx);
3854
+ if (ctx.imageState === void 0) {
3855
+ ctx.imageState = {
3856
+ image,
3857
+ sampling,
3858
+ colorMatrix: ctx.state.colorMatrix,
3859
+ clipDepth: ctx.clipDepth
3860
+ };
3861
+ }
3862
+ }
3863
+ function flushImages(ctx) {
3864
+ const batch = ctx.imageBatch;
3865
+ const staged = ctx.imageState;
3866
+ if (batch.length === 0 || staged === void 0) return;
3867
+ const gl = ctx.gl;
3868
+ const prog = ctx.imageFillVOpacity;
3869
+ gl.useProgram(prog.handle);
3870
+ const indexCount = batch.uploadAndBind();
3871
+ setProjAndModel(ctx, prog, BATCH_MODEL);
3872
+ setAlphaUniform(ctx, prog, 1);
3873
+ setColorMatrixUniforms(ctx, prog, staged.colorMatrix);
3874
+ ctx.imageCache.bind(staged.image, 0);
3875
+ gl.texParameteri(
3876
+ gl.TEXTURE_2D,
3877
+ gl.TEXTURE_MAG_FILTER,
3878
+ staged.sampling === "nearest" ? gl.NEAREST : gl.LINEAR
3879
+ );
3880
+ gl.uniform1i(prog.uniform("u_sampler"), 0);
3881
+ applyClipTest(ctx, staged.clipDepth);
3882
+ gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_INT, 0);
3883
+ gl.bindVertexArray(null);
3884
+ batch.reset();
3885
+ ctx.imageState = void 0;
3886
+ }
3887
+ function flushBatches(ctx) {
3888
+ flushSolids(ctx);
3889
+ flushImages(ctx);
3890
+ }
3585
3891
  function expandAnchorColors(perAnchor, handle) {
3586
3892
  const aA = handle.anchorA;
3587
3893
  const aB = handle.anchorB;
@@ -3785,7 +4091,7 @@ function rasterizePathToStencil(ctx, path) {
3785
4091
  gl.bindVertexArray(null);
3786
4092
  }
3787
4093
  function pushClip(ctx, path, newDepth) {
3788
- flushSolids(ctx);
4094
+ flushBatches(ctx);
3789
4095
  const gl = ctx.gl;
3790
4096
  const ancestors = ancestorMask(newDepth - 1);
3791
4097
  const newBit = 1 << newDepth;
@@ -3800,7 +4106,7 @@ function pushClip(ctx, path, newDepth) {
3800
4106
  gl.stencilMask(255);
3801
4107
  }
3802
4108
  function popClip(ctx, path, oldDepth) {
3803
- flushSolids(ctx);
4109
+ flushBatches(ctx);
3804
4110
  const gl = ctx.gl;
3805
4111
  gl.enable(gl.STENCIL_TEST);
3806
4112
  const oldBit = 1 << oldDepth + 1;
@@ -3847,7 +4153,7 @@ function drawPathStroke(ctx, rawCmd) {
3847
4153
  const stroke = cmd.stroke;
3848
4154
  const align = stroke.align ?? "center";
3849
4155
  if (cmd.path.kind === "polygon" && align !== "center") {
3850
- flushSolids(ctx);
4156
+ flushBatches(ctx);
3851
4157
  drawPathStrokeStenciled(ctx, cmd, align);
3852
4158
  return;
3853
4159
  }
@@ -4195,45 +4501,6 @@ function drawTextGroup(ctx, group, prog, dx, dy) {
4195
4501
  gl.drawElements(gl.TRIANGLES, group.quads.length * 6, gl.UNSIGNED_INT, 0);
4196
4502
  gl.bindVertexArray(null);
4197
4503
  }
4198
- var IMAGE_QUAD_RING_SIZE = 64;
4199
- var IMAGE_QUAD_RINGS = /* @__PURE__ */ new WeakMap();
4200
- var IMAGE_QUAD_INDICES = new Uint32Array([0, 1, 2, 1, 3, 2]);
4201
- var IMAGE_QUAD_STRIDE = 16;
4202
- function imageQuadRing(gl, prog) {
4203
- const existing = IMAGE_QUAD_RINGS.get(prog);
4204
- if (existing) return existing;
4205
- const aPosLoc = prog.attribute("a_position");
4206
- const aUvLoc = prog.attribute("a_uv");
4207
- const ibo = gl.createBuffer();
4208
- if (!ibo) throw new Error("drawImage: createBuffer (IBO) returned null");
4209
- const vaos = [];
4210
- const vbos = [];
4211
- for (let slot = 0; slot < IMAGE_QUAD_RING_SIZE; slot++) {
4212
- const vao = gl.createVertexArray();
4213
- const vbo = gl.createBuffer();
4214
- if (!vao) throw new Error("drawImage: createVertexArray returned null");
4215
- if (!vbo) throw new Error("drawImage: createBuffer returned null");
4216
- gl.bindVertexArray(vao);
4217
- gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
4218
- gl.bufferData(gl.ARRAY_BUFFER, 4 * IMAGE_QUAD_STRIDE, gl.DYNAMIC_DRAW);
4219
- if (aPosLoc !== void 0) {
4220
- gl.enableVertexAttribArray(aPosLoc);
4221
- gl.vertexAttribPointer(aPosLoc, 2, gl.FLOAT, false, IMAGE_QUAD_STRIDE, 0);
4222
- }
4223
- if (aUvLoc !== void 0) {
4224
- gl.enableVertexAttribArray(aUvLoc);
4225
- gl.vertexAttribPointer(aUvLoc, 2, gl.FLOAT, false, IMAGE_QUAD_STRIDE, 8);
4226
- }
4227
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
4228
- if (slot === 0) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, IMAGE_QUAD_INDICES, gl.STATIC_DRAW);
4229
- gl.bindVertexArray(null);
4230
- vaos.push(vao);
4231
- vbos.push(vbo);
4232
- }
4233
- const ring = { vaos, vbos, ibo, next: 0 };
4234
- IMAGE_QUAD_RINGS.set(prog, ring);
4235
- return ring;
4236
- }
4237
4504
  var TEXT_QUAD_RING_SIZE = 64;
4238
4505
  var TEXT_QUAD_RINGS = /* @__PURE__ */ new WeakMap();
4239
4506
  function grownTo(need) {
@@ -4311,22 +4578,9 @@ function disposeTextQuads(gl, prog) {
4311
4578
  gl.deleteBuffer(ring.ibo);
4312
4579
  TEXT_QUAD_RINGS.delete(prog);
4313
4580
  }
4314
- function disposeImageQuads(gl, prog) {
4315
- const ring = IMAGE_QUAD_RINGS.get(prog);
4316
- if (!ring) return;
4317
- for (const vao of ring.vaos) gl.deleteVertexArray(vao);
4318
- for (const vbo of ring.vbos) gl.deleteBuffer(vbo);
4319
- gl.deleteBuffer(ring.ibo);
4320
- IMAGE_QUAD_RINGS.delete(prog);
4321
- }
4322
- var IMAGE_QUAD_VERTICES = new Float32Array(16);
4323
4581
  function drawImage(ctx, cmd) {
4324
4582
  ctx.imageCache.upload(cmd.image, cmd.image);
4325
- const gl = ctx.gl;
4326
- const prog = ctx.imageFill;
4327
- gl.useProgram(prog.handle);
4328
- const x0 = cmd.x, y0 = cmd.y;
4329
- const x1 = cmd.x + cmd.w, y1 = cmd.y + cmd.h;
4583
+ stageImage(ctx, cmd.image, cmd.sampling ?? "linear");
4330
4584
  const src = cmd.source;
4331
4585
  let u0 = 0, v0 = 0, u1 = 1, v1 = 1;
4332
4586
  if (src) {
@@ -4346,43 +4600,50 @@ function drawImage(ctx, cmd) {
4346
4600
  v0 = v1;
4347
4601
  v1 = t;
4348
4602
  }
4349
- const v = IMAGE_QUAD_VERTICES;
4350
- v[0] = x0;
4351
- v[1] = y0;
4352
- v[2] = u0;
4353
- v[3] = v0;
4354
- v[4] = x1;
4355
- v[5] = y0;
4356
- v[6] = u1;
4357
- v[7] = v0;
4358
- v[8] = x0;
4359
- v[9] = y1;
4360
- v[10] = u0;
4361
- v[11] = v1;
4362
- v[12] = x1;
4363
- v[13] = y1;
4364
- v[14] = u1;
4365
- v[15] = v1;
4366
- const ring = imageQuadRing(gl, prog);
4367
- const slot = ring.next;
4368
- ring.next = (slot + 1) % IMAGE_QUAD_RING_SIZE;
4369
- gl.bindVertexArray(ring.vaos[slot]);
4370
- gl.bindBuffer(gl.ARRAY_BUFFER, ring.vbos[slot]);
4371
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, v);
4372
- setProjAndModel(ctx, prog);
4373
- setColorMatrixUniforms(ctx, prog);
4374
- ctx.imageCache.bind(cmd.image, 0);
4375
- gl.texParameteri(
4376
- gl.TEXTURE_2D,
4377
- gl.TEXTURE_MAG_FILTER,
4378
- cmd.sampling === "nearest" ? gl.NEAREST : gl.LINEAR
4603
+ ctx.imageBatch.pushQuad(
4604
+ cmd.x,
4605
+ cmd.y,
4606
+ cmd.w,
4607
+ cmd.h,
4608
+ ctx.state.transform,
4609
+ u0,
4610
+ v0,
4611
+ u1,
4612
+ v1,
4613
+ (cmd.opacity ?? 1) * ctx.state.alpha
4379
4614
  );
4380
- gl.uniform1i(prog.uniform("u_sampler"), 0);
4381
- gl.uniform1f(prog.uniform("u_opacity"), cmd.opacity ?? 1);
4382
- setAlphaUniform(ctx, prog, ctx.state.alpha);
4383
- applyClipTest(ctx);
4384
- gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, 0);
4385
- gl.bindVertexArray(null);
4615
+ }
4616
+ function drawSprites(ctx, cmd) {
4617
+ const data = cmd.sprites;
4618
+ const count = Math.floor(data.length / SPRITE_STRIDE);
4619
+ if (count === 0) return;
4620
+ ctx.imageCache.upload(cmd.image, cmd.image);
4621
+ const sampling = cmd.sampling ?? "linear";
4622
+ stageImage(ctx, cmd.image, sampling);
4623
+ const batch = ctx.imageBatch;
4624
+ const m = ctx.state.transform;
4625
+ const groupAlpha = ctx.state.alpha;
4626
+ const tw = cmd.image.width;
4627
+ const th = cmd.image.height;
4628
+ for (let s = 0, i = 0; s < count; s++, i += SPRITE_STRIDE) {
4629
+ if (batch.wouldOverflow()) {
4630
+ flushImages(ctx);
4631
+ stageImage(ctx, cmd.image, sampling);
4632
+ }
4633
+ const sx = data[i + 4], sy = data[i + 5], sw = data[i + 6], sh = data[i + 7];
4634
+ batch.pushQuad(
4635
+ data[i],
4636
+ data[i + 1],
4637
+ data[i + 2],
4638
+ data[i + 3],
4639
+ m,
4640
+ sx / tw,
4641
+ sy / th,
4642
+ (sx + sw) / tw,
4643
+ (sy + sh) / th,
4644
+ data[i + 8] * groupAlpha
4645
+ );
4646
+ }
4386
4647
  }
4387
4648
 
4388
4649
  // src/renderer/shaders/customPrelude.ts
@@ -4485,6 +4746,7 @@ var WeaselRenderer = class {
4485
4746
  textSdf;
4486
4747
  textSdfR8;
4487
4748
  imageFill;
4749
+ imageFillVOpacity;
4488
4750
  gradFill;
4489
4751
  patternFill;
4490
4752
  meshCache;
@@ -4495,6 +4757,7 @@ var WeaselRenderer = class {
4495
4757
  quadVbo = null;
4496
4758
  quadIbo = null;
4497
4759
  solidBatch;
4760
+ imageBatch;
4498
4761
  groupState = new GroupState();
4499
4762
  widthCss;
4500
4763
  heightCss;
@@ -4559,6 +4822,13 @@ var WeaselRenderer = class {
4559
4822
  this.imageFill = new ShaderProgram(this.gl, IMAGE_VERT_SRC, IMAGE_FRAG_SRC);
4560
4823
  this.imageFill.lookupUniforms(IMAGE_FILL_UNIFORMS);
4561
4824
  this.imageFill.lookupAttributes(IMAGE_FILL_ATTRIBUTES);
4825
+ this.imageFillVOpacity = new ShaderProgram(
4826
+ this.gl,
4827
+ IMAGE_VOPACITY_VERT_SRC,
4828
+ IMAGE_VOPACITY_FRAG_SRC
4829
+ );
4830
+ this.imageFillVOpacity.lookupUniforms(IMAGE_VOPACITY_UNIFORMS);
4831
+ this.imageFillVOpacity.lookupAttributes(IMAGE_VOPACITY_ATTRIBUTES);
4562
4832
  this.gradFill = new ShaderProgram(this.gl, GRAD_VERT_SRC, GRAD_FRAG_SRC);
4563
4833
  this.gradFill.lookupUniforms(GRAD_FILL_UNIFORMS);
4564
4834
  this.gradFill.lookupAttributes(GRAD_FILL_ATTRIBUTES);
@@ -4573,6 +4843,7 @@ var WeaselRenderer = class {
4573
4843
  this.gradRampCache = new GradientRampCache(this.gl);
4574
4844
  this.uploadQuadGeometry();
4575
4845
  this.solidBatch = new SolidBatch(this.gl, this.pathFillVColor);
4846
+ this.imageBatch = new ImageBatch(this.gl, this.imageFillVOpacity);
4576
4847
  }
4577
4848
  uploadQuadGeometry() {
4578
4849
  const gl = this.gl;
@@ -4696,6 +4967,13 @@ var WeaselRenderer = class {
4696
4967
  this.imageFill = new ShaderProgram(this.gl, IMAGE_VERT_SRC, IMAGE_FRAG_SRC);
4697
4968
  this.imageFill.lookupUniforms(IMAGE_FILL_UNIFORMS);
4698
4969
  this.imageFill.lookupAttributes(IMAGE_FILL_ATTRIBUTES);
4970
+ this.imageFillVOpacity = new ShaderProgram(
4971
+ this.gl,
4972
+ IMAGE_VOPACITY_VERT_SRC,
4973
+ IMAGE_VOPACITY_FRAG_SRC
4974
+ );
4975
+ this.imageFillVOpacity.lookupUniforms(IMAGE_VOPACITY_UNIFORMS);
4976
+ this.imageFillVOpacity.lookupAttributes(IMAGE_VOPACITY_ATTRIBUTES);
4699
4977
  this.gradFill = new ShaderProgram(this.gl, GRAD_VERT_SRC, GRAD_FRAG_SRC);
4700
4978
  this.gradFill.lookupUniforms(GRAD_FILL_UNIFORMS);
4701
4979
  this.gradFill.lookupAttributes(GRAD_FILL_ATTRIBUTES);
@@ -4711,6 +4989,7 @@ var WeaselRenderer = class {
4711
4989
  markAllFontsNotUploaded();
4712
4990
  this.uploadQuadGeometry();
4713
4991
  this.solidBatch = new SolidBatch(this.gl, this.pathFillVColor);
4992
+ this.imageBatch = new ImageBatch(this.gl, this.imageFillVOpacity);
4714
4993
  for (const id of this.programRegistry.keys()) {
4715
4994
  const src = getProgramSource(id);
4716
4995
  if (!src) continue;
@@ -4759,7 +5038,7 @@ var WeaselRenderer = class {
4759
5038
  }
4760
5039
  this.meshCache.freeTransient();
4761
5040
  this.meshCache.drainPendingDeletes();
4762
- for (const prog of [this.pathFill, this.pathFillVColor, this.textSdf, this.textSdfR8, this.imageFill, this.gradFill, this.patternFill]) {
5041
+ for (const prog of [this.pathFill, this.pathFillVColor, this.textSdf, this.textSdfR8, this.imageFill, this.imageFillVOpacity, this.gradFill, this.patternFill]) {
4763
5042
  gl.deleteProgram(prog.handle);
4764
5043
  }
4765
5044
  for (const prog of this.programRegistry.values()) {
@@ -4770,9 +5049,9 @@ var WeaselRenderer = class {
4770
5049
  this.gradRampCache.free();
4771
5050
  if (this.quadVbo) gl.deleteBuffer(this.quadVbo);
4772
5051
  if (this.quadIbo) gl.deleteBuffer(this.quadIbo);
4773
- disposeImageQuads(gl, this.imageFill);
4774
5052
  for (const prog of [this.textSdf, this.textSdfR8, this.pathFill]) disposeTextQuads(gl, prog);
4775
5053
  this.solidBatch.dispose();
5054
+ this.imageBatch.dispose();
4776
5055
  }
4777
5056
  /**
4778
5057
  * Draw one frame.
@@ -4801,6 +5080,7 @@ var WeaselRenderer = class {
4801
5080
  textSdf: this.textSdf,
4802
5081
  textSdfR8: this.textSdfR8,
4803
5082
  imageFill: this.imageFill,
5083
+ imageFillVOpacity: this.imageFillVOpacity,
4804
5084
  gradFill: this.gradFill,
4805
5085
  patternFill: this.patternFill,
4806
5086
  meshCache: this.meshCache,
@@ -4812,6 +5092,7 @@ var WeaselRenderer = class {
4812
5092
  quadVbo: this.quadVbo,
4813
5093
  quadIbo: this.quadIbo,
4814
5094
  solidBatch: this.solidBatch,
5095
+ imageBatch: this.imageBatch,
4815
5096
  state: this.groupState,
4816
5097
  widthCss: this.widthCss,
4817
5098
  heightCss: this.heightCss,
@@ -4821,7 +5102,7 @@ var WeaselRenderer = class {
4821
5102
  viewMatrix: viewMatrix2
4822
5103
  };
4823
5104
  for (const cmd of commands) dispatch(ctx, cmd);
4824
- flushSolids(ctx);
5105
+ flushBatches(ctx);
4825
5106
  this.meshCache.freeTransient();
4826
5107
  }
4827
5108
  resize(dims) {
@@ -4855,6 +5136,10 @@ var WeaselRenderer = class {
4855
5136
  return this.solidBatch;
4856
5137
  }
4857
5138
  /** @internal */
5139
+ _imageBatch() {
5140
+ return this.imageBatch;
5141
+ }
5142
+ /** @internal */
4858
5143
  _textSdf() {
4859
5144
  return this.textSdf;
4860
5145
  }
@@ -4867,6 +5152,10 @@ var WeaselRenderer = class {
4867
5152
  return this.imageFill;
4868
5153
  }
4869
5154
  /** @internal */
5155
+ _imageFillVOpacity() {
5156
+ return this.imageFillVOpacity;
5157
+ }
5158
+ /** @internal */
4870
5159
  _gradFill() {
4871
5160
  return this.gradFill;
4872
5161
  }
@@ -5915,6 +6204,86 @@ function clientToCanvasRect(rect, clientX, clientY) {
5915
6204
  return [clientX - rect.left, clientY - rect.top];
5916
6205
  }
5917
6206
 
6207
+ // src/interactions/gestures/pointerSession/recovery.ts
6208
+ var LOST_CAPTURE_EVENT = "lostpointercapture";
6209
+ function reportsButtons(down) {
6210
+ return !!down.buttons;
6211
+ }
6212
+ function isMissedRelease(move) {
6213
+ return move.buttons === 0;
6214
+ }
6215
+
6216
+ // src/interactions/gestures/pointerSession/pointerSession.ts
6217
+ function openPointerSession(origin, down, cb, opts = {}) {
6218
+ const pointerId = down.pointerId;
6219
+ const doc = origin.ownerDocument ?? document;
6220
+ const buttonsReported = reportsButtons(down);
6221
+ let active = true;
6222
+ const teardown = () => {
6223
+ active = false;
6224
+ doc.removeEventListener("pointermove", onMove, true);
6225
+ doc.removeEventListener("pointerup", onUp, true);
6226
+ doc.removeEventListener("pointercancel", onPointerCancel, true);
6227
+ doc.removeEventListener("pointerdown", onRepress, true);
6228
+ origin.removeEventListener(LOST_CAPTURE_EVENT, onLostCapture);
6229
+ if (opts.capture !== false) {
6230
+ try {
6231
+ origin.releasePointerCapture?.(pointerId);
6232
+ } catch {
6233
+ }
6234
+ }
6235
+ };
6236
+ const mine = (e) => e.pointerId === pointerId;
6237
+ const end = (e) => {
6238
+ if (!active) return;
6239
+ teardown();
6240
+ cb.onEnd?.(e);
6241
+ };
6242
+ const abort = (reason) => {
6243
+ if (!active) return;
6244
+ teardown();
6245
+ cb.onCancel?.(reason);
6246
+ };
6247
+ function onMove(e) {
6248
+ if (!active || !mine(e)) return;
6249
+ if (buttonsReported && isMissedRelease(e)) {
6250
+ end(e);
6251
+ return;
6252
+ }
6253
+ cb.onMove?.(e);
6254
+ }
6255
+ function onUp(e) {
6256
+ if (mine(e)) end(e);
6257
+ }
6258
+ function onPointerCancel(e) {
6259
+ if (mine(e)) abort("pointercancel");
6260
+ }
6261
+ function onRepress(e) {
6262
+ if (mine(e)) abort("superseded");
6263
+ }
6264
+ function onLostCapture(e) {
6265
+ if (e.pointerId === pointerId) abort("lostcapture");
6266
+ }
6267
+ if (opts.capture !== false) {
6268
+ try {
6269
+ origin.setPointerCapture?.(pointerId);
6270
+ } catch {
6271
+ }
6272
+ }
6273
+ doc.addEventListener("pointermove", onMove, true);
6274
+ doc.addEventListener("pointerup", onUp, true);
6275
+ doc.addEventListener("pointercancel", onPointerCancel, true);
6276
+ doc.addEventListener("pointerdown", onRepress, true);
6277
+ origin.addEventListener(LOST_CAPTURE_EVENT, onLostCapture);
6278
+ return {
6279
+ pointerId,
6280
+ get active() {
6281
+ return active;
6282
+ },
6283
+ cancel: () => abort("aborted")
6284
+ };
6285
+ }
6286
+
5918
6287
  // src/interactions/gestures/handleDrag/handleDrag.ts
5919
6288
  function defaultRectEl(target) {
5920
6289
  if (target instanceof SVGElement && target.ownerSVGElement) {
@@ -5923,10 +6292,15 @@ function defaultRectEl(target) {
5923
6292
  return target;
5924
6293
  }
5925
6294
  function useHandleDrag(opts) {
5926
- const { onMove, onStart, onEnd, getRect } = opts;
6295
+ const { onMove, onStart, onEnd, onCancel, getRect } = opts;
6296
+ const live = useRef(null);
6297
+ useEffect(() => () => {
6298
+ live.current?.cancel();
6299
+ }, []);
5927
6300
  const onPointerDown = useCallback(
5928
6301
  (e) => {
5929
6302
  e.preventDefault();
6303
+ live.current?.cancel();
5930
6304
  const target = e.currentTarget;
5931
6305
  const rectEl = getRect ? getRect(target) : defaultRectEl(target);
5932
6306
  const rect = rectEl.getBoundingClientRect();
@@ -5934,26 +6308,24 @@ function useHandleDrag(opts) {
5934
6308
  const [x, y] = clientToCanvasRect(rect, clientX, clientY);
5935
6309
  return { x, y };
5936
6310
  };
5937
- target.setPointerCapture(e.pointerId);
5938
- onStart?.(localOf(e.clientX, e.clientY), e);
5939
- const move = (ev) => {
5940
- onMove(localOf(ev.clientX, ev.clientY), ev);
5941
- };
5942
- const end = (ev) => {
5943
- target.removeEventListener("pointermove", move);
5944
- target.removeEventListener("pointerup", end);
5945
- target.removeEventListener("pointercancel", end);
5946
- try {
5947
- target.releasePointerCapture(ev.pointerId);
5948
- } catch {
6311
+ let moved = false;
6312
+ live.current = openPointerSession(target, e, {
6313
+ onMove: (ev) => {
6314
+ moved = true;
6315
+ onMove(localOf(ev.clientX, ev.clientY), ev);
6316
+ },
6317
+ onEnd: (ev) => {
6318
+ live.current = null;
6319
+ onEnd?.({ point: localOf(ev.clientX, ev.clientY), moved, event: ev });
6320
+ },
6321
+ onCancel: (reason) => {
6322
+ live.current = null;
6323
+ onCancel?.(reason);
5949
6324
  }
5950
- onEnd?.(ev);
5951
- };
5952
- target.addEventListener("pointermove", move);
5953
- target.addEventListener("pointerup", end);
5954
- target.addEventListener("pointercancel", end);
6325
+ });
6326
+ onStart?.(localOf(e.clientX, e.clientY), e);
5955
6327
  },
5956
- [onMove, onStart, onEnd, getRect]
6328
+ [onMove, onStart, onEnd, onCancel, getRect]
5957
6329
  );
5958
6330
  return { onPointerDown };
5959
6331
  }
@@ -6009,57 +6381,53 @@ function positionGhost(el, x, y) {
6009
6381
  el.style.transform = `translate(${x - el.offsetWidth / 2}px, ${y - el.offsetHeight / 2}px) scale(0.92)`;
6010
6382
  }
6011
6383
  function beginPointerDrag(payload, source, startX, startY, ghostFn) {
6012
- if (activeDrag) return;
6384
+ if (activeDrag) return null;
6013
6385
  const ghost = ghostFn(source, payload);
6014
6386
  document.body.appendChild(ghost);
6015
6387
  positionGhost(ghost, startX, startY);
6016
6388
  activeDrag = { payload, ghost, lastZone: null };
6017
- function onMove(e) {
6018
- if (!activeDrag) return;
6019
- positionGhost(activeDrag.ghost, e.clientX, e.clientY);
6020
- const zone = findZone(e.clientX, e.clientY, payload.kind);
6021
- if (zone !== activeDrag.lastZone) {
6022
- activeDrag.lastZone?.onOver?.(false);
6023
- zone?.onOver?.(true);
6024
- activeDrag.lastZone = zone;
6025
- }
6026
- zone?.onMove?.(payload, e.clientX, e.clientY);
6027
- }
6028
- function onUp(e) {
6029
- if (!activeDrag) return;
6030
- const zone = findZone(e.clientX, e.clientY, payload.kind);
6031
- const dropped = activeDrag.payload;
6032
- activeDrag.lastZone?.onOver?.(false);
6033
- try {
6034
- if (zone) zone.onDrop(dropped, e.clientX, e.clientY);
6035
- } catch (err) {
6036
- console.error("weasel pointerDrag: drop zone onDrop threw", err);
6037
- } finally {
6038
- cleanup();
6039
- }
6040
- }
6041
- function onCancel() {
6042
- activeDrag?.lastZone?.onOver?.(false);
6043
- cleanup();
6044
- }
6045
- function cleanup() {
6389
+ const teardown = () => {
6046
6390
  activeDrag?.ghost.remove();
6047
6391
  activeDrag = null;
6048
- document.removeEventListener("pointermove", onMove);
6049
- document.removeEventListener("pointerup", onUp);
6050
- document.removeEventListener("pointercancel", onCancel);
6051
- }
6052
- document.addEventListener("pointermove", onMove);
6053
- document.addEventListener("pointerup", onUp);
6054
- document.addEventListener("pointercancel", onCancel);
6392
+ };
6393
+ return {
6394
+ move(e) {
6395
+ if (!activeDrag) return;
6396
+ positionGhost(activeDrag.ghost, e.clientX, e.clientY);
6397
+ const zone = findZone(e.clientX, e.clientY, payload.kind);
6398
+ if (zone !== activeDrag.lastZone) {
6399
+ activeDrag.lastZone?.onOver?.(false);
6400
+ zone?.onOver?.(true);
6401
+ activeDrag.lastZone = zone;
6402
+ }
6403
+ zone?.onMove?.(payload, e.clientX, e.clientY);
6404
+ },
6405
+ drop(e) {
6406
+ if (!activeDrag) return;
6407
+ const zone = findZone(e.clientX, e.clientY, payload.kind);
6408
+ const dropped = activeDrag.payload;
6409
+ activeDrag.lastZone?.onOver?.(false);
6410
+ try {
6411
+ if (zone) zone.onDrop(dropped, e.clientX, e.clientY);
6412
+ } catch (err) {
6413
+ console.error("weasel pointerDrag: drop zone onDrop threw", err);
6414
+ } finally {
6415
+ teardown();
6416
+ }
6417
+ },
6418
+ cancel() {
6419
+ activeDrag?.lastZone?.onOver?.(false);
6420
+ teardown();
6421
+ }
6422
+ };
6055
6423
  }
6056
6424
  var DRAG_THRESHOLD_PX_SQ = 25;
6057
6425
  function useDragHandle(getPayload, options) {
6058
6426
  const optsRef = useRef(options);
6059
6427
  optsRef.current = options;
6060
- const pendingTeardownRef = useRef(null);
6428
+ const live = useRef(null);
6061
6429
  useEffect(() => () => {
6062
- pendingTeardownRef.current?.();
6430
+ live.current?.cancel();
6063
6431
  }, []);
6064
6432
  const onPointerDown = useCallback((e) => {
6065
6433
  if (e.pointerType === "mouse" && e.button !== 0) return;
@@ -6067,41 +6435,46 @@ function useDragHandle(getPayload, options) {
6067
6435
  if (e.target instanceof HTMLElement && e.target.closest("input, button, select, textarea")) return;
6068
6436
  const startX = e.clientX;
6069
6437
  const startY = e.clientY;
6070
- let started = false;
6071
- function onMove(ev) {
6072
- if (started) return;
6073
- const dx = ev.clientX - startX;
6074
- const dy = ev.clientY - startY;
6075
- if (dx * dx + dy * dy > DRAG_THRESHOLD_PX_SQ) {
6076
- started = true;
6077
- const payload = getPayload();
6078
- cleanup();
6079
- if (payload) {
6080
- target.addEventListener(
6081
- "click",
6082
- (ce) => {
6083
- ce.stopPropagation();
6084
- ce.preventDefault();
6085
- },
6086
- { capture: true, once: true }
6087
- );
6088
- beginPointerDrag(payload, target, ev.clientX, ev.clientY, optsRef.current?.createGhost ?? defaultGhost);
6438
+ let ghost = null;
6439
+ let past = false;
6440
+ live.current = openPointerSession(target, e, {
6441
+ onMove: (ev) => {
6442
+ if (ghost) {
6443
+ ghost.move(ev);
6444
+ return;
6089
6445
  }
6446
+ if (past) return;
6447
+ const dx = ev.clientX - startX;
6448
+ const dy = ev.clientY - startY;
6449
+ if (dx * dx + dy * dy <= DRAG_THRESHOLD_PX_SQ) return;
6450
+ past = true;
6451
+ const payload = getPayload();
6452
+ if (!payload) return;
6453
+ target.addEventListener(
6454
+ "click",
6455
+ (ce) => {
6456
+ ce.stopPropagation();
6457
+ ce.preventDefault();
6458
+ },
6459
+ { capture: true, once: true }
6460
+ );
6461
+ ghost = beginPointerDrag(
6462
+ payload,
6463
+ target,
6464
+ ev.clientX,
6465
+ ev.clientY,
6466
+ optsRef.current?.createGhost ?? defaultGhost
6467
+ );
6468
+ },
6469
+ onEnd: (ev) => {
6470
+ live.current = null;
6471
+ ghost?.drop(ev);
6472
+ },
6473
+ onCancel: () => {
6474
+ live.current = null;
6475
+ ghost?.cancel();
6090
6476
  }
6091
- }
6092
- function onEnd() {
6093
- cleanup();
6094
- }
6095
- function cleanup() {
6096
- document.removeEventListener("pointermove", onMove);
6097
- document.removeEventListener("pointerup", onEnd);
6098
- document.removeEventListener("pointercancel", onEnd);
6099
- pendingTeardownRef.current = null;
6100
- }
6101
- document.addEventListener("pointermove", onMove);
6102
- document.addEventListener("pointerup", onEnd);
6103
- document.addEventListener("pointercancel", onEnd);
6104
- pendingTeardownRef.current = cleanup;
6477
+ }, { capture: false });
6105
6478
  }, [getPayload]);
6106
6479
  return { onPointerDown, style: { touchAction: "none" } };
6107
6480
  }
@@ -6128,47 +6501,34 @@ function startThresholdDrag(e, opts) {
6128
6501
  const startX = e.clientX;
6129
6502
  const startY = e.clientY;
6130
6503
  const threshold = opts.threshold ?? 4;
6131
- const target = e.currentTarget;
6132
- target.setPointerCapture(e.pointerId);
6133
6504
  let activated = false;
6134
- function maybeActivate(ev) {
6505
+ const maybeActivate = (ev) => {
6135
6506
  if (activated) return;
6136
6507
  const dx = ev.clientX - startX;
6137
6508
  const dy = ev.clientY - startY;
6138
6509
  if (dx * dx + dy * dy < threshold * threshold) return;
6139
6510
  activated = true;
6140
6511
  opts.onActivate?.(ev);
6141
- }
6142
- function onMove(ev) {
6143
- maybeActivate(ev);
6144
- if (!activated) return;
6145
- opts.onMove(ev);
6146
- }
6147
- function cleanup() {
6148
- document.removeEventListener("pointermove", onMove);
6149
- document.removeEventListener("pointerup", onUp);
6150
- document.removeEventListener("pointercancel", onCancel);
6151
- }
6152
- function onUp(ev) {
6153
- cleanup();
6154
- try {
6155
- target.releasePointerCapture(ev.pointerId);
6156
- } catch {
6157
- }
6158
- if (!activated) {
6512
+ };
6513
+ const session = openPointerSession(e.currentTarget, e, {
6514
+ onMove: (ev) => {
6515
+ maybeActivate(ev);
6516
+ if (activated) opts.onMove(ev);
6517
+ },
6518
+ onEnd: (ev) => {
6519
+ if (activated) opts.onCommit(ev);
6520
+ else opts.onCancel?.();
6521
+ },
6522
+ onCancel: () => {
6159
6523
  opts.onCancel?.();
6160
- return;
6161
6524
  }
6162
- opts.onCommit(ev);
6163
- }
6164
- function onCancel() {
6165
- cleanup();
6166
- opts.onCancel?.();
6167
- }
6168
- document.addEventListener("pointermove", onMove);
6169
- document.addEventListener("pointerup", onUp);
6170
- document.addEventListener("pointercancel", onCancel);
6171
- return { isDragging: () => activated };
6525
+ });
6526
+ return {
6527
+ isDragging: () => activated,
6528
+ cancel: () => {
6529
+ session.cancel();
6530
+ }
6531
+ };
6172
6532
  }
6173
6533
 
6174
6534
  // src/core/device/targets.ts
@@ -8217,6 +8577,30 @@ var cloneAction = {
8217
8577
  enabled: () => true
8218
8578
  };
8219
8579
 
8580
+ // src/core/viewport/createVelocityTracker.ts
8581
+ function createVelocityTracker() {
8582
+ let samples = [];
8583
+ return {
8584
+ record(dx, dy, t) {
8585
+ samples.push({ dx, dy, t });
8586
+ const cutoff = t - 100;
8587
+ samples = samples.filter((s) => s.t >= cutoff);
8588
+ },
8589
+ getVelocity() {
8590
+ const s = samples;
8591
+ if (s.length < 2) return { vx: 0, vy: 0 };
8592
+ const dt = s[s.length - 1].t - s[0].t;
8593
+ if (dt === 0) return { vx: 0, vy: 0 };
8594
+ const totalDx = s.slice(1).reduce((acc, p) => acc + p.dx, 0);
8595
+ const totalDy = s.slice(1).reduce((acc, p) => acc + p.dy, 0);
8596
+ return { vx: totalDx / dt, vy: totalDy / dt };
8597
+ },
8598
+ reset() {
8599
+ samples = [];
8600
+ }
8601
+ };
8602
+ }
8603
+
8220
8604
  // src/interactions/actions/defaults/viewportDragPan.ts
8221
8605
  var viewportDragPanAction = {
8222
8606
  id: "viewport.dragPan",
@@ -8235,17 +8619,37 @@ var viewportDragPanAction = {
8235
8619
  requires: ["view"],
8236
8620
  invoker: {
8237
8621
  timing: "ongoing",
8238
- start(ctx) {
8622
+ start(ctx, opts) {
8239
8623
  const view = ctx.deps.view;
8240
8624
  if (!view) return {};
8241
8625
  const startView = view.get();
8242
- const scratch = { startView, view };
8626
+ const params = resolveParams(opts?.params) ?? {};
8627
+ const inertia = params.inertia === false ? void 0 : params.inertia;
8628
+ const scratch = {
8629
+ startView,
8630
+ view,
8631
+ axis: params.axis ?? "both",
8632
+ inertia,
8633
+ // Only track when a coast could actually happen, so the common
8634
+ // no-inertia drag does no per-move work.
8635
+ tracker: inertia && view.decay ? createVelocityTracker() : void 0,
8636
+ lastScreen: { x: 0, y: 0 }
8637
+ };
8243
8638
  return {
8244
8639
  kind: "pan",
8245
8640
  onMove(moveCtx) {
8246
8641
  if (!moveCtx.drag) return;
8247
8642
  const screen = moveCtx.drag.screenDelta ?? moveCtx.drag.delta;
8248
- const { x: screenDx, y: screenDy } = screen;
8643
+ const screenDx = scratch.axis === "y" ? 0 : screen.x;
8644
+ const screenDy = scratch.axis === "x" ? 0 : screen.y;
8645
+ if (scratch.tracker) {
8646
+ scratch.tracker.record(
8647
+ screenDx - scratch.lastScreen.x,
8648
+ screenDy - scratch.lastScreen.y,
8649
+ performance.now()
8650
+ );
8651
+ scratch.lastScreen = { x: screenDx, y: screenDy };
8652
+ }
8249
8653
  const sv = scratch.startView;
8250
8654
  scratch.view.set({
8251
8655
  ...sv,
@@ -8253,7 +8657,24 @@ var viewportDragPanAction = {
8253
8657
  y: sv.y - screenDy / sv.scale.y
8254
8658
  });
8255
8659
  },
8256
- onEnd() {
8660
+ onEnd(_endCtx, reason) {
8661
+ const { tracker, inertia: inertia2, view: v } = scratch;
8662
+ if (reason === "cancel" || !tracker || !inertia2 || !v.decay) return;
8663
+ const { vx, vy } = tracker.getVelocity();
8664
+ const scale2 = scratch.startView.scale;
8665
+ v.decay({
8666
+ // Screen px/ms → world units/ms, matching the onMove conversion.
8667
+ velocity: { vx: -vx / scale2.x, vy: -vy / scale2.y },
8668
+ friction: inertia2.friction,
8669
+ minSpeed: inertia2.minSpeed,
8670
+ boundary: inertia2.boundary,
8671
+ viewBounds: inertia2.bounds,
8672
+ initialPosition: { x: v.get().x, y: v.get().y },
8673
+ onTick: (dx, dy) => {
8674
+ const cur = v.get();
8675
+ v.set({ ...cur, x: cur.x + dx, y: cur.y + dy });
8676
+ }
8677
+ });
8257
8678
  }
8258
8679
  };
8259
8680
  }
@@ -8357,6 +8778,54 @@ function makeViewportZoomAction(opts = {}) {
8357
8778
  }
8358
8779
  var viewportZoomAction = makeViewportZoomAction();
8359
8780
 
8781
+ // src/interactions/actions/defaults/viewportWheelPan.ts
8782
+ var viewportWheelPanAction = {
8783
+ id: "viewport.wheelPan",
8784
+ label: "Pan (wheel)",
8785
+ group: "viewport",
8786
+ /**
8787
+ * Plain wheel → pan; Shift+wheel → horizontal pan (axis swap).
8788
+ * Cmd+wheel is claimed by `viewportZoomAction`. The matcher's strict
8789
+ * modifier check keeps the bindings exclusive: omitting `mods` forbids
8790
+ * any modifier, and `mods: { shift: true }` forbids the others.
8791
+ */
8792
+ defaultBinding: [
8793
+ { spec: { kind: "wheel" }, opts: {} },
8794
+ { spec: { kind: "wheel", mods: { shift: true } }, opts: { params: { swapAxis: true } } }
8795
+ ],
8796
+ requires: ["view"],
8797
+ invoker: {
8798
+ timing: "immediate",
8799
+ run(deps, params) {
8800
+ const view = deps.view;
8801
+ if (!view) return;
8802
+ const axis = params?.axis ?? "both";
8803
+ const deltaX = params?.deltaX ?? 0;
8804
+ const deltaY = params?.deltaY ?? 0;
8805
+ const current = view.get();
8806
+ const swap = params?.swapAxis === true;
8807
+ const dx = swap ? (deltaX !== 0 ? deltaX : deltaY) / current.scale.x : deltaX / current.scale.x;
8808
+ const dy = swap ? 0 : deltaY / current.scale.y;
8809
+ view.set({
8810
+ ...current,
8811
+ x: current.x + (axis === "y" ? 0 : dx),
8812
+ y: current.y + (axis === "x" ? 0 : dy)
8813
+ });
8814
+ }
8815
+ },
8816
+ enabled: () => true
8817
+ };
8818
+ function makeViewportWheelPanAction(opts = {}) {
8819
+ const axis = opts.axis ?? "both";
8820
+ return {
8821
+ ...viewportWheelPanAction,
8822
+ defaultBinding: [
8823
+ { spec: { kind: "wheel" }, opts: { params: { axis } } },
8824
+ { spec: { kind: "wheel", mods: { shift: true } }, opts: { params: { swapAxis: true, axis } } }
8825
+ ]
8826
+ };
8827
+ }
8828
+
8360
8829
  // src/interactions/dispatcher/predicates.ts
8361
8830
  function bodyPredicate(fn) {
8362
8831
  return Object.assign(fn, { readsAffordance: false });
@@ -13131,6 +13600,7 @@ function useGestureDispatcher(opts) {
13131
13600
  };
13132
13601
  const heldKeys = /* @__PURE__ */ new Set();
13133
13602
  const activePointers = /* @__PURE__ */ new Set();
13603
+ const buttonsReportedFor = /* @__PURE__ */ new Set();
13134
13604
  const pointerPositions = /* @__PURE__ */ new Map();
13135
13605
  let multiTouchStart = null;
13136
13606
  const TAP_THRESHOLD_PX = 8;
@@ -13273,8 +13743,10 @@ function useGestureDispatcher(opts) {
13273
13743
  };
13274
13744
  const onPointerDown = (e) => {
13275
13745
  if ((e.button ?? 0) !== 0) return;
13746
+ if (activePointers.has(e.pointerId)) onPointerUp(e);
13276
13747
  routeDown(e.pointerId, e.clientX, e.clientY);
13277
13748
  activePointers.add(e.pointerId);
13749
+ if (reportsButtons(e)) buttonsReportedFor.add(e.pointerId);
13278
13750
  pointerPositions.set(e.pointerId, { x: e.clientX, y: e.clientY });
13279
13751
  try {
13280
13752
  canvas?.setPointerCapture?.(e.pointerId);
@@ -13353,6 +13825,10 @@ function useGestureDispatcher(opts) {
13353
13825
  }
13354
13826
  };
13355
13827
  const onPointerMove = (e) => {
13828
+ if (activePointers.has(e.pointerId) && buttonsReportedFor.has(e.pointerId) && isMissedRelease(e)) {
13829
+ onPointerUp(e);
13830
+ return;
13831
+ }
13356
13832
  routeAt(e.pointerId, e.clientX, e.clientY);
13357
13833
  if (activePointers.has(e.pointerId)) {
13358
13834
  pointerPositions.set(e.pointerId, { x: e.clientX, y: e.clientY });
@@ -13502,6 +13978,7 @@ function useGestureDispatcher(opts) {
13502
13978
  cancelLongPress(e.pointerId);
13503
13979
  const prevSize = activePointers.size;
13504
13980
  activePointers.delete(e.pointerId);
13981
+ buttonsReportedFor.delete(e.pointerId);
13505
13982
  pointerPositions.delete(e.pointerId);
13506
13983
  const staysUnderThreshold = bufferedDown.delete(e.pointerId);
13507
13984
  if (prevSize >= 2 && activePointers.size < 2) {
@@ -13609,6 +14086,7 @@ function useGestureDispatcher(opts) {
13609
14086
  routeAt(e.pointerId, e.clientX, e.clientY);
13610
14087
  cancelLongPress(e.pointerId);
13611
14088
  activePointers.delete(e.pointerId);
14089
+ buttonsReportedFor.delete(e.pointerId);
13612
14090
  pointerPositions.delete(e.pointerId);
13613
14091
  lastPointerDown.delete(e.pointerId);
13614
14092
  bufferedDown.delete(e.pointerId);
@@ -13624,6 +14102,11 @@ function useGestureDispatcher(opts) {
13624
14102
  dispatch2(ev);
13625
14103
  routeRelease(e.pointerId);
13626
14104
  };
14105
+ const onLostCapture = (e) => {
14106
+ const pe = e;
14107
+ if (!activePointers.has(pe.pointerId)) return;
14108
+ onPointerCancel(pe);
14109
+ };
13627
14110
  const onContextMenu = (e) => {
13628
14111
  e.preventDefault();
13629
14112
  routeAt(null, e.clientX, e.clientY);
@@ -13713,6 +14196,7 @@ function useGestureDispatcher(opts) {
13713
14196
  canvas?.addEventListener("pointermove", onPointerMove);
13714
14197
  canvas?.addEventListener("pointerup", onPointerUp);
13715
14198
  canvas?.addEventListener("pointercancel", onPointerCancel);
14199
+ canvas?.addEventListener(LOST_CAPTURE_EVENT, onLostCapture);
13716
14200
  canvas?.addEventListener("contextmenu", onContextMenu);
13717
14201
  canvas?.addEventListener("dragenter", onDragOver);
13718
14202
  canvas?.addEventListener("dragover", onDragOver);
@@ -13733,6 +14217,7 @@ function useGestureDispatcher(opts) {
13733
14217
  canvas?.removeEventListener("pointermove", onPointerMove);
13734
14218
  canvas?.removeEventListener("pointerup", onPointerUp);
13735
14219
  canvas?.removeEventListener("pointercancel", onPointerCancel);
14220
+ canvas?.removeEventListener(LOST_CAPTURE_EVENT, onLostCapture);
13736
14221
  canvas?.removeEventListener("contextmenu", onContextMenu);
13737
14222
  canvas?.removeEventListener("dragenter", onDragOver);
13738
14223
  canvas?.removeEventListener("dragover", onDragOver);
@@ -13775,26 +14260,7 @@ function computeWheelAction(state, input, bounds = { min: DEFAULT_MIN_ZOOM, max:
13775
14260
  };
13776
14261
  }
13777
14262
  function useVelocityTracker() {
13778
- const samplesRef = useRef([]);
13779
- return useMemo(() => ({
13780
- record(dx, dy, t) {
13781
- samplesRef.current.push({ dx, dy, t });
13782
- const cutoff = t - 100;
13783
- samplesRef.current = samplesRef.current.filter((s) => s.t >= cutoff);
13784
- },
13785
- getVelocity() {
13786
- const s = samplesRef.current;
13787
- if (s.length < 2) return { vx: 0, vy: 0 };
13788
- const dt = s[s.length - 1].t - s[0].t;
13789
- if (dt === 0) return { vx: 0, vy: 0 };
13790
- const totalDx = s.slice(1).reduce((acc, p) => acc + p.dx, 0);
13791
- const totalDy = s.slice(1).reduce((acc, p) => acc + p.dy, 0);
13792
- return { vx: totalDx / dt, vy: totalDy / dt };
13793
- },
13794
- reset() {
13795
- samplesRef.current = [];
13796
- }
13797
- }), []);
14263
+ return useMemo(() => createVelocityTracker(), []);
13798
14264
  }
13799
14265
  var SPRING_DAMPING = 0.5;
13800
14266
  function useDecayLoop() {
@@ -17762,7 +18228,9 @@ function useRotateTool(adapter, options) {
17762
18228
  [overlay]
17763
18229
  );
17764
18230
  }
17765
- function useHandTool(_opts = {}) {
18231
+ function useHandTool(opts = {}) {
18232
+ const optsRef = useRef(opts);
18233
+ optsRef.current = opts;
17766
18234
  return useMemo(
17767
18235
  () => Object.assign(defineViewportTool({
17768
18236
  id: "hand",
@@ -17780,7 +18248,16 @@ function useHandTool(_opts = {}) {
17780
18248
  // viewportDragPanAction, which also owns the grabbing cursor
17781
18249
  // (`Action.activeCursor`) the tool used to declare as an `engaged`
17782
18250
  // phase entry.
17783
- bindings: [{ spec: { kind: "drag" }, actionId: "viewport.dragPan" }]
18251
+ bindings: [{
18252
+ spec: { kind: "drag" },
18253
+ actionId: "viewport.dragPan",
18254
+ opts: {
18255
+ params: () => ({
18256
+ axis: optsRef.current.axis ?? "both",
18257
+ inertia: optsRef.current.inertia
18258
+ })
18259
+ }
18260
+ }]
17784
18261
  }),
17785
18262
  []
17786
18263
  );
@@ -19151,24 +19628,25 @@ function projectBounds(b, view) {
19151
19628
  const [sx, sy] = worldToScreen(b.x, b.y, t);
19152
19629
  return { ...b, x: sx, y: sy, width: b.width * view.scale.x, height: b.height * view.scale.y };
19153
19630
  }
19631
+ function leavesOf(id, getChildren, isContainer) {
19632
+ if (!isContainer(id)) return [id];
19633
+ const out = [];
19634
+ const visit = (nid) => {
19635
+ const kids = getChildren(nid);
19636
+ if (kids.length === 0) {
19637
+ if (!isContainer(nid)) out.push(nid);
19638
+ return;
19639
+ }
19640
+ for (const k of kids) visit(k);
19641
+ };
19642
+ visit(id);
19643
+ return out;
19644
+ }
19154
19645
  function composeSelectionPose(opts) {
19155
19646
  const { moveOverlay, resizeOverlay, getStoredPose, getChildren, isContainer } = opts;
19156
19647
  const getBounds = opts.getBounds ?? ((pose) => pose);
19157
19648
  const fromBounds = opts.fromBounds ?? ((bounds) => bounds);
19158
- const leavesOf = (id) => {
19159
- if (!getChildren || !isContainer || !isContainer(id)) return [id];
19160
- const out = [];
19161
- const visit = (nid) => {
19162
- const kids = getChildren(nid);
19163
- if (kids.length === 0) {
19164
- if (!isContainer(nid)) out.push(nid);
19165
- return;
19166
- }
19167
- for (const k of kids) visit(k);
19168
- };
19169
- visit(id);
19170
- return out;
19171
- };
19649
+ const selectionLeaves = (id) => getChildren && isContainer ? leavesOf(id, getChildren, isContainer) : [id];
19172
19650
  const resolveLeaf = (id) => {
19173
19651
  const moved = moveOverlay?.poses.get(id);
19174
19652
  if (moved !== void 0) return moved;
@@ -19177,7 +19655,7 @@ function composeSelectionPose(opts) {
19177
19655
  };
19178
19656
  return (id) => {
19179
19657
  if (isContainer?.(id)) {
19180
- const leaves = leavesOf(id);
19658
+ const leaves = selectionLeaves(id);
19181
19659
  if (leaves.length === 0) return null;
19182
19660
  const containerResizeLeafPoses = resizeOverlay && resizeOverlay.id === id ? resizeOverlay.leafPoses : void 0;
19183
19661
  const leafBounds = [];
@@ -19208,26 +19686,12 @@ function makeContainerAwareBoundsResolver(getPose, getBounds, getChildren, isCon
19208
19686
  return p === null ? null : getBounds(p);
19209
19687
  };
19210
19688
  }
19211
- const leavesOf = (id) => {
19212
- if (!isContainer(id)) return [id];
19213
- const out = [];
19214
- const visit = (nid) => {
19215
- const kids = getChildren(nid);
19216
- if (kids.length === 0) {
19217
- if (!isContainer(nid)) out.push(nid);
19218
- return;
19219
- }
19220
- for (const k of kids) visit(k);
19221
- };
19222
- visit(id);
19223
- return out;
19224
- };
19225
19689
  return (id) => {
19226
19690
  if (!isContainer(id)) {
19227
19691
  const p = getPose(id);
19228
19692
  return p === null ? null : getBounds(p);
19229
19693
  }
19230
- const leaves = leavesOf(id);
19694
+ const leaves = leavesOf(id, getChildren, isContainer);
19231
19695
  if (leaves.length === 0) return null;
19232
19696
  const leafBounds = [];
19233
19697
  for (const leafId of leaves) {
@@ -20394,7 +20858,7 @@ function CanvasInner(props, ref) {
20394
20858
  return {
20395
20859
  worldX,
20396
20860
  worldY,
20397
- modifiers: m ? { alt: m.alt, shift: m.shift, meta: m.meta, ctrl: m.ctrl, space: false } : { alt: false, shift: false, meta: false, ctrl: false, space: false },
20861
+ modifiers: m ? { alt: m.alt, shift: m.shift, meta: m.meta, ctrl: m.ctrl } : { alt: false, shift: false, meta: false, ctrl: false },
20398
20862
  selection: effectiveSelectionRefForCtx.current,
20399
20863
  adapter: effectiveAdapterRefForCtx.current,
20400
20864
  applyOps: (ops, label) => {
@@ -24097,10 +24561,10 @@ function boundsOf(coords) {
24097
24561
  }
24098
24562
  return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
24099
24563
  }
24100
- function useViewDepSource(currentViewRef, onViewChange, recenter, hostSize, animation) {
24101
- const wiring = useRef({ currentViewRef, onViewChange, recenter, hostSize, animation });
24102
- wiring.current = { currentViewRef, onViewChange, recenter, hostSize, animation };
24103
- const shape = `${recenter ? "r" : ""}${hostSize ? "h" : ""}${animation ? "a" : ""}`;
24564
+ function useViewDepSource(currentViewRef, onViewChange, recenter, hostSize, animation, decay) {
24565
+ const wiring = useRef({ currentViewRef, onViewChange, recenter, hostSize, animation, decay });
24566
+ wiring.current = { currentViewRef, onViewChange, recenter, hostSize, animation, decay };
24567
+ const shape = `${recenter ? "r" : ""}${hostSize ? "h" : ""}${animation ? "a" : ""}${decay ? "d" : ""}`;
24104
24568
  const shapeRef = useRef(null);
24105
24569
  const viewApiRef = useRef(null);
24106
24570
  if (viewApiRef.current === null || shapeRef.current !== shape) {
@@ -24119,6 +24583,10 @@ function useViewDepSource(currentViewRef, onViewChange, recenter, hostSize, anim
24119
24583
  animate: (to, opts) => wiring.current.animation.animate(to, opts),
24120
24584
  stopAnimation: () => wiring.current.animation.stop(),
24121
24585
  animationTarget: () => wiring.current.animation.target()
24586
+ } : {},
24587
+ ...decay ? {
24588
+ decay: (config) => wiring.current.decay.start(config),
24589
+ stopDecay: () => wiring.current.decay.cancel()
24122
24590
  } : {}
24123
24591
  };
24124
24592
  }
@@ -24882,7 +25350,6 @@ function useActionsPropResolver(actions) {
24882
25350
  }
24883
25351
  if (!prop) return;
24884
25352
  const unregisters = [];
24885
- const restores = [];
24886
25353
  const warnedIds = /* @__PURE__ */ new Set();
24887
25354
  for (const [slotId, entry] of Object.entries(prop)) {
24888
25355
  if (entry === null) {
@@ -24910,64 +25377,30 @@ function useActionsPropResolver(actions) {
24910
25377
  }
24911
25378
  const { id: _drop, ...rest } = entry;
24912
25379
  const merged = { ...existing, ...rest };
24913
- restores.push(() => {
24914
- reg.register(existing);
24915
- });
24916
25380
  unregisters.push(reg.register(merged));
24917
25381
  }
24918
25382
  return () => {
24919
25383
  for (const u of unregisters) u();
24920
- for (const r of restores) r();
24921
25384
  };
24922
25385
  }, [reg, actions]);
24923
25386
  }
24924
-
24925
- // src/interactions/actions/defaults/viewportWheelPan.ts
24926
- var viewportWheelPanAction = {
24927
- id: "viewport.wheelPan",
24928
- label: "Pan (wheel)",
24929
- group: "viewport",
24930
- /**
24931
- * Plain wheel → pan; Shift+wheel → horizontal pan (axis swap).
24932
- * Cmd+wheel is claimed by `viewportZoomAction`. The matcher's strict
24933
- * modifier check keeps the bindings exclusive: omitting `mods` forbids
24934
- * any modifier, and `mods: { shift: true }` forbids the others.
24935
- */
24936
- defaultBinding: [
24937
- { spec: { kind: "wheel" }, opts: {} },
24938
- { spec: { kind: "wheel", mods: { shift: true } }, opts: { params: { swapAxis: true } } }
24939
- ],
24940
- requires: ["view"],
24941
- invoker: {
24942
- timing: "immediate",
24943
- run(deps, params) {
24944
- const view = deps.view;
24945
- if (!view) return;
24946
- const deltaX = params?.deltaX ?? 0;
24947
- const deltaY = params?.deltaY ?? 0;
24948
- const current = view.get();
24949
- const swap = params?.swapAxis === true;
24950
- const dx = swap ? (deltaX !== 0 ? deltaX : deltaY) / current.scale.x : deltaX / current.scale.x;
24951
- const dy = swap ? 0 : deltaY / current.scale.y;
24952
- view.set({ ...current, x: current.x + dx, y: current.y + dy });
24953
- }
24954
- },
24955
- enabled: () => true
24956
- };
24957
-
24958
- // src/canvas/SceneCanvas/useViewportActions.ts
24959
25387
  function useViewportActions(args) {
24960
25388
  const { pan, zoom, pinchZoom } = args;
24961
25389
  const reg = useActionsRegistry();
24962
25390
  const regRef = useRef(reg);
24963
25391
  regRef.current = reg;
25392
+ const panKey = typeof pan === "object" ? JSON.stringify(pan) : String(pan);
24964
25393
  const zoomKey = typeof zoom === "object" ? JSON.stringify(zoom) : String(zoom);
24965
25394
  const pinchKey = typeof pinchZoom === "object" ? JSON.stringify(pinchZoom) : String(pinchZoom);
24966
25395
  useEffect(() => {
24967
25396
  const r = regRef.current;
24968
25397
  if (!r) return;
24969
25398
  const unregisters = [];
24970
- if (pan) unregisters.push(r.register(viewportWheelPanAction));
25399
+ if (pan) {
25400
+ unregisters.push(
25401
+ r.register(makeViewportWheelPanAction(typeof pan === "object" ? pan : {}))
25402
+ );
25403
+ }
24971
25404
  if (zoom) {
24972
25405
  const zoomAction = typeof zoom === "object" ? makeViewportZoomAction(zoom) : makeViewportZoomAction();
24973
25406
  unregisters.push(r.register(zoomAction));
@@ -24982,7 +25415,7 @@ function useViewportActions(args) {
24982
25415
  return () => {
24983
25416
  for (const u of unregisters) u();
24984
25417
  };
24985
- }, [pan, zoomKey, pinchKey]);
25418
+ }, [panKey, zoomKey, pinchKey]);
24986
25419
  }
24987
25420
 
24988
25421
  // src/core/scene/NodeRouting.ts
@@ -25417,13 +25850,13 @@ function SceneCanvasInner(props, ref) {
25417
25850
  const viewportRegistered = !!viewport;
25418
25851
  const inertiaEnabled = !!viewport?.inertia;
25419
25852
  const inertiaObj = typeof viewport?.inertia === "object" ? viewport.inertia : void 0;
25420
- const handToolInertia = inertiaEnabled && inertiaObj ? {
25421
- friction: inertiaObj.friction,
25422
- minSpeed: inertiaObj.minSpeed,
25423
- boundary: inertiaObj.boundary,
25424
- bounds: inertiaObj.bounds
25853
+ const handToolInertia = inertiaEnabled ? {
25854
+ friction: inertiaObj?.friction,
25855
+ minSpeed: inertiaObj?.minSpeed,
25856
+ boundary: inertiaObj?.boundary,
25857
+ bounds: inertiaObj?.bounds
25425
25858
  } : void 0;
25426
- const handTool = useHandTool(handToolInertia ? { } : {});
25859
+ const handTool = useHandTool(handToolInertia ? { inertia: handToolInertia } : {});
25427
25860
  const viewportAmbient = [];
25428
25861
  const baseRequestedTools = defaultTools ?? (toolBundle ? BUNDLE_TOOLS[toolBundle] : null) ?? (viewportRegistered ? ["select", "rotate", "hand"] : ["select", "rotate"]);
25429
25862
  const KNOWN_BUILTIN_IDS = /* @__PURE__ */ new Set([
@@ -25876,7 +26309,7 @@ function SceneCanvasInner(props, ref) {
25876
26309
  dispatcher,
25877
26310
  getActionRef,
25878
26311
  pickEvery: internalPickEvery,
25879
- viewportPanEnabled: viewport?.pan !== false,
26312
+ viewportPanEnabled: viewport?.pan ?? true,
25880
26313
  viewportZoom: resolvedViewportZoom,
25881
26314
  viewportPinchZoom: viewport?.pinchZoom ?? true,
25882
26315
  viewportRecenter: viewport?.recenter,
@@ -26167,6 +26600,7 @@ function StandardActionsRegistrar({
26167
26600
  getActionRef.current = null;
26168
26601
  };
26169
26602
  }, [registry2, getActionRef]);
26603
+ const decayLoop = useDecayLoop();
26170
26604
  const view = useViewDepSource(
26171
26605
  currentViewRef,
26172
26606
  onViewChange,
@@ -26175,7 +26609,8 @@ function StandardActionsRegistrar({
26175
26609
  const el = canvasRef.current;
26176
26610
  return el ? { width: el.clientWidth, height: el.clientHeight } : null;
26177
26611
  },
26178
- viewAnimation
26612
+ viewAnimation,
26613
+ decayLoop
26179
26614
  );
26180
26615
  const sceneAsHistory = scene;
26181
26616
  useStandardActions({ selection, scene, view, history: sceneAsHistory });
@@ -26635,8 +27070,7 @@ function MinimapCanvasInner(props) {
26635
27070
  }, [mainView, mainViewDims, indicatorStyle]);
26636
27071
  const extraCommands = useMemo(() => [indicatorCmd], [indicatorCmd]);
26637
27072
  const localCanvasRef = useRef(null);
26638
- const dragActiveRef = useRef(false);
26639
- const capturedPointerIdRef = useRef(null);
27073
+ const sessionRef = useRef(null);
26640
27074
  const mainViewRef = useRef(mainView);
26641
27075
  const mainViewDimsRef = useRef(mainViewDims);
26642
27076
  const onMainViewChangeRef = useRef(onMainViewChange);
@@ -26665,36 +27099,18 @@ function MinimapCanvasInner(props) {
26665
27099
  }, []);
26666
27100
  const handlePointerDown = useCallback((ev) => {
26667
27101
  const canvas = localCanvasRef.current;
26668
- if (!canvas) return;
27102
+ if (!canvas || sessionRef.current) return;
26669
27103
  if (ev.pointerType === "mouse" && ev.button !== 0) return;
26670
- try {
26671
- canvas.setPointerCapture(ev.pointerId);
26672
- } catch {
26673
- }
26674
- dragActiveRef.current = true;
26675
- capturedPointerIdRef.current = ev.pointerId;
26676
- recenterFromPointer(ev);
26677
- }, [recenterFromPointer]);
26678
- const handlePointerMove = useCallback((ev) => {
26679
- if (!dragActiveRef.current) return;
26680
- if (capturedPointerIdRef.current !== null && ev.pointerId !== capturedPointerIdRef.current) {
26681
- return;
26682
- }
27104
+ const done = () => {
27105
+ sessionRef.current = null;
27106
+ };
27107
+ sessionRef.current = openPointerSession(canvas, ev, {
27108
+ onMove: recenterFromPointer,
27109
+ onEnd: done,
27110
+ onCancel: done
27111
+ });
26683
27112
  recenterFromPointer(ev);
26684
27113
  }, [recenterFromPointer]);
26685
- const releaseCapture = useCallback((ev) => {
26686
- const canvas = localCanvasRef.current;
26687
- if (canvas) {
26688
- try {
26689
- if (canvas.hasPointerCapture?.(ev.pointerId)) {
26690
- canvas.releasePointerCapture(ev.pointerId);
26691
- }
26692
- } catch {
26693
- }
26694
- }
26695
- dragActiveRef.current = false;
26696
- capturedPointerIdRef.current = null;
26697
- }, []);
26698
27114
  const setCanvasRef = useCallback((el) => {
26699
27115
  localCanvasRef.current = el;
26700
27116
  if (typeof canvasRef === "function") {
@@ -26707,16 +27123,11 @@ function MinimapCanvasInner(props) {
26707
27123
  const canvas = localCanvasRef.current;
26708
27124
  if (!canvas) return;
26709
27125
  canvas.addEventListener("pointerdown", handlePointerDown);
26710
- canvas.addEventListener("pointermove", handlePointerMove);
26711
- canvas.addEventListener("pointerup", releaseCapture);
26712
- canvas.addEventListener("pointercancel", releaseCapture);
26713
27126
  return () => {
26714
27127
  canvas.removeEventListener("pointerdown", handlePointerDown);
26715
- canvas.removeEventListener("pointermove", handlePointerMove);
26716
- canvas.removeEventListener("pointerup", releaseCapture);
26717
- canvas.removeEventListener("pointercancel", releaseCapture);
27128
+ sessionRef.current?.cancel();
26718
27129
  };
26719
- }, [handlePointerDown, handlePointerMove, releaseCapture]);
27130
+ }, [handlePointerDown]);
26720
27131
  return /* @__PURE__ */ jsx(
26721
27132
  SceneViewCanvas,
26722
27133
  {
@@ -30856,6 +31267,6 @@ function mergeContributions(...bundles) {
30856
31267
  return out;
30857
31268
  }
30858
31269
 
30859
- export { ALWAYS, ANCHOR_HIT_BASE_PX, ActionsProviderIfRoot, ActiveToolContextProvider, ActiveToolContextProviderIfRoot, ArrowIcon, BUILTIN_MARKERS, BUNDLE_TOOLS, COARSE_TARGET_SCALE, CURVE_REPS, CanvasView, ColorOverrideRegistry, CropIcon, CursorCoordsHud, DEFAULT_ALPHA_DECAY, DEFAULT_ALPHA_MIN, DEFAULT_DEBUG_STROKES, DEFAULT_DEBUG_THEME, DEFAULT_DEVICE_PROFILE, DEFAULT_FILL_COLOR, DEFAULT_HANDLE_SIZE2 as DEFAULT_HANDLE_SIZE, DEFAULT_MAX_ZOOM, DEFAULT_MIN_ZOOM, DEFAULT_PALETTE, DEFAULT_ROTATION_HANDLE_DISTANCE, DEFAULT_SHAPE_FILL, DEFAULT_STROKE_COLOR, DEFAULT_VELOCITY_DECAY, DepRegistryProviderIfRoot, DeviceProfileProvider, DivideIcon, EASINGS, EllipseIcon, ExcludeIcon, EyedropperIcon, FALLBACK_FIT_VIEW, GHOST_STROKE, HANDLE_BASE_PX, HandIcon, IDENTITY_COLOR_MATRIX, IDENTITY_JOINT, IDENTITY_POSE_COMPOSITION, ImageIcon, IntersectIcon, KIT_SHAPE_KINDS, LassoIcon, LineIcon, MIXED, MOVE_ANCHORS, MinimapCanvas, NEVER, OUTLINE_MIN_SCREEN_PX, PAINTED_CURSOR_LAYER_ID, PATH_ANCHOR_CHROME_ID, PenIcon, PencilIcon, PickHud, PointerProviderIfRoot, PolygonIcon, RECT_ALIGN_PROJECTION, ROTATION_HANDLE_BASE_PX, RectIcon, SPRING_PRESETS, SceneCanvas, SceneViewCanvas, SelectIcon, SelectionContextProvider, SelectionContextProviderIfRoot, ShaderCompileError, StarIcon, SubtractIcon, TOOL_PREF_KINDS, TextIcon, UnionIcon, UnknownIcon, VERSION, VIEW_ANIMATION_KEY, WeaselProvider, WeaselRenderer, _resetMarkersForTests, _resetPaintKindsForTests, aabbCenter, actionIs, actionShortcuts, alignDeltaFor, alignInsertBehavior, alignMoveBehavior, alignResizeBehavior, always, and, animateLifecycle, animateOnSetPose, annulusSemiAxes, applyBooleanOp, applyStyleToRange, areaSelectAction, arrayAdapter, asNodeId, asPaint, axisAlignedBounds, bezierCubic, bezierQuadratic, blendPoses, buildChromeCtx, buildGradientRamp, buildRuleCtx, buildSceneViewCommands, canBringForward, canHover, canSendBackward, capabilityAll, capabilityIn, capabilityIs, capabilityNot, caretIndexAt, cellAt, charOffsetToDomPosition, clampView, clearSelectionAction, clientToCanvas, clipboardCopyAction, clipboardCutAction, cloneAction, coarsePointer, composeAffordanceLayer, composePath, composeRectPose, composeSelectionPose, composeWorldPose, computeFitView, computeFitViewport, computeIndicatorCommand, computeWheelAction, cond, constrainTo45, containedThenNearest, countPathAnchors, createCellHighlightLayer, createChildrenLayer, createCornerResizeAffordance, createDebugOverlayLayer, createDebugSink, createDispatcher, createGridLayer, createGuidesLayer, createHistory2 as createHistory, createMoveToIndexOp, createNodeProperties, createNodeRouting, createPaintedCursorLayer, createPaintedCursorState, createParallaxLayer, createPathAnchorAffordances, createPathEditingOverlayLayer, createPathLayer, createPenPreviewLayer, createReorderOp, createRotationAffordance, createScene, createSelectionHandlesLayer, createSelectionOutlineLayer, createSelectionOverlayLayer, createSetDataOp, createSetLayerOp, createSetPathOp, createSetTextOp, createTextLayer, createViewResolver, createViewportLayer, cubicBezierEasing, cycleVertexColors, decomposePath, decomposeRectPose, defaultCommitAdapter, defaultDrawOne, defaultLabelTextRenderer, defaultNodeProperties, defaultNodeRouting, defaultVisibilityRules, defineTool, defineViewportTool, deriveAlignmentGuides, deriveParallaxView, describeRule, domPositionToCharOffset, domToRuns, drawLayers, drawOneLayer, easeIn, easeInBack, easeInBounce, easeInCirc, easeInCubic, easeInElastic, easeInExpo, easeInOut, easeInOutBack, easeInOutBounce, easeInOutCirc, easeInOutCubic, easeInOutElastic, easeInOutExpo, easeInOutQuad, easeInOutQuart, easeInOutQuint, easeInOutSine, easeInQuad, easeInQuart, easeInQuint, easeInSine, easeOut, easeOutBack, easeOutBounce, easeOutCirc, easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, editAnchorsAction, enterTextEditAction, enumerateAnchors, evaluate, fillInPoseFrame, fillToBoundsFrame, findNodeShape, findShapeInk, findShapeSilhouette, fitTextPose, fitToBounds, fitViewToBounds, fitZoom, focused, forEachCoalesced, frameRect, freeform, gateLayer, gesturing, getAlpha01, getImageBitmap, getMarker, getNodeShapes, getPaintKind, getStylusData, gradientForBounds, gradientGeometry, hexToRgba, hitAffordanceRegions, hitAnchor, hitRotationHandle, hostAnchorCss, hostAnchorRect, hovering, hoveringSelected, imageStatus, inferredNodeProperties, inferredNodeRouting, insertAction, interpolateView, isBuiltinToolPref, isEditableTarget2 as isEditableTarget, isGradientFill, isLayerPainted, isLayerVisible, isPatternSpec, keySpecShortcut, lassoSelectAction, lerpColorArray, lerpOklab, lerpOklch, linear, listMarkers, listPaintKinds, liveScope, makePinchZoomAction, makeViewportZoomAction, markerDrawCommands, markerInset, markerKeyOf, markerSites, mat3, matchAlignment, matchesKeyBinding, meanScale, mergeAlphaFromPrev, mergeContributions, modeIn, modeIs, modeNot, modifierHeld, momentum, moveAction, multiActive, nearest, nearestWithin, nestedHitTester, never, nodeHasFlag, none, normalizeHex, not, nurbs, oklabToOklch, oklabToSrgbU8, oklchToOklab, openFilePicker, or, paintAlpha, paintKindOf, paintWithAlpha, paintWithColor, parseColor, parseColorToRgba255, parseDebugFlags, pathContainsPoint, pathContainsPolygon, pathContainsRect, pathDistanceToPoint, pathDivide, pathExclude, pathFromD, pathInPoseFrame, pathInWorld, pathIntersect, pathIntersectsPolygon, pathIntersectsRect, pathOriginProjection, pathSubtract, pathToAnchors, pathUnion, pickTopMostHit, pinchZoomAction, planPixelRender, pointInRotatedRect, pointInTextPose, poseRotationOf, pressureToWidth, rainbowVertexColors, rebaseLocalPose, rectCorners, registerContentHandler, registerMarker, registerNodeShape, registerPaintKind, registerProgram, renderLabel, renderSceneToCanvas, renderSceneToPixels, resizeAction, resolveColor, resolveDeviceProfile, resolveEasing, resolveFillPattern, resolveMarkerSize, resolveParams, resolvePatternSpec, resolveSkeleton, resolveStrokeWidth, resolveVisibility, rgbaToHex, rotateAction, rotateAroundAABBCenter, rotatePathAround, rotatePoint, rotatedRectCorners, rotationDegreesUnit, rotationHandle, roundToCell, runsToDom, sampleGradientStops, sampleTrack, sceneFromJSON, sceneNodeClientRect, sceneSelectionStore, sceneToAdapter, scopeBindings, screenToWorld, selectFromLasso, selectionAtLeast, selectionEmpty, selectionIs, setFlagOverRange, shapeCoversPoint, sliceAction, snapPoint, solid, solidVertexColors, specificity, spiro, splitPathByLine, splitSubpaths, springPose, springVertexColors, srgbU8ToOklab, staggerVertexColors, startThresholdDrag, strokeInsets, strokeOf, strokeWith, styleAtRange, subscribeImageReady, tessellate, tessellateStroke, textCommand, textCommandFromRuns, tileGrid, toHex8, translatePoseViaDescriptor, translateRectPose, trimPolyline, tweenPose, tweenVertexColors, unionAABB, unionBounds, unionBoundsPath, useActiveToolContext, useAlign, useAnimator, useArrayAdapter, useAutoCenter, useBooleans, useBooleansAdapter, useCanvasFocus, useCanvasSize, useDecayLoop, useDeviceProfile, useDistribute, useDragGesture, useDragHandle, useDragRadial, useDragRect, useDropZone, useEllipseTool, useEyedropperTool, useGestureDispatcher, useGridCellHover, useGuides, useHandTool, useHandleDrag, useHostAnchor, useHoverTracking, useImageTool, useKeybindings, useLassoTool, useLineTool, useNodeOverlayFrame, useOptionalActiveToolContext, usePenTool, usePencilTool, usePinchGesture, usePinchZoomTool, usePointerStylus, usePolygonTool, usePublishSelection, useRectTool, useResizePolicy, useRotateTool, useScene, useSceneAdapter, useSceneTextEdit, useSelectTool, useSelection, useSelectionContext, useSimulation, useSliceDep, useStandardActions, useStarTool, useTextEdit, useTextTool, useTools, useVelocityTracker, useViewAnimation, useVisibleRaf, useZoom, viewToMat3, viewToTransform, viewportDragPanAction, viewportZoomAction, viewportsAt, visualBoundsViaDescriptor, when, withAlpha01, withCoord, withGradientKind, worldEditToStorage, worldPoseLookup, worldToScreen, zoomAt, zoomAtLeast };
30860
- //# sourceMappingURL=chunk-HYJL7OOJ.js.map
30861
- //# sourceMappingURL=chunk-HYJL7OOJ.js.map
31270
+ export { ALWAYS, ANCHOR_HIT_BASE_PX, ActionsProviderIfRoot, ActiveToolContextProvider, ActiveToolContextProviderIfRoot, ArrowIcon, BUILTIN_MARKERS, BUNDLE_TOOLS, COARSE_TARGET_SCALE, CURVE_REPS, CanvasView, ColorOverrideRegistry, CropIcon, CursorCoordsHud, DEFAULT_ALPHA_DECAY, DEFAULT_ALPHA_MIN, DEFAULT_DEBUG_STROKES, DEFAULT_DEBUG_THEME, DEFAULT_DEVICE_PROFILE, DEFAULT_FILL_COLOR, DEFAULT_HANDLE_SIZE2 as DEFAULT_HANDLE_SIZE, DEFAULT_MAX_ZOOM, DEFAULT_MIN_ZOOM, DEFAULT_PALETTE, DEFAULT_ROTATION_HANDLE_DISTANCE, DEFAULT_SHAPE_FILL, DEFAULT_STROKE_COLOR, DEFAULT_VELOCITY_DECAY, DepRegistryProviderIfRoot, DeviceProfileProvider, DivideIcon, EASINGS, EllipseIcon, ExcludeIcon, EyedropperIcon, FALLBACK_FIT_VIEW, GHOST_STROKE, HANDLE_BASE_PX, HandIcon, IDENTITY_COLOR_MATRIX, IDENTITY_JOINT, IDENTITY_POSE_COMPOSITION, ImageIcon, IntersectIcon, KIT_SHAPE_KINDS, LassoIcon, LineIcon, MIXED, MOVE_ANCHORS, MinimapCanvas, NEVER, OUTLINE_MIN_SCREEN_PX, PAINTED_CURSOR_LAYER_ID, PATH_ANCHOR_CHROME_ID, PenIcon, PencilIcon, PickHud, PointerProviderIfRoot, PolygonIcon, RECT_ALIGN_PROJECTION, ROTATION_HANDLE_BASE_PX, RectIcon, SPRING_PRESETS, SPRITE_STRIDE, SceneCanvas, SceneViewCanvas, SelectIcon, SelectionContextProvider, SelectionContextProviderIfRoot, ShaderCompileError, StarIcon, SubtractIcon, TOOL_PREF_KINDS, TextIcon, UnionIcon, UnknownIcon, VERSION, VIEW_ANIMATION_KEY, WeaselProvider, WeaselRenderer, _resetMarkersForTests, _resetPaintKindsForTests, aabbCenter, actionIs, actionShortcuts, alignDeltaFor, alignInsertBehavior, alignMoveBehavior, alignResizeBehavior, always, and, animateLifecycle, animateOnSetPose, annulusSemiAxes, applyBooleanOp, applyStyleToRange, areaSelectAction, arrayAdapter, asNodeId, asPaint, axisAlignedBounds, bezierCubic, bezierQuadratic, blendPoses, buildChromeCtx, buildGradientRamp, buildRuleCtx, buildSceneViewCommands, canBringForward, canHover, canSendBackward, capabilityAll, capabilityIn, capabilityIs, capabilityNot, caretIndexAt, cellAt, charOffsetToDomPosition, clampView, clearSelectionAction, clientToCanvas, clipboardCopyAction, clipboardCutAction, cloneAction, coarsePointer, composeAffordanceLayer, composePath, composeRectPose, composeSelectionPose, composeWorldPose, computeFitView, computeFitViewport, computeIndicatorCommand, computeWheelAction, cond, constrainTo45, containedThenNearest, countPathAnchors, createCellHighlightLayer, createChildrenLayer, createCornerResizeAffordance, createDebugOverlayLayer, createDebugSink, createDispatcher, createGridLayer, createGuidesLayer, createHistory2 as createHistory, createMoveToIndexOp, createNodeProperties, createNodeRouting, createPaintedCursorLayer, createPaintedCursorState, createParallaxLayer, createPathAnchorAffordances, createPathEditingOverlayLayer, createPathLayer, createPenPreviewLayer, createReorderOp, createRotationAffordance, createScene, createSelectionHandlesLayer, createSelectionOutlineLayer, createSelectionOverlayLayer, createSetDataOp, createSetLayerOp, createSetPathOp, createSetTextOp, createTextLayer, createVelocityTracker, createViewResolver, createViewportLayer, cubicBezierEasing, cycleVertexColors, decomposePath, decomposeRectPose, defaultCommitAdapter, defaultDrawOne, defaultLabelTextRenderer, defaultNodeProperties, defaultNodeRouting, defaultVisibilityRules, defineTool, defineViewportTool, deriveAlignmentGuides, deriveParallaxView, describeRule, domPositionToCharOffset, domToRuns, drawLayers, drawOneLayer, easeIn, easeInBack, easeInBounce, easeInCirc, easeInCubic, easeInElastic, easeInExpo, easeInOut, easeInOutBack, easeInOutBounce, easeInOutCirc, easeInOutCubic, easeInOutElastic, easeInOutExpo, easeInOutQuad, easeInOutQuart, easeInOutQuint, easeInOutSine, easeInQuad, easeInQuart, easeInQuint, easeInSine, easeOut, easeOutBack, easeOutBounce, easeOutCirc, easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, editAnchorsAction, enterTextEditAction, enumerateAnchors, evaluate, fillInPoseFrame, fillToBoundsFrame, findNodeShape, findShapeInk, findShapeSilhouette, fitTextPose, fitToBounds, fitViewToBounds, fitZoom, focused, forEachCoalesced, frameRect, freeform, gateLayer, gesturing, getAlpha01, getImageBitmap, getMarker, getNodeShapes, getPaintKind, getStylusData, gradientForBounds, gradientGeometry, hexToRgba, hitAffordanceRegions, hitAnchor, hitRotationHandle, hostAnchorCss, hostAnchorRect, hovering, hoveringSelected, imageStatus, inferredNodeProperties, inferredNodeRouting, insertAction, interpolateView, isBuiltinToolPref, isEditableTarget2 as isEditableTarget, isGradientFill, isLayerPainted, isLayerVisible, isPatternSpec, keySpecShortcut, lassoSelectAction, lerpColorArray, lerpOklab, lerpOklch, linear, listMarkers, listPaintKinds, liveScope, makePinchZoomAction, makeViewportWheelPanAction, makeViewportZoomAction, markerDrawCommands, markerInset, markerKeyOf, markerSites, mat3, matchAlignment, matchesKeyBinding, meanScale, mergeAlphaFromPrev, mergeContributions, modeIn, modeIs, modeNot, modifierHeld, momentum, moveAction, multiActive, nearest, nearestWithin, nestedHitTester, never, nodeHasFlag, none, normalizeHex, not, nurbs, oklabToOklch, oklabToSrgbU8, oklchToOklab, openFilePicker, openPointerSession, or, paintAlpha, paintKindOf, paintWithAlpha, paintWithColor, parseColor, parseColorToRgba255, parseDebugFlags, pathContainsPoint, pathContainsPolygon, pathContainsRect, pathDistanceToPoint, pathDivide, pathExclude, pathFromD, pathInPoseFrame, pathInWorld, pathIntersect, pathIntersectsPolygon, pathIntersectsRect, pathOriginProjection, pathSubtract, pathToAnchors, pathUnion, pickTopMostHit, pinchZoomAction, planPixelRender, pointInRotatedRect, pointInTextPose, poseRotationOf, pressureToWidth, rainbowVertexColors, rebaseLocalPose, rectCorners, registerContentHandler, registerMarker, registerNodeShape, registerPaintKind, registerProgram, renderLabel, renderSceneToCanvas, renderSceneToPixels, resizeAction, resolveColor, resolveDeviceProfile, resolveEasing, resolveFillPattern, resolveMarkerSize, resolveParams, resolvePatternSpec, resolveSkeleton, resolveStrokeWidth, resolveVisibility, rgbaToHex, rotateAction, rotateAroundAABBCenter, rotatePathAround, rotatePoint, rotatedRectCorners, rotationDegreesUnit, rotationHandle, roundToCell, runsToDom, sampleGradientStops, sampleTrack, sceneFromJSON, sceneNodeClientRect, sceneSelectionStore, sceneToAdapter, scopeBindings, screenToWorld, selectFromLasso, selectionAtLeast, selectionEmpty, selectionIs, setFlagOverRange, shapeCoversPoint, sliceAction, snapPoint, solid, solidVertexColors, specificity, spiro, splitPathByLine, splitSubpaths, springPose, springVertexColors, srgbU8ToOklab, staggerVertexColors, startThresholdDrag, strokeInsets, strokeOf, strokeWith, styleAtRange, subscribeImageReady, tessellate, tessellateStroke, textCommand, textCommandFromRuns, tileGrid, toHex8, translatePoseViaDescriptor, translateRectPose, trimPolyline, tweenPose, tweenVertexColors, unionAABB, unionBounds, unionBoundsPath, useActiveToolContext, useAlign, useAnimator, useArrayAdapter, useAutoCenter, useBooleans, useBooleansAdapter, useCanvasFocus, useCanvasSize, useDecayLoop, useDeviceProfile, useDistribute, useDragGesture, useDragHandle, useDragRadial, useDragRect, useDropZone, useEllipseTool, useEyedropperTool, useGestureDispatcher, useGridCellHover, useGuides, useHandTool, useHandleDrag, useHostAnchor, useHoverTracking, useImageTool, useKeybindings, useLassoTool, useLineTool, useNodeOverlayFrame, useOptionalActiveToolContext, usePenTool, usePencilTool, usePinchGesture, usePinchZoomTool, usePointerStylus, usePolygonTool, usePublishSelection, useRectTool, useResizePolicy, useRotateTool, useScene, useSceneAdapter, useSceneTextEdit, useSelectTool, useSelection, useSelectionContext, useSimulation, useSliceDep, useStandardActions, useStarTool, useTextEdit, useTextTool, useTools, useVelocityTracker, useViewAnimation, useVisibleRaf, useZoom, viewToMat3, viewToTransform, viewportDragPanAction, viewportWheelPanAction, viewportZoomAction, viewportsAt, visualBoundsViaDescriptor, when, withAlpha01, withCoord, withGradientKind, worldEditToStorage, worldPoseLookup, worldToScreen, zoomAt, zoomAtLeast };
31271
+ //# sourceMappingURL=chunk-NU7D64RL.js.map
31272
+ //# sourceMappingURL=chunk-NU7D64RL.js.map