@weasel-js/core 1.4.0 → 1.4.2

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.2" ;
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,85 @@ function clientToCanvasRect(rect, clientX, clientY) {
5915
6204
  return [clientX - rect.left, clientY - rect.top];
5916
6205
  }
5917
6206
 
6207
+ // src/interactions/gestures/pointerSession/pointerSession.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
+ function openPointerSession(origin, down, cb, opts = {}) {
6216
+ const pointerId = down.pointerId;
6217
+ const doc = origin.ownerDocument ?? document;
6218
+ const buttonsReported = reportsButtons(down);
6219
+ let active = true;
6220
+ const teardown = () => {
6221
+ active = false;
6222
+ doc.removeEventListener("pointermove", onMove, true);
6223
+ doc.removeEventListener("pointerup", onUp, true);
6224
+ doc.removeEventListener("pointercancel", onPointerCancel, true);
6225
+ doc.removeEventListener("pointerdown", onRepress, true);
6226
+ origin.removeEventListener(LOST_CAPTURE_EVENT, onLostCapture);
6227
+ if (opts.capture !== false) {
6228
+ try {
6229
+ origin.releasePointerCapture?.(pointerId);
6230
+ } catch {
6231
+ }
6232
+ }
6233
+ };
6234
+ const mine = (e) => e.pointerId === pointerId;
6235
+ const end = (e) => {
6236
+ if (!active) return;
6237
+ teardown();
6238
+ cb.onEnd?.(e);
6239
+ };
6240
+ const abort = (reason) => {
6241
+ if (!active) return;
6242
+ teardown();
6243
+ cb.onCancel?.(reason);
6244
+ };
6245
+ function onMove(e) {
6246
+ if (!active || !mine(e)) return;
6247
+ if (buttonsReported && isMissedRelease(e)) {
6248
+ end(e);
6249
+ return;
6250
+ }
6251
+ cb.onMove?.(e);
6252
+ }
6253
+ function onUp(e) {
6254
+ if (mine(e)) end(e);
6255
+ }
6256
+ function onPointerCancel(e) {
6257
+ if (mine(e)) abort("pointercancel");
6258
+ }
6259
+ function onRepress(e) {
6260
+ if (mine(e)) abort("superseded");
6261
+ }
6262
+ function onLostCapture(e) {
6263
+ if (e.pointerId !== pointerId) return;
6264
+ if (!origin.isConnected) abort("lostcapture");
6265
+ }
6266
+ if (opts.capture !== false) {
6267
+ try {
6268
+ origin.setPointerCapture?.(pointerId);
6269
+ } catch {
6270
+ }
6271
+ }
6272
+ doc.addEventListener("pointermove", onMove, true);
6273
+ doc.addEventListener("pointerup", onUp, true);
6274
+ doc.addEventListener("pointercancel", onPointerCancel, true);
6275
+ doc.addEventListener("pointerdown", onRepress, true);
6276
+ origin.addEventListener(LOST_CAPTURE_EVENT, onLostCapture);
6277
+ return {
6278
+ pointerId,
6279
+ get active() {
6280
+ return active;
6281
+ },
6282
+ cancel: () => abort("aborted")
6283
+ };
6284
+ }
6285
+
5918
6286
  // src/interactions/gestures/handleDrag/handleDrag.ts
5919
6287
  function defaultRectEl(target) {
5920
6288
  if (target instanceof SVGElement && target.ownerSVGElement) {
@@ -5923,10 +6291,15 @@ function defaultRectEl(target) {
5923
6291
  return target;
5924
6292
  }
5925
6293
  function useHandleDrag(opts) {
5926
- const { onMove, onStart, onEnd, getRect } = opts;
6294
+ const { onMove, onStart, onEnd, onCancel, getRect } = opts;
6295
+ const live = useRef(null);
6296
+ useEffect(() => () => {
6297
+ live.current?.cancel();
6298
+ }, []);
5927
6299
  const onPointerDown = useCallback(
5928
6300
  (e) => {
5929
6301
  e.preventDefault();
6302
+ live.current?.cancel();
5930
6303
  const target = e.currentTarget;
5931
6304
  const rectEl = getRect ? getRect(target) : defaultRectEl(target);
5932
6305
  const rect = rectEl.getBoundingClientRect();
@@ -5934,26 +6307,24 @@ function useHandleDrag(opts) {
5934
6307
  const [x, y] = clientToCanvasRect(rect, clientX, clientY);
5935
6308
  return { x, y };
5936
6309
  };
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 {
6310
+ let moved = false;
6311
+ live.current = openPointerSession(target, e, {
6312
+ onMove: (ev) => {
6313
+ moved = true;
6314
+ onMove(localOf(ev.clientX, ev.clientY), ev);
6315
+ },
6316
+ onEnd: (ev) => {
6317
+ live.current = null;
6318
+ onEnd?.({ point: localOf(ev.clientX, ev.clientY), moved, event: ev });
6319
+ },
6320
+ onCancel: (reason) => {
6321
+ live.current = null;
6322
+ onCancel?.(reason);
5949
6323
  }
5950
- onEnd?.(ev);
5951
- };
5952
- target.addEventListener("pointermove", move);
5953
- target.addEventListener("pointerup", end);
5954
- target.addEventListener("pointercancel", end);
6324
+ });
6325
+ onStart?.(localOf(e.clientX, e.clientY), e);
5955
6326
  },
5956
- [onMove, onStart, onEnd, getRect]
6327
+ [onMove, onStart, onEnd, onCancel, getRect]
5957
6328
  );
5958
6329
  return { onPointerDown };
5959
6330
  }
@@ -6009,57 +6380,53 @@ function positionGhost(el, x, y) {
6009
6380
  el.style.transform = `translate(${x - el.offsetWidth / 2}px, ${y - el.offsetHeight / 2}px) scale(0.92)`;
6010
6381
  }
6011
6382
  function beginPointerDrag(payload, source, startX, startY, ghostFn) {
6012
- if (activeDrag) return;
6383
+ if (activeDrag) return null;
6013
6384
  const ghost = ghostFn(source, payload);
6014
6385
  document.body.appendChild(ghost);
6015
6386
  positionGhost(ghost, startX, startY);
6016
6387
  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() {
6388
+ const teardown = () => {
6046
6389
  activeDrag?.ghost.remove();
6047
6390
  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);
6391
+ };
6392
+ return {
6393
+ move(e) {
6394
+ if (!activeDrag) return;
6395
+ positionGhost(activeDrag.ghost, e.clientX, e.clientY);
6396
+ const zone = findZone(e.clientX, e.clientY, payload.kind);
6397
+ if (zone !== activeDrag.lastZone) {
6398
+ activeDrag.lastZone?.onOver?.(false);
6399
+ zone?.onOver?.(true);
6400
+ activeDrag.lastZone = zone;
6401
+ }
6402
+ zone?.onMove?.(payload, e.clientX, e.clientY);
6403
+ },
6404
+ drop(e) {
6405
+ if (!activeDrag) return;
6406
+ const zone = findZone(e.clientX, e.clientY, payload.kind);
6407
+ const dropped = activeDrag.payload;
6408
+ activeDrag.lastZone?.onOver?.(false);
6409
+ try {
6410
+ if (zone) zone.onDrop(dropped, e.clientX, e.clientY);
6411
+ } catch (err) {
6412
+ console.error("weasel pointerDrag: drop zone onDrop threw", err);
6413
+ } finally {
6414
+ teardown();
6415
+ }
6416
+ },
6417
+ cancel() {
6418
+ activeDrag?.lastZone?.onOver?.(false);
6419
+ teardown();
6420
+ }
6421
+ };
6055
6422
  }
6056
6423
  var DRAG_THRESHOLD_PX_SQ = 25;
6057
6424
  function useDragHandle(getPayload, options) {
6058
6425
  const optsRef = useRef(options);
6059
6426
  optsRef.current = options;
6060
- const pendingTeardownRef = useRef(null);
6427
+ const live = useRef(null);
6061
6428
  useEffect(() => () => {
6062
- pendingTeardownRef.current?.();
6429
+ live.current?.cancel();
6063
6430
  }, []);
6064
6431
  const onPointerDown = useCallback((e) => {
6065
6432
  if (e.pointerType === "mouse" && e.button !== 0) return;
@@ -6067,41 +6434,46 @@ function useDragHandle(getPayload, options) {
6067
6434
  if (e.target instanceof HTMLElement && e.target.closest("input, button, select, textarea")) return;
6068
6435
  const startX = e.clientX;
6069
6436
  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);
6437
+ let ghost = null;
6438
+ let past = false;
6439
+ live.current = openPointerSession(target, e, {
6440
+ onMove: (ev) => {
6441
+ if (ghost) {
6442
+ ghost.move(ev);
6443
+ return;
6089
6444
  }
6445
+ if (past) return;
6446
+ const dx = ev.clientX - startX;
6447
+ const dy = ev.clientY - startY;
6448
+ if (dx * dx + dy * dy <= DRAG_THRESHOLD_PX_SQ) return;
6449
+ past = true;
6450
+ const payload = getPayload();
6451
+ if (!payload) return;
6452
+ target.addEventListener(
6453
+ "click",
6454
+ (ce) => {
6455
+ ce.stopPropagation();
6456
+ ce.preventDefault();
6457
+ },
6458
+ { capture: true, once: true }
6459
+ );
6460
+ ghost = beginPointerDrag(
6461
+ payload,
6462
+ target,
6463
+ ev.clientX,
6464
+ ev.clientY,
6465
+ optsRef.current?.createGhost ?? defaultGhost
6466
+ );
6467
+ },
6468
+ onEnd: (ev) => {
6469
+ live.current = null;
6470
+ ghost?.drop(ev);
6471
+ },
6472
+ onCancel: () => {
6473
+ live.current = null;
6474
+ ghost?.cancel();
6090
6475
  }
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;
6476
+ }, { capture: false });
6105
6477
  }, [getPayload]);
6106
6478
  return { onPointerDown, style: { touchAction: "none" } };
6107
6479
  }
@@ -6128,47 +6500,34 @@ function startThresholdDrag(e, opts) {
6128
6500
  const startX = e.clientX;
6129
6501
  const startY = e.clientY;
6130
6502
  const threshold = opts.threshold ?? 4;
6131
- const target = e.currentTarget;
6132
- target.setPointerCapture(e.pointerId);
6133
6503
  let activated = false;
6134
- function maybeActivate(ev) {
6504
+ const maybeActivate = (ev) => {
6135
6505
  if (activated) return;
6136
6506
  const dx = ev.clientX - startX;
6137
6507
  const dy = ev.clientY - startY;
6138
6508
  if (dx * dx + dy * dy < threshold * threshold) return;
6139
6509
  activated = true;
6140
6510
  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) {
6511
+ };
6512
+ const session = openPointerSession(opts.origin ?? e.currentTarget, e, {
6513
+ onMove: (ev) => {
6514
+ maybeActivate(ev);
6515
+ if (activated) opts.onMove(ev);
6516
+ },
6517
+ onEnd: (ev) => {
6518
+ if (activated) opts.onCommit(ev);
6519
+ else opts.onClick?.(ev);
6520
+ },
6521
+ onCancel: () => {
6159
6522
  opts.onCancel?.();
6160
- return;
6161
6523
  }
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 };
6524
+ });
6525
+ return {
6526
+ isDragging: () => activated,
6527
+ cancel: () => {
6528
+ session.cancel();
6529
+ }
6530
+ };
6172
6531
  }
6173
6532
 
6174
6533
  // src/core/device/targets.ts
@@ -8217,6 +8576,30 @@ var cloneAction = {
8217
8576
  enabled: () => true
8218
8577
  };
8219
8578
 
8579
+ // src/core/viewport/createVelocityTracker.ts
8580
+ function createVelocityTracker() {
8581
+ let samples = [];
8582
+ return {
8583
+ record(dx, dy, t) {
8584
+ samples.push({ dx, dy, t });
8585
+ const cutoff = t - 100;
8586
+ samples = samples.filter((s) => s.t >= cutoff);
8587
+ },
8588
+ getVelocity() {
8589
+ const s = samples;
8590
+ if (s.length < 2) return { vx: 0, vy: 0 };
8591
+ const dt = s[s.length - 1].t - s[0].t;
8592
+ if (dt === 0) return { vx: 0, vy: 0 };
8593
+ const totalDx = s.slice(1).reduce((acc, p) => acc + p.dx, 0);
8594
+ const totalDy = s.slice(1).reduce((acc, p) => acc + p.dy, 0);
8595
+ return { vx: totalDx / dt, vy: totalDy / dt };
8596
+ },
8597
+ reset() {
8598
+ samples = [];
8599
+ }
8600
+ };
8601
+ }
8602
+
8220
8603
  // src/interactions/actions/defaults/viewportDragPan.ts
8221
8604
  var viewportDragPanAction = {
8222
8605
  id: "viewport.dragPan",
@@ -8235,17 +8618,37 @@ var viewportDragPanAction = {
8235
8618
  requires: ["view"],
8236
8619
  invoker: {
8237
8620
  timing: "ongoing",
8238
- start(ctx) {
8621
+ start(ctx, opts) {
8239
8622
  const view = ctx.deps.view;
8240
8623
  if (!view) return {};
8241
8624
  const startView = view.get();
8242
- const scratch = { startView, view };
8625
+ const params = resolveParams(opts?.params) ?? {};
8626
+ const inertia = params.inertia === false ? void 0 : params.inertia;
8627
+ const scratch = {
8628
+ startView,
8629
+ view,
8630
+ axis: params.axis ?? "both",
8631
+ inertia,
8632
+ // Only track when a coast could actually happen, so the common
8633
+ // no-inertia drag does no per-move work.
8634
+ tracker: inertia && view.decay ? createVelocityTracker() : void 0,
8635
+ lastScreen: { x: 0, y: 0 }
8636
+ };
8243
8637
  return {
8244
8638
  kind: "pan",
8245
8639
  onMove(moveCtx) {
8246
8640
  if (!moveCtx.drag) return;
8247
8641
  const screen = moveCtx.drag.screenDelta ?? moveCtx.drag.delta;
8248
- const { x: screenDx, y: screenDy } = screen;
8642
+ const screenDx = scratch.axis === "y" ? 0 : screen.x;
8643
+ const screenDy = scratch.axis === "x" ? 0 : screen.y;
8644
+ if (scratch.tracker) {
8645
+ scratch.tracker.record(
8646
+ screenDx - scratch.lastScreen.x,
8647
+ screenDy - scratch.lastScreen.y,
8648
+ performance.now()
8649
+ );
8650
+ scratch.lastScreen = { x: screenDx, y: screenDy };
8651
+ }
8249
8652
  const sv = scratch.startView;
8250
8653
  scratch.view.set({
8251
8654
  ...sv,
@@ -8253,7 +8656,24 @@ var viewportDragPanAction = {
8253
8656
  y: sv.y - screenDy / sv.scale.y
8254
8657
  });
8255
8658
  },
8256
- onEnd() {
8659
+ onEnd(_endCtx, reason) {
8660
+ const { tracker, inertia: inertia2, view: v } = scratch;
8661
+ if (reason === "cancel" || !tracker || !inertia2 || !v.decay) return;
8662
+ const { vx, vy } = tracker.getVelocity();
8663
+ const scale2 = scratch.startView.scale;
8664
+ v.decay({
8665
+ // Screen px/ms → world units/ms, matching the onMove conversion.
8666
+ velocity: { vx: -vx / scale2.x, vy: -vy / scale2.y },
8667
+ friction: inertia2.friction,
8668
+ minSpeed: inertia2.minSpeed,
8669
+ boundary: inertia2.boundary,
8670
+ viewBounds: inertia2.bounds,
8671
+ initialPosition: { x: v.get().x, y: v.get().y },
8672
+ onTick: (dx, dy) => {
8673
+ const cur = v.get();
8674
+ v.set({ ...cur, x: cur.x + dx, y: cur.y + dy });
8675
+ }
8676
+ });
8257
8677
  }
8258
8678
  };
8259
8679
  }
@@ -8357,6 +8777,54 @@ function makeViewportZoomAction(opts = {}) {
8357
8777
  }
8358
8778
  var viewportZoomAction = makeViewportZoomAction();
8359
8779
 
8780
+ // src/interactions/actions/defaults/viewportWheelPan.ts
8781
+ var viewportWheelPanAction = {
8782
+ id: "viewport.wheelPan",
8783
+ label: "Pan (wheel)",
8784
+ group: "viewport",
8785
+ /**
8786
+ * Plain wheel → pan; Shift+wheel → horizontal pan (axis swap).
8787
+ * Cmd+wheel is claimed by `viewportZoomAction`. The matcher's strict
8788
+ * modifier check keeps the bindings exclusive: omitting `mods` forbids
8789
+ * any modifier, and `mods: { shift: true }` forbids the others.
8790
+ */
8791
+ defaultBinding: [
8792
+ { spec: { kind: "wheel" }, opts: {} },
8793
+ { spec: { kind: "wheel", mods: { shift: true } }, opts: { params: { swapAxis: true } } }
8794
+ ],
8795
+ requires: ["view"],
8796
+ invoker: {
8797
+ timing: "immediate",
8798
+ run(deps, params) {
8799
+ const view = deps.view;
8800
+ if (!view) return;
8801
+ const axis = params?.axis ?? "both";
8802
+ const deltaX = params?.deltaX ?? 0;
8803
+ const deltaY = params?.deltaY ?? 0;
8804
+ const current = view.get();
8805
+ const swap = params?.swapAxis === true;
8806
+ const dx = swap ? (deltaX !== 0 ? deltaX : deltaY) / current.scale.x : deltaX / current.scale.x;
8807
+ const dy = swap ? 0 : deltaY / current.scale.y;
8808
+ view.set({
8809
+ ...current,
8810
+ x: current.x + (axis === "y" ? 0 : dx),
8811
+ y: current.y + (axis === "x" ? 0 : dy)
8812
+ });
8813
+ }
8814
+ },
8815
+ enabled: () => true
8816
+ };
8817
+ function makeViewportWheelPanAction(opts = {}) {
8818
+ const axis = opts.axis ?? "both";
8819
+ return {
8820
+ ...viewportWheelPanAction,
8821
+ defaultBinding: [
8822
+ { spec: { kind: "wheel" }, opts: { params: { axis } } },
8823
+ { spec: { kind: "wheel", mods: { shift: true } }, opts: { params: { swapAxis: true, axis } } }
8824
+ ]
8825
+ };
8826
+ }
8827
+
8360
8828
  // src/interactions/dispatcher/predicates.ts
8361
8829
  function bodyPredicate(fn) {
8362
8830
  return Object.assign(fn, { readsAffordance: false });
@@ -13003,7 +13471,7 @@ function withViewDeps(base, overlay) {
13003
13471
  };
13004
13472
  }
13005
13473
  function computeMultiTouchGeometry(positions) {
13006
- const pts = [...positions.values()];
13474
+ const pts = [...positions];
13007
13475
  if (pts.length < 2) {
13008
13476
  return { centroid: { x: 0, y: 0 }, spread: 0 };
13009
13477
  }
@@ -13130,13 +13598,13 @@ function useGestureDispatcher(opts) {
13130
13598
  return result;
13131
13599
  };
13132
13600
  const heldKeys = /* @__PURE__ */ new Set();
13133
- const activePointers = /* @__PURE__ */ new Set();
13134
- const pointerPositions = /* @__PURE__ */ new Map();
13601
+ const held = /* @__PURE__ */ new Map();
13602
+ const heldGeometry = () => computeMultiTouchGeometry(
13603
+ [...held.values()].map((p) => p.pos)
13604
+ );
13135
13605
  let multiTouchStart = null;
13136
13606
  const TAP_THRESHOLD_PX = 8;
13137
13607
  const DRAG_THRESHOLD_PX = 4;
13138
- const bufferedDown = /* @__PURE__ */ new Map();
13139
- const lastPointerDown = /* @__PURE__ */ new Map();
13140
13608
  const LONG_PRESS_MS = 500;
13141
13609
  const longPressTimers = /* @__PURE__ */ new Map();
13142
13610
  const cancelLongPress = (pointerId) => {
@@ -13151,7 +13619,7 @@ function useGestureDispatcher(opts) {
13151
13619
  longPressTimers.clear();
13152
13620
  };
13153
13621
  const fireLongPress = (pointerId) => {
13154
- const down = lastPointerDown.get(pointerId);
13622
+ const down = held.get(pointerId)?.down;
13155
13623
  if (!down) return;
13156
13624
  const shared = {
13157
13625
  target: down.target,
@@ -13273,13 +13741,8 @@ function useGestureDispatcher(opts) {
13273
13741
  };
13274
13742
  const onPointerDown = (e) => {
13275
13743
  if ((e.button ?? 0) !== 0) return;
13744
+ if (!canvas) return;
13276
13745
  routeDown(e.pointerId, e.clientX, e.clientY);
13277
- activePointers.add(e.pointerId);
13278
- pointerPositions.set(e.pointerId, { x: e.clientX, y: e.clientY });
13279
- try {
13280
- canvas?.setPointerCapture?.(e.pointerId);
13281
- } catch {
13282
- }
13283
13746
  const worldPoint = { x: e.clientX, y: e.clientY };
13284
13747
  const affordance = target().affordanceAt?.(worldPoint) ?? void 0;
13285
13748
  const body = target().classifyTarget?.(worldPoint);
@@ -13303,23 +13766,35 @@ function useGestureDispatcher(opts) {
13303
13766
  ...bodyTarget !== void 0 ? { bodyTarget } : {},
13304
13767
  ...bodyKind !== void 0 ? { bodyKind } : {}
13305
13768
  };
13306
- bufferedDown.set(e.pointerId, { ev, clientX: e.clientX, clientY: e.clientY });
13307
- dispatch2({ ...ev, stage: "press" });
13308
- lastPointerDown.set(e.pointerId, {
13309
- target: e.target,
13310
- clientX: e.clientX,
13311
- clientY: e.clientY,
13312
- worldX: w.x,
13313
- worldY: w.y,
13314
- affordance,
13315
- bodyTarget,
13316
- bodyKind,
13317
- altKey: e.altKey,
13318
- ctrlKey: e.ctrlKey,
13319
- metaKey: e.metaKey,
13320
- shiftKey: e.shiftKey
13769
+ held.set(e.pointerId, {
13770
+ session: openPointerSession(canvas, e, {
13771
+ onMove: onPointerMove,
13772
+ onEnd: onPointerUp,
13773
+ onCancel: () => {
13774
+ onPointerCancel(e.pointerId);
13775
+ }
13776
+ }),
13777
+ pos: { x: e.clientX, y: e.clientY },
13778
+ // Deferred until the first past-threshold pointermove: a bare
13779
+ // press-and-release should fire `click`, not start a drag action.
13780
+ buffered: { ev, clientX: e.clientX, clientY: e.clientY },
13781
+ down: {
13782
+ target: e.target,
13783
+ clientX: e.clientX,
13784
+ clientY: e.clientY,
13785
+ worldX: w.x,
13786
+ worldY: w.y,
13787
+ affordance,
13788
+ bodyTarget,
13789
+ bodyKind,
13790
+ altKey: e.altKey,
13791
+ ctrlKey: e.ctrlKey,
13792
+ metaKey: e.metaKey,
13793
+ shiftKey: e.shiftKey
13794
+ }
13321
13795
  });
13322
- if ((e.pointerType === "touch" || e.pointerType === "pen") && activePointers.size === 1) {
13796
+ dispatch2({ ...ev, stage: "press" });
13797
+ if ((e.pointerType === "touch" || e.pointerType === "pen") && held.size === 1) {
13323
13798
  cancelLongPress(e.pointerId);
13324
13799
  longPressTimers.set(
13325
13800
  e.pointerId,
@@ -13330,18 +13805,18 @@ function useGestureDispatcher(opts) {
13330
13805
  }, LONG_PRESS_MS)
13331
13806
  );
13332
13807
  }
13333
- if (activePointers.size >= 2) {
13808
+ if (held.size >= 2) {
13334
13809
  cancelAllLongPress();
13335
- for (const id of activePointers) bufferedDown.delete(id);
13336
- const { centroid, spread } = computeMultiTouchGeometry(pointerPositions);
13810
+ for (const p of held.values()) p.buffered = null;
13811
+ const { centroid, spread } = heldGeometry();
13337
13812
  if (!multiTouchStart) {
13338
- multiTouchStart = { fingers: activePointers.size, centroid: { ...centroid } };
13813
+ multiTouchStart = { fingers: held.size, centroid: { ...centroid } };
13339
13814
  } else {
13340
- multiTouchStart.fingers = activePointers.size;
13815
+ multiTouchStart.fingers = held.size;
13341
13816
  }
13342
13817
  const mt = {
13343
13818
  kind: "multitouch",
13344
- fingers: activePointers.size,
13819
+ fingers: held.size,
13345
13820
  altKey: e.altKey,
13346
13821
  ctrlKey: e.ctrlKey,
13347
13822
  metaKey: e.metaKey,
@@ -13354,25 +13829,24 @@ function useGestureDispatcher(opts) {
13354
13829
  };
13355
13830
  const onPointerMove = (e) => {
13356
13831
  routeAt(e.pointerId, e.clientX, e.clientY);
13357
- if (activePointers.has(e.pointerId)) {
13358
- pointerPositions.set(e.pointerId, { x: e.clientX, y: e.clientY });
13359
- }
13360
- const buffered = bufferedDown.get(e.pointerId);
13361
- if (buffered) {
13832
+ const rec = held.get(e.pointerId);
13833
+ if (rec) rec.pos = { x: e.clientX, y: e.clientY };
13834
+ const buffered = rec?.buffered;
13835
+ if (rec && buffered) {
13362
13836
  const dx = e.clientX - buffered.clientX;
13363
13837
  const dy = e.clientY - buffered.clientY;
13364
13838
  if (Math.hypot(dx, dy) >= DRAG_THRESHOLD_PX) {
13365
- bufferedDown.delete(e.pointerId);
13839
+ rec.buffered = null;
13366
13840
  cancelLongPress(e.pointerId);
13367
13841
  dispatch2(buffered.ev);
13368
13842
  }
13369
13843
  }
13370
- const mtGestureId = `multitouch-${activePointers.size}`;
13371
- if (activePointers.size >= 2 && dispatcherNow().inFlight().has(mtGestureId)) {
13372
- const { centroid, spread } = computeMultiTouchGeometry(pointerPositions);
13844
+ const mtGestureId = `multitouch-${held.size}`;
13845
+ if (held.size >= 2 && dispatcherNow().inFlight().has(mtGestureId)) {
13846
+ const { centroid, spread } = heldGeometry();
13373
13847
  const mtEv = {
13374
13848
  kind: "multitouch",
13375
- fingers: activePointers.size,
13849
+ fingers: held.size,
13376
13850
  altKey: e.altKey,
13377
13851
  ctrlKey: e.ctrlKey,
13378
13852
  metaKey: e.metaKey,
@@ -13444,7 +13918,7 @@ function useGestureDispatcher(opts) {
13444
13918
  }
13445
13919
  function resolveHoverCursor() {
13446
13920
  if (!canvas || !lastHover) return;
13447
- if (activePointers.size > 0 || dispatcherNow().inFlight().size > 0) {
13921
+ if (held.size > 0 || dispatcherNow().inFlight().size > 0) {
13448
13922
  applyHoverCursor(dispatcherNow().inFlightCursor());
13449
13923
  return;
13450
13924
  }
@@ -13492,6 +13966,10 @@ function useGestureDispatcher(opts) {
13492
13966
  if (!disposed) refreshHoverCursor();
13493
13967
  }, 0);
13494
13968
  };
13969
+ const onHoverMove = (e) => {
13970
+ if (held.has(e.pointerId)) return;
13971
+ onPointerMove(e);
13972
+ };
13495
13973
  const onHoverPointerLeave = () => {
13496
13974
  lastHover = null;
13497
13975
  paintedCursorRef.current?.()?.clearPointer();
@@ -13500,20 +13978,21 @@ function useGestureDispatcher(opts) {
13500
13978
  const onPointerUp = (e) => {
13501
13979
  routeAt(e.pointerId, e.clientX, e.clientY);
13502
13980
  cancelLongPress(e.pointerId);
13503
- const prevSize = activePointers.size;
13504
- activePointers.delete(e.pointerId);
13505
- pointerPositions.delete(e.pointerId);
13506
- const staysUnderThreshold = bufferedDown.delete(e.pointerId);
13507
- if (prevSize >= 2 && activePointers.size < 2) {
13981
+ const prevSize = held.size;
13982
+ const rec = held.get(e.pointerId);
13983
+ held.delete(e.pointerId);
13984
+ const staysUnderThreshold = rec?.buffered != null;
13985
+ if (prevSize >= 2 && held.size < 2) {
13508
13986
  const mtGestureId = `multitouch-${prevSize}`;
13509
13987
  if (dispatcherNow().inFlight().has(mtGestureId)) {
13510
13988
  dispatcherNow().cancelAll("commit");
13511
13989
  }
13512
13990
  const start = multiTouchStart;
13513
13991
  if (start) {
13514
- const finalPositions = new Map(pointerPositions);
13515
- finalPositions.set(e.pointerId, { x: e.clientX, y: e.clientY });
13516
- const { centroid } = computeMultiTouchGeometry(finalPositions);
13992
+ const { centroid } = computeMultiTouchGeometry([
13993
+ ...[...held.values()].map((p) => p.pos),
13994
+ { x: e.clientX, y: e.clientY }
13995
+ ]);
13517
13996
  const dx = centroid.x - start.centroid.x;
13518
13997
  const dy = centroid.y - start.centroid.y;
13519
13998
  if (Math.hypot(dx, dy) <= TAP_THRESHOLD_PX) {
@@ -13530,8 +14009,7 @@ function useGestureDispatcher(opts) {
13530
14009
  multiTouchStart = null;
13531
14010
  }
13532
14011
  }
13533
- const downForClick = lastPointerDown.get(e.pointerId);
13534
- const movedDuringDrag = downForClick ? downForClick.clientX !== e.clientX || downForClick.clientY !== e.clientY : true;
14012
+ const movedDuringDrag = rec ? rec.down.clientX !== e.clientX || rec.down.clientY !== e.clientY : true;
13535
14013
  const hadDragInFlight = dispatcherNow().inFlight().has(pointerGestureId(e.pointerId)) && movedDuringDrag;
13536
14014
  const wUp = toWorld2(e.clientX, e.clientY);
13537
14015
  const ev = {
@@ -13547,8 +14025,7 @@ function useGestureDispatcher(opts) {
13547
14025
  shiftKey: e.shiftKey
13548
14026
  };
13549
14027
  dispatch2(ev);
13550
- const down = lastPointerDown.get(e.pointerId);
13551
- lastPointerDown.delete(e.pointerId);
14028
+ const down = rec?.down;
13552
14029
  if (!hadDragInFlight && staysUnderThreshold && down) {
13553
14030
  const wClick = toWorld2(e.clientX, e.clientY);
13554
14031
  const clickEv = {
@@ -13605,24 +14082,24 @@ function useGestureDispatcher(opts) {
13605
14082
  if (!disposed) refreshHoverCursor();
13606
14083
  }, 0);
13607
14084
  };
13608
- const onPointerCancel = (e) => {
13609
- routeAt(e.pointerId, e.clientX, e.clientY);
13610
- cancelLongPress(e.pointerId);
13611
- activePointers.delete(e.pointerId);
13612
- pointerPositions.delete(e.pointerId);
13613
- lastPointerDown.delete(e.pointerId);
13614
- bufferedDown.delete(e.pointerId);
14085
+ const onPointerCancel = (pointerId) => {
14086
+ if (disposed) return;
14087
+ const rec = held.get(pointerId);
14088
+ if (!rec) return;
14089
+ held.delete(pointerId);
14090
+ routeAt(pointerId, rec.pos.x, rec.pos.y);
14091
+ cancelLongPress(pointerId);
13615
14092
  multiTouchStart = null;
13616
14093
  const ev = {
13617
14094
  kind: "pointercancel",
13618
- pointerId: e.pointerId,
13619
- altKey: e.altKey,
13620
- ctrlKey: e.ctrlKey,
13621
- metaKey: e.metaKey,
13622
- shiftKey: e.shiftKey
14095
+ pointerId,
14096
+ altKey: rec.down.altKey,
14097
+ ctrlKey: rec.down.ctrlKey,
14098
+ metaKey: rec.down.metaKey,
14099
+ shiftKey: rec.down.shiftKey
13623
14100
  };
13624
14101
  dispatch2(ev);
13625
- routeRelease(e.pointerId);
14102
+ routeRelease(pointerId);
13626
14103
  };
13627
14104
  const onContextMenu = (e) => {
13628
14105
  e.preventDefault();
@@ -13710,9 +14187,7 @@ function useGestureDispatcher(opts) {
13710
14187
  canvas?.addEventListener("pointerleave", onHoverPointerLeave);
13711
14188
  canvas?.addEventListener("wheel", onWheel, { passive: false });
13712
14189
  canvas?.addEventListener("pointerdown", onPointerDown);
13713
- canvas?.addEventListener("pointermove", onPointerMove);
13714
- canvas?.addEventListener("pointerup", onPointerUp);
13715
- canvas?.addEventListener("pointercancel", onPointerCancel);
14190
+ canvas?.addEventListener("pointermove", onHoverMove);
13716
14191
  canvas?.addEventListener("contextmenu", onContextMenu);
13717
14192
  canvas?.addEventListener("dragenter", onDragOver);
13718
14193
  canvas?.addEventListener("dragover", onDragOver);
@@ -13730,9 +14205,7 @@ function useGestureDispatcher(opts) {
13730
14205
  canvas?.removeEventListener("pointerleave", onHoverPointerLeave);
13731
14206
  canvas?.removeEventListener("wheel", onWheel);
13732
14207
  canvas?.removeEventListener("pointerdown", onPointerDown);
13733
- canvas?.removeEventListener("pointermove", onPointerMove);
13734
- canvas?.removeEventListener("pointerup", onPointerUp);
13735
- canvas?.removeEventListener("pointercancel", onPointerCancel);
14208
+ canvas?.removeEventListener("pointermove", onHoverMove);
13736
14209
  canvas?.removeEventListener("contextmenu", onContextMenu);
13737
14210
  canvas?.removeEventListener("dragenter", onDragOver);
13738
14211
  canvas?.removeEventListener("dragover", onDragOver);
@@ -13743,6 +14216,8 @@ function useGestureDispatcher(opts) {
13743
14216
  clearHoverCursor();
13744
14217
  cancelAllLongPress();
13745
14218
  disposed = true;
14219
+ for (const p of held.values()) p.session.cancel();
14220
+ held.clear();
13746
14221
  for (const d of allDispatchers()) d.cancelAll("cancel");
13747
14222
  };
13748
14223
  }, [enabled, keyboard, canvasRef]);
@@ -13775,26 +14250,7 @@ function computeWheelAction(state, input, bounds = { min: DEFAULT_MIN_ZOOM, max:
13775
14250
  };
13776
14251
  }
13777
14252
  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
- }), []);
14253
+ return useMemo(() => createVelocityTracker(), []);
13798
14254
  }
13799
14255
  var SPRING_DAMPING = 0.5;
13800
14256
  function useDecayLoop() {
@@ -17762,7 +18218,9 @@ function useRotateTool(adapter, options) {
17762
18218
  [overlay]
17763
18219
  );
17764
18220
  }
17765
- function useHandTool(_opts = {}) {
18221
+ function useHandTool(opts = {}) {
18222
+ const optsRef = useRef(opts);
18223
+ optsRef.current = opts;
17766
18224
  return useMemo(
17767
18225
  () => Object.assign(defineViewportTool({
17768
18226
  id: "hand",
@@ -17780,7 +18238,16 @@ function useHandTool(_opts = {}) {
17780
18238
  // viewportDragPanAction, which also owns the grabbing cursor
17781
18239
  // (`Action.activeCursor`) the tool used to declare as an `engaged`
17782
18240
  // phase entry.
17783
- bindings: [{ spec: { kind: "drag" }, actionId: "viewport.dragPan" }]
18241
+ bindings: [{
18242
+ spec: { kind: "drag" },
18243
+ actionId: "viewport.dragPan",
18244
+ opts: {
18245
+ params: () => ({
18246
+ axis: optsRef.current.axis ?? "both",
18247
+ inertia: optsRef.current.inertia
18248
+ })
18249
+ }
18250
+ }]
17784
18251
  }),
17785
18252
  []
17786
18253
  );
@@ -19151,24 +19618,25 @@ function projectBounds(b, view) {
19151
19618
  const [sx, sy] = worldToScreen(b.x, b.y, t);
19152
19619
  return { ...b, x: sx, y: sy, width: b.width * view.scale.x, height: b.height * view.scale.y };
19153
19620
  }
19621
+ function leavesOf(id, getChildren, isContainer) {
19622
+ if (!isContainer(id)) return [id];
19623
+ const out = [];
19624
+ const visit = (nid) => {
19625
+ const kids = getChildren(nid);
19626
+ if (kids.length === 0) {
19627
+ if (!isContainer(nid)) out.push(nid);
19628
+ return;
19629
+ }
19630
+ for (const k of kids) visit(k);
19631
+ };
19632
+ visit(id);
19633
+ return out;
19634
+ }
19154
19635
  function composeSelectionPose(opts) {
19155
19636
  const { moveOverlay, resizeOverlay, getStoredPose, getChildren, isContainer } = opts;
19156
19637
  const getBounds = opts.getBounds ?? ((pose) => pose);
19157
19638
  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
- };
19639
+ const selectionLeaves = (id) => getChildren && isContainer ? leavesOf(id, getChildren, isContainer) : [id];
19172
19640
  const resolveLeaf = (id) => {
19173
19641
  const moved = moveOverlay?.poses.get(id);
19174
19642
  if (moved !== void 0) return moved;
@@ -19177,7 +19645,7 @@ function composeSelectionPose(opts) {
19177
19645
  };
19178
19646
  return (id) => {
19179
19647
  if (isContainer?.(id)) {
19180
- const leaves = leavesOf(id);
19648
+ const leaves = selectionLeaves(id);
19181
19649
  if (leaves.length === 0) return null;
19182
19650
  const containerResizeLeafPoses = resizeOverlay && resizeOverlay.id === id ? resizeOverlay.leafPoses : void 0;
19183
19651
  const leafBounds = [];
@@ -19208,26 +19676,12 @@ function makeContainerAwareBoundsResolver(getPose, getBounds, getChildren, isCon
19208
19676
  return p === null ? null : getBounds(p);
19209
19677
  };
19210
19678
  }
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
19679
  return (id) => {
19226
19680
  if (!isContainer(id)) {
19227
19681
  const p = getPose(id);
19228
19682
  return p === null ? null : getBounds(p);
19229
19683
  }
19230
- const leaves = leavesOf(id);
19684
+ const leaves = leavesOf(id, getChildren, isContainer);
19231
19685
  if (leaves.length === 0) return null;
19232
19686
  const leafBounds = [];
19233
19687
  for (const leafId of leaves) {
@@ -20394,7 +20848,7 @@ function CanvasInner(props, ref) {
20394
20848
  return {
20395
20849
  worldX,
20396
20850
  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 },
20851
+ modifiers: m ? { alt: m.alt, shift: m.shift, meta: m.meta, ctrl: m.ctrl } : { alt: false, shift: false, meta: false, ctrl: false },
20398
20852
  selection: effectiveSelectionRefForCtx.current,
20399
20853
  adapter: effectiveAdapterRefForCtx.current,
20400
20854
  applyOps: (ops, label) => {
@@ -24097,10 +24551,10 @@ function boundsOf(coords) {
24097
24551
  }
24098
24552
  return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
24099
24553
  }
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" : ""}`;
24554
+ function useViewDepSource(currentViewRef, onViewChange, recenter, hostSize, animation, decay) {
24555
+ const wiring = useRef({ currentViewRef, onViewChange, recenter, hostSize, animation, decay });
24556
+ wiring.current = { currentViewRef, onViewChange, recenter, hostSize, animation, decay };
24557
+ const shape = `${recenter ? "r" : ""}${hostSize ? "h" : ""}${animation ? "a" : ""}${decay ? "d" : ""}`;
24104
24558
  const shapeRef = useRef(null);
24105
24559
  const viewApiRef = useRef(null);
24106
24560
  if (viewApiRef.current === null || shapeRef.current !== shape) {
@@ -24119,6 +24573,10 @@ function useViewDepSource(currentViewRef, onViewChange, recenter, hostSize, anim
24119
24573
  animate: (to, opts) => wiring.current.animation.animate(to, opts),
24120
24574
  stopAnimation: () => wiring.current.animation.stop(),
24121
24575
  animationTarget: () => wiring.current.animation.target()
24576
+ } : {},
24577
+ ...decay ? {
24578
+ decay: (config) => wiring.current.decay.start(config),
24579
+ stopDecay: () => wiring.current.decay.cancel()
24122
24580
  } : {}
24123
24581
  };
24124
24582
  }
@@ -24882,7 +25340,6 @@ function useActionsPropResolver(actions) {
24882
25340
  }
24883
25341
  if (!prop) return;
24884
25342
  const unregisters = [];
24885
- const restores = [];
24886
25343
  const warnedIds = /* @__PURE__ */ new Set();
24887
25344
  for (const [slotId, entry] of Object.entries(prop)) {
24888
25345
  if (entry === null) {
@@ -24910,64 +25367,30 @@ function useActionsPropResolver(actions) {
24910
25367
  }
24911
25368
  const { id: _drop, ...rest } = entry;
24912
25369
  const merged = { ...existing, ...rest };
24913
- restores.push(() => {
24914
- reg.register(existing);
24915
- });
24916
25370
  unregisters.push(reg.register(merged));
24917
25371
  }
24918
25372
  return () => {
24919
25373
  for (const u of unregisters) u();
24920
- for (const r of restores) r();
24921
25374
  };
24922
25375
  }, [reg, actions]);
24923
25376
  }
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
25377
  function useViewportActions(args) {
24960
25378
  const { pan, zoom, pinchZoom } = args;
24961
25379
  const reg = useActionsRegistry();
24962
25380
  const regRef = useRef(reg);
24963
25381
  regRef.current = reg;
25382
+ const panKey = typeof pan === "object" ? JSON.stringify(pan) : String(pan);
24964
25383
  const zoomKey = typeof zoom === "object" ? JSON.stringify(zoom) : String(zoom);
24965
25384
  const pinchKey = typeof pinchZoom === "object" ? JSON.stringify(pinchZoom) : String(pinchZoom);
24966
25385
  useEffect(() => {
24967
25386
  const r = regRef.current;
24968
25387
  if (!r) return;
24969
25388
  const unregisters = [];
24970
- if (pan) unregisters.push(r.register(viewportWheelPanAction));
25389
+ if (pan) {
25390
+ unregisters.push(
25391
+ r.register(makeViewportWheelPanAction(typeof pan === "object" ? pan : {}))
25392
+ );
25393
+ }
24971
25394
  if (zoom) {
24972
25395
  const zoomAction = typeof zoom === "object" ? makeViewportZoomAction(zoom) : makeViewportZoomAction();
24973
25396
  unregisters.push(r.register(zoomAction));
@@ -24982,7 +25405,7 @@ function useViewportActions(args) {
24982
25405
  return () => {
24983
25406
  for (const u of unregisters) u();
24984
25407
  };
24985
- }, [pan, zoomKey, pinchKey]);
25408
+ }, [panKey, zoomKey, pinchKey]);
24986
25409
  }
24987
25410
 
24988
25411
  // src/core/scene/NodeRouting.ts
@@ -25206,7 +25629,6 @@ function SceneCanvasInner(props, ref) {
25206
25629
  () => isSerialized(sceneInput) ? sceneFromJSON(sceneInput, {}) : null
25207
25630
  );
25208
25631
  const scene = bakedScene ?? sceneInput;
25209
- useSyncExternalStore(scene.subscribe, scene.getVersion, scene.getVersion);
25210
25632
  const { view: viewProp, onViewChange: onViewChangeProp, defaultView, ...restProps } = rest;
25211
25633
  const internalCanvasRef = useRef(null);
25212
25634
  const canvasApiRef = useRef(null);
@@ -25237,6 +25659,9 @@ function SceneCanvasInner(props, ref) {
25237
25659
  useEffect(() => scene.overrides.subscribe(() => {
25238
25660
  canvasApiRef.current?.requestRedraw?.();
25239
25661
  }), [scene]);
25662
+ useEffect(() => scene.subscribe(() => {
25663
+ canvasApiRef.current?.requestRedraw?.();
25664
+ }), [scene]);
25240
25665
  const currentViewRef = useRef(
25241
25666
  viewProp ?? defaultView ?? { x: 0, y: 0, scale: { x: 1, y: 1 } }
25242
25667
  );
@@ -25417,13 +25842,13 @@ function SceneCanvasInner(props, ref) {
25417
25842
  const viewportRegistered = !!viewport;
25418
25843
  const inertiaEnabled = !!viewport?.inertia;
25419
25844
  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
25845
+ const handToolInertia = inertiaEnabled ? {
25846
+ friction: inertiaObj?.friction,
25847
+ minSpeed: inertiaObj?.minSpeed,
25848
+ boundary: inertiaObj?.boundary,
25849
+ bounds: inertiaObj?.bounds
25425
25850
  } : void 0;
25426
- const handTool = useHandTool(handToolInertia ? { } : {});
25851
+ const handTool = useHandTool(handToolInertia ? { inertia: handToolInertia } : {});
25427
25852
  const viewportAmbient = [];
25428
25853
  const baseRequestedTools = defaultTools ?? (toolBundle ? BUNDLE_TOOLS[toolBundle] : null) ?? (viewportRegistered ? ["select", "rotate", "hand"] : ["select", "rotate"]);
25429
25854
  const KNOWN_BUILTIN_IDS = /* @__PURE__ */ new Set([
@@ -25876,7 +26301,7 @@ function SceneCanvasInner(props, ref) {
25876
26301
  dispatcher,
25877
26302
  getActionRef,
25878
26303
  pickEvery: internalPickEvery,
25879
- viewportPanEnabled: viewport?.pan !== false,
26304
+ viewportPanEnabled: viewport?.pan ?? true,
25880
26305
  viewportZoom: resolvedViewportZoom,
25881
26306
  viewportPinchZoom: viewport?.pinchZoom ?? true,
25882
26307
  viewportRecenter: viewport?.recenter,
@@ -26167,6 +26592,7 @@ function StandardActionsRegistrar({
26167
26592
  getActionRef.current = null;
26168
26593
  };
26169
26594
  }, [registry2, getActionRef]);
26595
+ const decayLoop = useDecayLoop();
26170
26596
  const view = useViewDepSource(
26171
26597
  currentViewRef,
26172
26598
  onViewChange,
@@ -26175,7 +26601,8 @@ function StandardActionsRegistrar({
26175
26601
  const el = canvasRef.current;
26176
26602
  return el ? { width: el.clientWidth, height: el.clientHeight } : null;
26177
26603
  },
26178
- viewAnimation
26604
+ viewAnimation,
26605
+ decayLoop
26179
26606
  );
26180
26607
  const sceneAsHistory = scene;
26181
26608
  useStandardActions({ selection, scene, view, history: sceneAsHistory });
@@ -26635,8 +27062,7 @@ function MinimapCanvasInner(props) {
26635
27062
  }, [mainView, mainViewDims, indicatorStyle]);
26636
27063
  const extraCommands = useMemo(() => [indicatorCmd], [indicatorCmd]);
26637
27064
  const localCanvasRef = useRef(null);
26638
- const dragActiveRef = useRef(false);
26639
- const capturedPointerIdRef = useRef(null);
27065
+ const sessionRef = useRef(null);
26640
27066
  const mainViewRef = useRef(mainView);
26641
27067
  const mainViewDimsRef = useRef(mainViewDims);
26642
27068
  const onMainViewChangeRef = useRef(onMainViewChange);
@@ -26665,36 +27091,18 @@ function MinimapCanvasInner(props) {
26665
27091
  }, []);
26666
27092
  const handlePointerDown = useCallback((ev) => {
26667
27093
  const canvas = localCanvasRef.current;
26668
- if (!canvas) return;
27094
+ if (!canvas || sessionRef.current) return;
26669
27095
  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
- }
27096
+ const done = () => {
27097
+ sessionRef.current = null;
27098
+ };
27099
+ sessionRef.current = openPointerSession(canvas, ev, {
27100
+ onMove: recenterFromPointer,
27101
+ onEnd: done,
27102
+ onCancel: done
27103
+ });
26683
27104
  recenterFromPointer(ev);
26684
27105
  }, [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
27106
  const setCanvasRef = useCallback((el) => {
26699
27107
  localCanvasRef.current = el;
26700
27108
  if (typeof canvasRef === "function") {
@@ -26707,16 +27115,11 @@ function MinimapCanvasInner(props) {
26707
27115
  const canvas = localCanvasRef.current;
26708
27116
  if (!canvas) return;
26709
27117
  canvas.addEventListener("pointerdown", handlePointerDown);
26710
- canvas.addEventListener("pointermove", handlePointerMove);
26711
- canvas.addEventListener("pointerup", releaseCapture);
26712
- canvas.addEventListener("pointercancel", releaseCapture);
26713
27118
  return () => {
26714
27119
  canvas.removeEventListener("pointerdown", handlePointerDown);
26715
- canvas.removeEventListener("pointermove", handlePointerMove);
26716
- canvas.removeEventListener("pointerup", releaseCapture);
26717
- canvas.removeEventListener("pointercancel", releaseCapture);
27120
+ sessionRef.current?.cancel();
26718
27121
  };
26719
- }, [handlePointerDown, handlePointerMove, releaseCapture]);
27122
+ }, [handlePointerDown]);
26720
27123
  return /* @__PURE__ */ jsx(
26721
27124
  SceneViewCanvas,
26722
27125
  {
@@ -30856,6 +31259,6 @@ function mergeContributions(...bundles) {
30856
31259
  return out;
30857
31260
  }
30858
31261
 
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
31262
+ 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 };
31263
+ //# sourceMappingURL=chunk-KIVJXUYE.js.map
31264
+ //# sourceMappingURL=chunk-KIVJXUYE.js.map