@weasel-js/core 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,13 +13,13 @@ import earcut from 'earcut';
13
13
  import { applyToPoint, rotateAboutPoint, boxToBox, pointInPolygon, segmentsCross } from '@weasel-js/geom';
14
14
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
15
15
  import polygonClipping from 'polygon-clipping';
16
- import { matchSpec, mimeMatchesGlob } from '@weasel-js/gestures';
16
+ import { matchSpec, parseTargetSpec, mimeMatchesGlob } from '@weasel-js/gestures';
17
17
  import { NORMAL, IMPLICIT_TAGS } from '@weasel-js/modes';
18
18
  import { createHistory } from '@weasel-js/history';
19
19
  export * from '@weasel-js/history';
20
20
 
21
21
  // src/version.ts
22
- var VERSION = "1.0.0" ;
22
+ var VERSION = "1.0.1" ;
23
23
 
24
24
  // src/features/grid/roundToCell.ts
25
25
  function roundToCell(value, cellSize) {
@@ -2224,7 +2224,7 @@ function warnMissingGlyphOnce(family, cp) {
2224
2224
  `weasel layoutRuns: no glyph for U+${cp.toString(16).toUpperCase().padStart(4, "0")} (${JSON.stringify(ch)}) in "${family}", and the dynamic tier could not rasterize it \u2014 skipping the character. Bake it into the atlas, or call registerCanvasFont("${family}") to serve missing codepoints from installed fonts.`
2225
2225
  );
2226
2226
  }
2227
- function layoutRuns(runs, opts, origin) {
2227
+ function layoutRuns(runs, opts) {
2228
2228
  const ctx = { groups: /* @__PURE__ */ new Map() };
2229
2229
  const entries = [];
2230
2230
  let prevCp;
@@ -2394,7 +2394,7 @@ function layoutRuns(runs, opts, origin) {
2394
2394
  }
2395
2395
  }
2396
2396
  const lineBoxes = [];
2397
- let penY = origin.y;
2397
+ let penY = 0;
2398
2398
  let maxLineWidth = 0;
2399
2399
  const finiteWidth = Number.isFinite(opts.maxWidth) ? opts.maxWidth : 0;
2400
2400
  for (const line of lines) {
@@ -2406,7 +2406,7 @@ function layoutRuns(runs, opts, origin) {
2406
2406
  const slack = finiteWidth - line.width;
2407
2407
  return opts.align === "center" ? slack / 2 : slack;
2408
2408
  })();
2409
- const lineX0 = origin.x + alignShift;
2409
+ const lineX0 = alignShift;
2410
2410
  const baselineSource = line.entries[0] ?? line.blank;
2411
2411
  const lineBaselineY = baselineSource ? penY + baselineSource.font.common.base * (baselineSource.fontSize / baselineSource.font.info.size) : penY;
2412
2412
  let penX = lineX0;
@@ -2480,7 +2480,7 @@ function layoutRuns(runs, opts, origin) {
2480
2480
  groups: [...ctx.groups.values()],
2481
2481
  decorations,
2482
2482
  lines: lineBoxes,
2483
- bounds: { width: maxLineWidth, height: penY - origin.y }
2483
+ bounds: { width: maxLineWidth, height: penY }
2484
2484
  };
2485
2485
  }
2486
2486
  var LAYOUT_CACHE_VARIANT_LIMIT = 8;
@@ -2493,10 +2493,10 @@ function outlineBucket(runs, min) {
2493
2493
  for (const size of sizes) if (size >= min) n++;
2494
2494
  return n;
2495
2495
  }
2496
- function variantKey(runs, opts, origin) {
2497
- return `${opts.maxWidth}|${opts.lineHeight}|${opts.align}|${outlineBucket(runs, opts.outlineMinSize)}|${origin.x}|${origin.y}`;
2496
+ function variantKey(runs, opts) {
2497
+ return `${opts.maxWidth}|${opts.lineHeight}|${opts.align}|${outlineBucket(runs, opts.outlineMinSize)}`;
2498
2498
  }
2499
- function cachedLayoutRuns(runs, opts, origin) {
2499
+ function cachedLayoutRuns(runs, opts) {
2500
2500
  const fonts = glyphGeneration();
2501
2501
  let entry = cache2.get(runs);
2502
2502
  if (entry === void 0) {
@@ -2506,10 +2506,10 @@ function cachedLayoutRuns(runs, opts, origin) {
2506
2506
  entry.generation = fonts;
2507
2507
  entry.byVariant.clear();
2508
2508
  }
2509
- const key = variantKey(runs, opts, origin);
2509
+ const key = variantKey(runs, opts);
2510
2510
  const hit = entry.byVariant.get(key);
2511
2511
  if (hit !== void 0) return hit;
2512
- const laid = layoutRuns(runs, opts, origin);
2512
+ const laid = layoutRuns(runs, opts);
2513
2513
  if (entry.byVariant.size >= LAYOUT_CACHE_VARIANT_LIMIT) entry.byVariant.clear();
2514
2514
  entry.byVariant.set(key, laid);
2515
2515
  return laid;
@@ -2827,26 +2827,267 @@ function outlineStrokeMesh(glyphKey, d, emWidth, stroke) {
2827
2827
  return mesh.indices.length === 0 ? null : mesh;
2828
2828
  }
2829
2829
 
2830
+ // src/renderer/solidBatch.ts
2831
+ var MAX_VERTICES_PER_BATCH = 32768;
2832
+ var FLOATS_PER_VERTEX = 6;
2833
+ var INITIAL_VERTICES = 256;
2834
+ var INITIAL_INDICES = 384;
2835
+ var SolidBatch = class {
2836
+ gl;
2837
+ vao;
2838
+ vbo;
2839
+ ibo;
2840
+ verts = new Float32Array(INITIAL_VERTICES * FLOATS_PER_VERTEX);
2841
+ idx = new Uint32Array(INITIAL_INDICES);
2842
+ nVerts = 0;
2843
+ nIdx = 0;
2844
+ /** What the GPU buffers are currently sized for. */
2845
+ vboVerts = 0;
2846
+ iboIdx = 0;
2847
+ constructor(gl, prog) {
2848
+ const aPos = prog.attribute("a_position");
2849
+ const aColor = prog.attribute("a_vertexColor");
2850
+ if (aPos === void 0 || aColor === void 0) {
2851
+ throw new Error("SolidBatch: vertex-color program is missing a_position / a_vertexColor");
2852
+ }
2853
+ const vao = gl.createVertexArray();
2854
+ const vbo = gl.createBuffer();
2855
+ const ibo = gl.createBuffer();
2856
+ if (!vao || !vbo || !ibo) throw new Error("SolidBatch: failed to create GL objects");
2857
+ this.gl = gl;
2858
+ this.vao = vao;
2859
+ this.vbo = vbo;
2860
+ this.ibo = ibo;
2861
+ const stride = FLOATS_PER_VERTEX * 4;
2862
+ gl.bindVertexArray(vao);
2863
+ gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
2864
+ gl.enableVertexAttribArray(aPos);
2865
+ gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, stride, 0);
2866
+ gl.enableVertexAttribArray(aColor);
2867
+ gl.vertexAttribPointer(aColor, 4, gl.FLOAT, false, stride, 8);
2868
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
2869
+ gl.bindVertexArray(null);
2870
+ this.growGpu(INITIAL_VERTICES, INITIAL_INDICES);
2871
+ }
2872
+ get length() {
2873
+ return this.nIdx;
2874
+ }
2875
+ /** Whether staging `vertices` more would put the run past the per-flush cap. */
2876
+ wouldOverflow(vertices) {
2877
+ return this.nVerts + vertices > MAX_VERTICES_PER_BATCH;
2878
+ }
2879
+ /**
2880
+ * Append one rect's four corners through `m`, all carrying `rgba` (straight
2881
+ * alpha). An affine maps a rect to a parallelogram, so two triangles still
2882
+ * cover it and the batch draws at `u_model` identity.
2883
+ */
2884
+ pushRect(x, y, w, h, m, r, g, b, a) {
2885
+ this.reserve(4, 6);
2886
+ const v = this.verts;
2887
+ let i = this.nVerts * FLOATS_PER_VERTEX;
2888
+ const ma = m[0], mb = m[1], mc = m[3], md = m[4], mtx = m[6], mty = m[7];
2889
+ const x1 = x + w;
2890
+ const y1 = y + h;
2891
+ const ax = ma * x + mc * y + mtx, ay = mb * x + md * y + mty;
2892
+ const bx = ma * x1 + mc * y + mtx, by = mb * x1 + md * y + mty;
2893
+ const cx = ma * x1 + mc * y1 + mtx, cy = mb * x1 + md * y1 + mty;
2894
+ const dx = ma * x + mc * y1 + mtx, dy = mb * x + md * y1 + mty;
2895
+ v[i++] = ax;
2896
+ v[i++] = ay;
2897
+ v[i++] = r;
2898
+ v[i++] = g;
2899
+ v[i++] = b;
2900
+ v[i++] = a;
2901
+ v[i++] = bx;
2902
+ v[i++] = by;
2903
+ v[i++] = r;
2904
+ v[i++] = g;
2905
+ v[i++] = b;
2906
+ v[i++] = a;
2907
+ v[i++] = cx;
2908
+ v[i++] = cy;
2909
+ v[i++] = r;
2910
+ v[i++] = g;
2911
+ v[i++] = b;
2912
+ v[i++] = a;
2913
+ v[i++] = dx;
2914
+ v[i++] = dy;
2915
+ v[i++] = r;
2916
+ v[i++] = g;
2917
+ v[i++] = b;
2918
+ v[i++] = a;
2919
+ const base = this.nVerts;
2920
+ let j = this.nIdx;
2921
+ this.idx[j++] = base;
2922
+ this.idx[j++] = base + 1;
2923
+ this.idx[j++] = base + 2;
2924
+ this.idx[j++] = base;
2925
+ this.idx[j++] = base + 2;
2926
+ this.idx[j++] = base + 3;
2927
+ this.nVerts += 4;
2928
+ this.nIdx += 6;
2929
+ }
2930
+ /**
2931
+ * Append a tessellated mesh through `m`, all vertices carrying `rgba`. The
2932
+ * mesh's own indices are rebased onto the staged vertices, which is why the
2933
+ * index buffer is uploaded per flush rather than written once.
2934
+ */
2935
+ pushMesh(mesh, m, r, g, b, a) {
2936
+ const src = mesh.vertices;
2937
+ const srcIdx = mesh.indices;
2938
+ const n = src.length >> 1;
2939
+ this.reserve(n, srcIdx.length);
2940
+ const v = this.verts;
2941
+ let i = this.nVerts * FLOATS_PER_VERTEX;
2942
+ const ma = m[0], mb = m[1], mc = m[3], md = m[4], mtx = m[6], mty = m[7];
2943
+ for (let k = 0; k < n; k++) {
2944
+ const x = src[k * 2];
2945
+ const y = src[k * 2 + 1];
2946
+ v[i++] = ma * x + mc * y + mtx;
2947
+ v[i++] = mb * x + md * y + mty;
2948
+ v[i++] = r;
2949
+ v[i++] = g;
2950
+ v[i++] = b;
2951
+ v[i++] = a;
2952
+ }
2953
+ const base = this.nVerts;
2954
+ const out = this.idx;
2955
+ let j = this.nIdx;
2956
+ for (let k = 0; k < srcIdx.length; k++) out[j++] = base + srcIdx[k];
2957
+ this.nVerts += n;
2958
+ this.nIdx += srcIdx.length;
2959
+ }
2960
+ /** Upload the staged geometry and bind the batch VAO. Returns the index
2961
+ * count for the caller's `drawElements`. */
2962
+ uploadAndBind() {
2963
+ const gl = this.gl;
2964
+ if (this.nVerts > this.vboVerts || this.nIdx > this.iboIdx) {
2965
+ this.growGpu(this.nVerts, this.nIdx);
2966
+ }
2967
+ gl.bindVertexArray(this.vao);
2968
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
2969
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verts, 0, this.nVerts * FLOATS_PER_VERTEX);
2970
+ gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, this.idx, 0, this.nIdx);
2971
+ return this.nIdx;
2972
+ }
2973
+ reset() {
2974
+ this.nVerts = 0;
2975
+ this.nIdx = 0;
2976
+ }
2977
+ dispose() {
2978
+ const gl = this.gl;
2979
+ gl.deleteBuffer(this.vbo);
2980
+ gl.deleteBuffer(this.ibo);
2981
+ gl.deleteVertexArray(this.vao);
2982
+ }
2983
+ /** Grow the CPU arrays so `vertices` / `indices` more fit. */
2984
+ reserve(vertices, indices) {
2985
+ const needVerts = (this.nVerts + vertices) * FLOATS_PER_VERTEX;
2986
+ if (needVerts > this.verts.length) {
2987
+ let len = this.verts.length;
2988
+ while (len < needVerts) len *= 2;
2989
+ const grown = new Float32Array(len);
2990
+ grown.set(this.verts);
2991
+ this.verts = grown;
2992
+ }
2993
+ const needIdx = this.nIdx + indices;
2994
+ if (needIdx > this.idx.length) {
2995
+ let len = this.idx.length;
2996
+ while (len < needIdx) len *= 2;
2997
+ const grown = new Uint32Array(len);
2998
+ grown.set(this.idx);
2999
+ this.idx = grown;
3000
+ }
3001
+ }
3002
+ /** Size both GPU buffers for at least what is staged, doubling to amortize. */
3003
+ growGpu(vertices, indices) {
3004
+ const gl = this.gl;
3005
+ if (vertices > this.vboVerts) {
3006
+ let capacity = Math.max(this.vboVerts, INITIAL_VERTICES);
3007
+ while (capacity < vertices) capacity *= 2;
3008
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
3009
+ gl.bufferData(gl.ARRAY_BUFFER, capacity * FLOATS_PER_VERTEX * 4, gl.DYNAMIC_DRAW);
3010
+ this.vboVerts = capacity;
3011
+ }
3012
+ if (indices > this.iboIdx) {
3013
+ let capacity = Math.max(this.iboIdx, INITIAL_INDICES);
3014
+ while (capacity < indices) capacity *= 2;
3015
+ gl.bindVertexArray(this.vao);
3016
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.ibo);
3017
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, capacity * 4, gl.DYNAMIC_DRAW);
3018
+ gl.bindVertexArray(null);
3019
+ this.iboIdx = capacity;
3020
+ }
3021
+ }
3022
+ };
3023
+
2830
3024
  // src/renderer/draw.ts
2831
- function setColorMatrixUniforms(ctx, prog) {
3025
+ var FRAME_UPLOADS = /* @__PURE__ */ new WeakMap();
3026
+ var FRAME_PROJ = /* @__PURE__ */ new WeakMap();
3027
+ function uploadedFor(ctx, prog) {
3028
+ let byProgram = FRAME_UPLOADS.get(ctx);
3029
+ if (!byProgram) {
3030
+ byProgram = /* @__PURE__ */ new WeakMap();
3031
+ FRAME_UPLOADS.set(ctx, byProgram);
3032
+ }
3033
+ let uploaded = byProgram.get(prog);
3034
+ if (!uploaded) {
3035
+ uploaded = {};
3036
+ byProgram.set(prog, uploaded);
3037
+ }
3038
+ return uploaded;
3039
+ }
3040
+ function sameValues(prev, next) {
3041
+ if (prev === void 0 || prev.length !== next.length) return false;
3042
+ for (let i = 0; i < prev.length; i++) if (prev[i] !== next[i]) return false;
3043
+ return true;
3044
+ }
3045
+ var BATCH_MODEL = mat3.identity();
3046
+ function projFor(ctx) {
3047
+ let proj = FRAME_PROJ.get(ctx);
3048
+ if (!proj) {
3049
+ proj = mat3.screenToClip(ctx.widthCss, ctx.heightCss);
3050
+ FRAME_PROJ.set(ctx, proj);
3051
+ }
3052
+ return proj;
3053
+ }
3054
+ var COLOR_MATRIX_SCRATCH = new Float32Array(16);
3055
+ function setColorMatrixUniforms(ctx, prog, cm = ctx.state.colorMatrix) {
2832
3056
  const gl = ctx.gl;
2833
- const cm = ctx.state.colorMatrix;
2834
- const m4 = new Float32Array(16);
2835
- for (let row = 0; row < 4; row++) {
2836
- for (let col = 0; col < 4; col++) {
2837
- m4[col * 4 + row] = cm[row * 5 + col];
3057
+ const uploaded = uploadedFor(ctx, prog);
3058
+ const mLoc = prog.uniform("u_colorMatrix");
3059
+ if (mLoc !== void 0) {
3060
+ for (let row = 0; row < 4; row++) {
3061
+ for (let col = 0; col < 4; col++) {
3062
+ COLOR_MATRIX_SCRATCH[col * 4 + row] = cm[row * 5 + col];
3063
+ }
3064
+ }
3065
+ if (!sameValues(uploaded.colorMatrix, COLOR_MATRIX_SCRATCH)) {
3066
+ gl.uniformMatrix4fv(mLoc, false, COLOR_MATRIX_SCRATCH);
3067
+ uploaded.colorMatrix = Float32Array.from(COLOR_MATRIX_SCRATCH);
2838
3068
  }
2839
3069
  }
2840
- const mLoc = prog.uniform("u_colorMatrix");
2841
3070
  const bLoc = prog.uniform("u_colorBias");
2842
- if (mLoc !== void 0) gl.uniformMatrix4fv(mLoc, false, m4);
2843
- if (bLoc !== void 0) gl.uniform4f(bLoc, cm[4], cm[9], cm[14], cm[19]);
3071
+ if (bLoc !== void 0) {
3072
+ const bias = [cm[4], cm[9], cm[14], cm[19]];
3073
+ if (!sameValues(uploaded.colorBias, bias)) {
3074
+ gl.uniform4f(bLoc, bias[0], bias[1], bias[2], bias[3]);
3075
+ uploaded.colorBias = Float32Array.from(bias);
3076
+ }
3077
+ }
2844
3078
  }
2845
- function fillMeshHandle(ctx, path) {
3079
+ function fillMesh(ctx, path) {
2846
3080
  if (ctx.flattenTolerance !== void 0) {
2847
- return ctx.meshCache.uploadTransient(tessellate(path, { flattenTolerance: ctx.flattenTolerance }));
3081
+ return tessellate(path, { flattenTolerance: ctx.flattenTolerance });
2848
3082
  }
2849
- return ctx.meshCache.handleFor(getMesh(path));
3083
+ return getMesh(path);
3084
+ }
3085
+ function meshHandle(ctx, mesh) {
3086
+ if (ctx.flattenTolerance !== void 0) return ctx.meshCache.uploadTransient(mesh);
3087
+ return ctx.meshCache.handleFor(mesh);
3088
+ }
3089
+ function fillMeshHandle(ctx, path) {
3090
+ return meshHandle(ctx, fillMesh(ctx, path));
2850
3091
  }
2851
3092
  function dispatch(ctx, cmd) {
2852
3093
  switch (cmd.kind) {
@@ -2855,10 +3096,13 @@ function dispatch(ctx, cmd) {
2855
3096
  case "path":
2856
3097
  return drawPath(ctx, cmd);
2857
3098
  case "text":
3099
+ flushSolids(ctx);
2858
3100
  return drawText(ctx, cmd);
2859
3101
  case "image":
3102
+ flushSolids(ctx);
2860
3103
  return drawImage(ctx, cmd);
2861
3104
  case "shader":
3105
+ flushSolids(ctx);
2862
3106
  return drawShader(ctx, cmd);
2863
3107
  }
2864
3108
  }
@@ -2987,11 +3231,13 @@ function drawGroup(ctx, cmd) {
2987
3231
  "weasel: clip nesting depth exceeded (max 7). You can't nest more than 7 levels of clipped containers in a single draw tree. Flatten the hierarchy or compose poses outside the scene graph."
2988
3232
  );
2989
3233
  }
3234
+ flushSolids(ctx);
2990
3235
  pushClip(ctx, cmd.clip, newDepth);
2991
3236
  ctx.clipDepth = newDepth;
2992
3237
  }
2993
3238
  for (const child of cmd.children) dispatch(ctx, child);
2994
3239
  if (cmd.clip) {
3240
+ flushSolids(ctx);
2995
3241
  popClip(ctx, cmd.clip, ctx.clipDepth - 1);
2996
3242
  ctx.clipDepth -= 1;
2997
3243
  }
@@ -3000,38 +3246,98 @@ function drawGroup(ctx, cmd) {
3000
3246
  function drawPath(ctx, cmd) {
3001
3247
  if (!cmd.fill && !cmd.stroke) return;
3002
3248
  if (cmd.fill) {
3003
- const isSolidRectFast = cmd.path.kind === "rect" && (cmd.fill.fill === void 0 || cmd.fill.fill === "solid") && (!cmd.vertexColors || cmd.vertexColors.length === 0) && ctx.rectVao !== null && ctx.rectVbo !== null;
3004
- if (isSolidRectFast && cmd.path.kind === "rect") {
3005
- drawRectFast(ctx, cmd.path, cmd.fill);
3249
+ const batchablePaint = (cmd.fill.fill === void 0 || cmd.fill.fill === "solid") && (!cmd.vertexColors || cmd.vertexColors.length === 0);
3250
+ const solid = cmd.fill;
3251
+ if (batchablePaint && cmd.path.kind === "rect") {
3252
+ pushRect(ctx, cmd.path, solid);
3006
3253
  } else {
3007
- const handle = fillMeshHandle(ctx, cmd.path);
3008
- if (cmd.vertexColors && cmd.vertexColors.length > 0 && (cmd.fill.fill === void 0 || cmd.fill.fill === "solid")) {
3009
- drawPathFillVColor(ctx, cmd, cmd.fill, handle);
3010
- } else if (handle.requiresStencil) {
3011
- drawPathFillStencil(ctx, cmd.fill, handle);
3254
+ const mesh = fillMesh(ctx, cmd.path);
3255
+ if (batchablePaint && canBatchMesh(mesh)) {
3256
+ pushMesh(ctx, mesh, solid);
3012
3257
  } else {
3013
- drawPathFillByKind(ctx, cmd.fill, handle);
3258
+ flushSolids(ctx);
3259
+ const handle = meshHandle(ctx, mesh);
3260
+ if (cmd.vertexColors && cmd.vertexColors.length > 0 && (cmd.fill.fill === void 0 || cmd.fill.fill === "solid")) {
3261
+ drawPathFillVColor(ctx, cmd, solid, handle);
3262
+ } else if (handle.requiresStencil) {
3263
+ drawPathFillStencil(ctx, cmd.fill, handle);
3264
+ } else {
3265
+ drawPathFillByKind(ctx, cmd.fill, handle);
3266
+ }
3014
3267
  }
3015
3268
  }
3016
3269
  }
3017
- if (cmd.stroke) {
3018
- drawPathStroke(ctx, cmd);
3270
+ if (cmd.stroke) drawPathStroke(ctx, cmd);
3271
+ }
3272
+ function isIdentityColorMatrix(cm) {
3273
+ return cm === IDENTITY_COLOR_MATRIX || sameValues(IDENTITY_COLOR_MATRIX, cm);
3274
+ }
3275
+ function stagedStateIsLive(ctx, staged) {
3276
+ if (staged.clipDepth !== ctx.clipDepth) return false;
3277
+ if (!staged.foldsAlpha && staged.alpha !== ctx.state.alpha) return false;
3278
+ const colorMatrix = ctx.state.colorMatrix;
3279
+ return staged.colorMatrix === colorMatrix || sameValues(staged.colorMatrix, colorMatrix);
3280
+ }
3281
+ var MAX_BATCHED_MESH_VERTICES = 256;
3282
+ function canBatchMesh(mesh) {
3283
+ return !mesh.requiresStencil && mesh.vertices.length >> 1 <= MAX_BATCHED_MESH_VERTICES;
3284
+ }
3285
+ function stageSolid(ctx, vertices) {
3286
+ if (ctx.solidState !== void 0 && !stagedStateIsLive(ctx, ctx.solidState)) flushSolids(ctx);
3287
+ if (ctx.solidBatch.wouldOverflow(vertices)) flushSolids(ctx);
3288
+ if (ctx.solidState === void 0) {
3289
+ const colorMatrix = ctx.state.colorMatrix;
3290
+ const foldsAlpha = isIdentityColorMatrix(colorMatrix);
3291
+ ctx.solidState = {
3292
+ alpha: foldsAlpha ? 1 : ctx.state.alpha,
3293
+ colorMatrix,
3294
+ clipDepth: ctx.clipDepth,
3295
+ foldsAlpha
3296
+ };
3019
3297
  }
3298
+ return ctx.solidState;
3299
+ }
3300
+ function stagedColor(ctx, staged, paint) {
3301
+ const [r, g, b, a] = resolveColor(paint.color);
3302
+ return [r, g, b, a * (paint.opacity ?? 1) * (staged.foldsAlpha ? ctx.state.alpha : 1)];
3303
+ }
3304
+ function pushRect(ctx, rect, fill) {
3305
+ const staged = stageSolid(ctx, 4);
3306
+ const [r, g, b, a] = stagedColor(ctx, staged, fill);
3307
+ ctx.solidBatch.pushRect(
3308
+ rect.x,
3309
+ rect.y,
3310
+ rect.width,
3311
+ rect.height,
3312
+ ctx.state.transform,
3313
+ r,
3314
+ g,
3315
+ b,
3316
+ a
3317
+ );
3318
+ }
3319
+ function pushMesh(ctx, mesh, paint) {
3320
+ const staged = stageSolid(ctx, mesh.vertices.length >> 1);
3321
+ const [r, g, b, a] = stagedColor(ctx, staged, paint);
3322
+ ctx.solidBatch.pushMesh(mesh, ctx.state.transform, r, g, b, a);
3020
3323
  }
3021
- function drawRectFast(ctx, rect, fill) {
3324
+ function flushSolids(ctx) {
3325
+ const batch = ctx.solidBatch;
3326
+ const staged = ctx.solidState;
3327
+ if (batch.length === 0 || staged === void 0) return;
3022
3328
  const gl = ctx.gl;
3023
- const { x, y, width: w, height: h } = rect;
3024
- const corners = new Float32Array([x, y, x + w, y, x + w, y + h, x, y + h]);
3025
- gl.useProgram(ctx.pathFill.handle);
3026
- gl.bindVertexArray(ctx.rectVao);
3027
- gl.bindBuffer(gl.ARRAY_BUFFER, ctx.rectVbo);
3028
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, corners);
3029
- setProjAndModel(ctx, ctx.pathFill);
3030
- setSolidPaintUniforms(ctx, ctx.pathFill, fill.color, fill.opacity);
3031
- setColorMatrixUniforms(ctx, ctx.pathFill);
3032
- applyClipTest(ctx);
3033
- gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, 0);
3329
+ const prog = ctx.pathFillVColor;
3330
+ gl.useProgram(prog.handle);
3331
+ const indexCount = batch.uploadAndBind();
3332
+ setProjAndModel(ctx, prog, BATCH_MODEL);
3333
+ gl.uniform4f(prog.uniform("u_color"), 1, 1, 1, 1);
3334
+ gl.uniform1f(prog.uniform("u_alpha"), staged.alpha);
3335
+ setColorMatrixUniforms(ctx, prog, staged.colorMatrix);
3336
+ applyClipTest(ctx, staged.clipDepth);
3337
+ gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_INT, 0);
3034
3338
  gl.bindVertexArray(null);
3339
+ batch.reset();
3340
+ ctx.solidState = void 0;
3035
3341
  }
3036
3342
  function expandAnchorColors(perAnchor, handle) {
3037
3343
  const aA = handle.anchorA;
@@ -3076,11 +3382,18 @@ function drawPathFillVColor(ctx, cmd, fill, handle) {
3076
3382
  gl.bindVertexArray(null);
3077
3383
  gl.deleteBuffer(colorVbo);
3078
3384
  }
3079
- function setProjAndModel(ctx, prog) {
3385
+ function setProjAndModel(ctx, prog, model = ctx.state.transform) {
3080
3386
  const gl = ctx.gl;
3081
- const proj = mat3.screenToClip(ctx.widthCss, ctx.heightCss);
3082
- gl.uniformMatrix3fv(prog.uniform("u_proj"), false, proj);
3083
- gl.uniformMatrix3fv(prog.uniform("u_model"), false, ctx.state.transform);
3387
+ const uploaded = uploadedFor(ctx, prog);
3388
+ const proj = projFor(ctx);
3389
+ if (!sameValues(uploaded.proj, proj)) {
3390
+ gl.uniformMatrix3fv(prog.uniform("u_proj"), false, proj);
3391
+ uploaded.proj = Float32Array.from(proj);
3392
+ }
3393
+ if (!sameValues(uploaded.model, model)) {
3394
+ gl.uniformMatrix3fv(prog.uniform("u_model"), false, model);
3395
+ uploaded.model = Float32Array.from(model);
3396
+ }
3084
3397
  }
3085
3398
  function setSolidPaintUniforms(ctx, prog, color, opacity) {
3086
3399
  const gl = ctx.gl;
@@ -3187,9 +3500,8 @@ function drawPathFillGradient(ctx, fill, handle) {
3187
3500
  gl.drawElements(gl.TRIANGLES, handle.indexCount, gl.UNSIGNED_INT, 0);
3188
3501
  gl.bindVertexArray(null);
3189
3502
  }
3190
- function applyClipTest(ctx) {
3503
+ function applyClipTest(ctx, depth = ctx.clipDepth) {
3191
3504
  const gl = ctx.gl;
3192
- const depth = ctx.clipDepth;
3193
3505
  if (depth === 0) {
3194
3506
  gl.disable(gl.STENCIL_TEST);
3195
3507
  return;
@@ -3273,6 +3585,7 @@ function drawPathStroke(ctx, cmd) {
3273
3585
  }
3274
3586
  const align = stroke.align ?? "center";
3275
3587
  if (cmd.path.kind === "polygon" && align !== "center") {
3588
+ flushSolids(ctx);
3276
3589
  drawPathStrokeStenciled(ctx, cmd, align);
3277
3590
  return;
3278
3591
  }
@@ -3283,6 +3596,12 @@ function drawPathStrokeUnclipped(ctx, cmd) {
3283
3596
  const solid = stroke.paint;
3284
3597
  const mesh = tessellateStroke(cmd.path, stroke, { flattenTolerance: ctx.flattenTolerance });
3285
3598
  if (mesh.indices.length === 0) return;
3599
+ const hasVColors = !!(stroke.vertexColors && stroke.vertexColors.length > 0);
3600
+ if (!hasVColors && canBatchMesh(mesh)) {
3601
+ pushMesh(ctx, mesh, solid);
3602
+ return;
3603
+ }
3604
+ flushSolids(ctx);
3286
3605
  const handle = ctx.meshCache.uploadTransient(mesh);
3287
3606
  const gl = ctx.gl;
3288
3607
  if (stroke.vertexColors && stroke.vertexColors.length > 0) {
@@ -3380,20 +3699,17 @@ function drawText(ctx, cmd) {
3380
3699
  const maxWidth = cmd.maxWidth ?? Infinity;
3381
3700
  const minScreen = ctx.textOutlineMinScreenSize ?? OUTLINE_MIN_SCREEN_PX;
3382
3701
  const outlineMinSize = Number.isFinite(minScreen) ? minScreen / modelScale(ctx.state.transform) : void 0;
3383
- const laid = cachedLayoutRuns(
3384
- cmd.runs,
3385
- { maxWidth, lineHeight, align, outlineMinSize },
3386
- { x: cmd.x, y: cmd.y }
3387
- );
3702
+ const laid = cachedLayoutRuns(cmd.runs, { maxWidth, lineHeight, align, outlineMinSize });
3388
3703
  if (laid.groups.length === 0 && laid.decorations.length === 0) return;
3389
- const dy = verticalAlignOffset(cmd.verticalAlign, cmd.height, laid.bounds.height);
3704
+ const dx = cmd.x;
3705
+ const dy = cmd.y + verticalAlignOffset(cmd.verticalAlign, cmd.height, laid.bounds.height);
3390
3706
  const gl = ctx.gl;
3391
3707
  applyClipTest(ctx);
3392
3708
  const preparedPrograms = /* @__PURE__ */ new Set();
3393
3709
  let currentProg = null;
3394
3710
  for (const group of laid.groups) {
3395
3711
  if (group.source === "outline") {
3396
- drawTextOutlineGroup(ctx, group, dy);
3712
+ drawTextOutlineGroup(ctx, group, dx, dy);
3397
3713
  currentProg = null;
3398
3714
  continue;
3399
3715
  }
@@ -3408,31 +3724,31 @@ function drawText(ctx, cmd) {
3408
3724
  setColorMatrixUniforms(ctx, prog);
3409
3725
  gl.uniform1f(prog.uniform("u_alpha"), ctx.state.alpha);
3410
3726
  }
3411
- drawTextGroup(ctx, group, prog, dy);
3727
+ drawTextGroup(ctx, group, prog, dx, dy);
3412
3728
  }
3413
- drawTextDecorations(ctx, laid.decorations, dy);
3729
+ drawTextDecorations(ctx, laid.decorations, dx, dy);
3414
3730
  }
3415
3731
  var SYNTHETIC_ITALIC_RADIANS = 0.2094;
3416
- function drawTextOutlineGroup(ctx, group, dy) {
3417
- const mesh = outlineGroupMesh(group, dy);
3732
+ function drawTextOutlineGroup(ctx, group, dx, dy) {
3733
+ const mesh = outlineGroupMesh(group, dx, dy);
3418
3734
  if (mesh) drawPathFillByKind(ctx, group.fill, ctx.meshCache.uploadTransient(mesh));
3419
3735
  if (!group.stroke) return;
3420
- const strokeMesh = outlineGroupStrokeMesh(group, dy);
3736
+ const strokeMesh = outlineGroupStrokeMesh(group, dx, dy);
3421
3737
  if (strokeMesh) {
3422
3738
  drawPathFillByKind(ctx, group.stroke.paint, ctx.meshCache.uploadTransient(strokeMesh));
3423
3739
  }
3424
3740
  }
3425
- function outlineGroupMesh(group, dy) {
3426
- return mergeGlyphMeshes(group, dy, (glyph) => outlineMesh(glyph.key, glyph.d));
3741
+ function outlineGroupMesh(group, dx, dy) {
3742
+ return mergeGlyphMeshes(group, dx, dy, (glyph) => outlineMesh(glyph.key, glyph.d));
3427
3743
  }
3428
- function outlineGroupStrokeMesh(group, dy) {
3744
+ function outlineGroupStrokeMesh(group, dx, dy) {
3429
3745
  const stroke = group.stroke;
3430
3746
  if (!stroke) return null;
3431
3747
  const width = stroke.width ?? 1;
3432
3748
  if (!(width > 0)) return null;
3433
- return mergeGlyphMeshes(group, dy, (glyph) => glyph.scale > 0 ? outlineStrokeMesh(glyph.key, glyph.d, quantizeEmWidth(width / glyph.scale), stroke) : null);
3749
+ return mergeGlyphMeshes(group, dx, dy, (glyph) => glyph.scale > 0 ? outlineStrokeMesh(glyph.key, glyph.d, quantizeEmWidth(width / glyph.scale), stroke) : null);
3434
3750
  }
3435
- function mergeGlyphMeshes(group, dy, meshFor) {
3751
+ function mergeGlyphMeshes(group, dx, dy, meshFor) {
3436
3752
  const parts = [];
3437
3753
  let vertexFloats = 0;
3438
3754
  let indexCount = 0;
@@ -3455,7 +3771,7 @@ function mergeGlyphMeshes(group, dy, meshFor) {
3455
3771
  for (let k = 0; k < mesh.vertices.length; k += 2) {
3456
3772
  const ex = mesh.vertices[k];
3457
3773
  const ey = mesh.vertices[k + 1];
3458
- vertices[vi++] = x + (ex - ey * shear) * scale2;
3774
+ vertices[vi++] = x + dx + (ex - ey * shear) * scale2;
3459
3775
  vertices[vi++] = baselineY + dy + ey * scale2;
3460
3776
  }
3461
3777
  for (let k = 0; k < mesh.indices.length; k++) indices[ii++] = base + mesh.indices[k];
@@ -3463,7 +3779,7 @@ function mergeGlyphMeshes(group, dy, meshFor) {
3463
3779
  }
3464
3780
  return { vertices, indices };
3465
3781
  }
3466
- function drawTextDecorations(ctx, decorations, dy) {
3782
+ function drawTextDecorations(ctx, decorations, dx, dy) {
3467
3783
  if (decorations.length === 0) return;
3468
3784
  const batches = /* @__PURE__ */ new Map();
3469
3785
  for (const d of decorations) {
@@ -3486,14 +3802,15 @@ function drawTextDecorations(ctx, decorations, dy) {
3486
3802
  const vertices = new Float32Array(rects.length * 4 * 2);
3487
3803
  let vi = 0;
3488
3804
  for (const d of rects) {
3805
+ const dx0 = d.x0 + dx, dx1 = d.x1 + dx;
3489
3806
  const dy0 = d.y0 + dy, dy1 = d.y1 + dy;
3490
- vertices[vi++] = d.x0;
3807
+ vertices[vi++] = dx0;
3491
3808
  vertices[vi++] = dy0;
3492
- vertices[vi++] = d.x1;
3809
+ vertices[vi++] = dx1;
3493
3810
  vertices[vi++] = dy0;
3494
- vertices[vi++] = d.x0;
3811
+ vertices[vi++] = dx0;
3495
3812
  vertices[vi++] = dy1;
3496
- vertices[vi++] = d.x1;
3813
+ vertices[vi++] = dx1;
3497
3814
  vertices[vi++] = dy1;
3498
3815
  }
3499
3816
  const indices = new Uint32Array(rects.length * 6);
@@ -3532,7 +3849,7 @@ function drawTextDecorations(ctx, decorations, dy) {
3532
3849
  gl.deleteBuffer(ibo);
3533
3850
  }
3534
3851
  }
3535
- function drawTextGroup(ctx, group, prog, dy) {
3852
+ function drawTextGroup(ctx, group, prog, dx, dy) {
3536
3853
  if (group.source === "canvas") {
3537
3854
  if (!syncDynamicPageTexture(ctx.textureCache, group.page)) return;
3538
3855
  } else {
@@ -3543,23 +3860,24 @@ function drawTextGroup(ctx, group, prog, dy) {
3543
3860
  const vertices = new Float32Array(group.quads.length * 4 * 5);
3544
3861
  let vi = 0;
3545
3862
  for (const q of group.quads) {
3863
+ const x0 = q.x0 + dx, x1 = q.x1 + dx;
3546
3864
  const y0 = q.y0 + dy, y1 = q.y1 + dy, by = q.baselineY + dy;
3547
- vertices[vi++] = q.x0;
3865
+ vertices[vi++] = x0;
3548
3866
  vertices[vi++] = y0;
3549
3867
  vertices[vi++] = q.u0;
3550
3868
  vertices[vi++] = q.v0;
3551
3869
  vertices[vi++] = by;
3552
- vertices[vi++] = q.x1;
3870
+ vertices[vi++] = x1;
3553
3871
  vertices[vi++] = y0;
3554
3872
  vertices[vi++] = q.u1;
3555
3873
  vertices[vi++] = q.v0;
3556
3874
  vertices[vi++] = by;
3557
- vertices[vi++] = q.x0;
3875
+ vertices[vi++] = x0;
3558
3876
  vertices[vi++] = y1;
3559
3877
  vertices[vi++] = q.u0;
3560
3878
  vertices[vi++] = q.v1;
3561
3879
  vertices[vi++] = by;
3562
- vertices[vi++] = q.x1;
3880
+ vertices[vi++] = x1;
3563
3881
  vertices[vi++] = y1;
3564
3882
  vertices[vi++] = q.u1;
3565
3883
  vertices[vi++] = q.v1;
@@ -3786,10 +4104,7 @@ var WeaselRenderer = class {
3786
4104
  programRegistry = /* @__PURE__ */ new Map();
3787
4105
  quadVbo = null;
3788
4106
  quadIbo = null;
3789
- /** Shared rect-fill geometry: 4 verts × 2 floats (dynamic), 6-index static IBO. */
3790
- rectVao = null;
3791
- rectVbo = null;
3792
- rectIbo = null;
4107
+ solidBatch;
3793
4108
  groupState = new GroupState();
3794
4109
  widthCss;
3795
4110
  heightCss;
@@ -3864,31 +4179,7 @@ var WeaselRenderer = class {
3864
4179
  this.imageCache = new GLImageCache(this.gl, this.imageMinification);
3865
4180
  this.gradRampCache = new GradientRampCache(this.gl);
3866
4181
  this.uploadQuadGeometry();
3867
- this.uploadRectGeometry(aPos);
3868
- }
3869
- /** Allocate the shared rect VAO + dynamic VBO (4 verts × 2 floats) + static
3870
- * IBO (6 indices). drawRectFast bufferSubData's the 4 corner coords each
3871
- * draw, avoiding per-rect buffer allocation in animated demos. */
3872
- uploadRectGeometry(aPositionLoc) {
3873
- const gl = this.gl;
3874
- this.rectVao = gl.createVertexArray();
3875
- this.rectVbo = gl.createBuffer();
3876
- this.rectIbo = gl.createBuffer();
3877
- if (!this.rectVao || !this.rectVbo || !this.rectIbo) {
3878
- throw new Error("WeaselRenderer: failed to create rect-fast geometry");
3879
- }
3880
- gl.bindVertexArray(this.rectVao);
3881
- gl.bindBuffer(gl.ARRAY_BUFFER, this.rectVbo);
3882
- gl.bufferData(gl.ARRAY_BUFFER, 8 * 4, gl.DYNAMIC_DRAW);
3883
- gl.enableVertexAttribArray(aPositionLoc);
3884
- gl.vertexAttribPointer(aPositionLoc, 2, gl.FLOAT, false, 0, 0);
3885
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.rectIbo);
3886
- gl.bufferData(
3887
- gl.ELEMENT_ARRAY_BUFFER,
3888
- new Uint32Array([0, 1, 2, 0, 2, 3]),
3889
- gl.STATIC_DRAW
3890
- );
3891
- gl.bindVertexArray(null);
4182
+ this.solidBatch = new SolidBatch(this.gl, this.pathFillVColor);
3892
4183
  }
3893
4184
  uploadQuadGeometry() {
3894
4185
  const gl = this.gl;
@@ -3976,7 +4267,7 @@ var WeaselRenderer = class {
3976
4267
  this.gradRampCache = new GradientRampCache(this.gl);
3977
4268
  markAllFontsNotUploaded();
3978
4269
  this.uploadQuadGeometry();
3979
- this.uploadRectGeometry(aPos);
4270
+ this.solidBatch = new SolidBatch(this.gl, this.pathFillVColor);
3980
4271
  for (const id of this.programRegistry.keys()) {
3981
4272
  const src = getProgramSource(id);
3982
4273
  if (!src) continue;
@@ -4036,9 +4327,7 @@ var WeaselRenderer = class {
4036
4327
  this.gradRampCache.free();
4037
4328
  if (this.quadVbo) gl.deleteBuffer(this.quadVbo);
4038
4329
  if (this.quadIbo) gl.deleteBuffer(this.quadIbo);
4039
- if (this.rectVbo) gl.deleteBuffer(this.rectVbo);
4040
- if (this.rectIbo) gl.deleteBuffer(this.rectIbo);
4041
- if (this.rectVao) gl.deleteVertexArray(this.rectVao);
4330
+ this.solidBatch.dispose();
4042
4331
  }
4043
4332
  /**
4044
4333
  * Draw one frame.
@@ -4071,8 +4360,7 @@ var WeaselRenderer = class {
4071
4360
  programRegistry: this.programRegistry,
4072
4361
  quadVbo: this.quadVbo,
4073
4362
  quadIbo: this.quadIbo,
4074
- rectVao: this.rectVao,
4075
- rectVbo: this.rectVbo,
4363
+ solidBatch: this.solidBatch,
4076
4364
  state: this.groupState,
4077
4365
  widthCss: this.widthCss,
4078
4366
  heightCss: this.heightCss,
@@ -4082,6 +4370,7 @@ var WeaselRenderer = class {
4082
4370
  viewMatrix
4083
4371
  };
4084
4372
  for (const cmd of commands) dispatch(ctx, cmd);
4373
+ flushSolids(ctx);
4085
4374
  this.meshCache.freeTransient();
4086
4375
  }
4087
4376
  resize(dims) {
@@ -4111,6 +4400,10 @@ var WeaselRenderer = class {
4111
4400
  return this.pathFillVColor;
4112
4401
  }
4113
4402
  /** @internal */
4403
+ _solidBatch() {
4404
+ return this.solidBatch;
4405
+ }
4406
+ /** @internal */
4114
4407
  _textSdf() {
4115
4408
  return this.textSdf;
4116
4409
  }
@@ -5380,12 +5673,7 @@ function moveGestureAdapter(scene) {
5380
5673
  return {
5381
5674
  getNode: (id) => scene.get(asNodeId(id)),
5382
5675
  getNodes: () => {
5383
- const out = [];
5384
- for (const id of scene.renderOrder()) {
5385
- const n = scene.get(id);
5386
- if (n) out.push(n);
5387
- }
5388
- return out;
5676
+ return [...scene.renderOrderNodes()];
5389
5677
  },
5390
5678
  getPose: (id) => scene.get(asNodeId(id)).pose,
5391
5679
  getParent: (id) => scene.get(asNodeId(id))?.parent ?? null,
@@ -6001,12 +6289,7 @@ function defaultCommitAdapter(scene) {
6001
6289
  return {
6002
6290
  getNode: (id) => scene.get(asNodeId(id)),
6003
6291
  getNodes: () => {
6004
- const out = [];
6005
- for (const id of scene.renderOrder()) {
6006
- const n = scene.get(id);
6007
- if (n) out.push(n);
6008
- }
6009
- return out;
6292
+ return [...scene.renderOrderNodes()];
6010
6293
  },
6011
6294
  getPose: (id) => scene.get(asNodeId(id)).pose,
6012
6295
  getParent: (id) => scene.get(asNodeId(id))?.parent ?? null,
@@ -10706,18 +10989,39 @@ function modsCount(mods) {
10706
10989
  }
10707
10990
  function targetRank(target) {
10708
10991
  if (target === void 0) return 0;
10709
- if (typeof target !== "string") return 1;
10710
- if (target.startsWith("kind:")) return target.endsWith(":selected") ? 3 : 2;
10711
- if (target.startsWith("affordance:")) return 2;
10712
- return 1;
10992
+ const form = parseTargetSpec(target);
10993
+ if (form === null) return 1;
10994
+ switch (form.form) {
10995
+ case "kind":
10996
+ return form.requireSelected ? 3 : 2;
10997
+ case "affordance":
10998
+ return 2;
10999
+ case "body":
11000
+ return 1;
11001
+ case "predicate":
11002
+ return 1;
11003
+ default: {
11004
+ return 1;
11005
+ }
11006
+ }
10713
11007
  }
10714
11008
  function targetConsultsAffordance(specTarget) {
10715
11009
  if (specTarget === void 0) return false;
10716
- if (typeof specTarget === "object" && specTarget !== null && "kindOf" in specTarget) {
10717
- const kindOf2 = specTarget.kindOf;
10718
- return kindOf2?.readsAffordance !== false;
11010
+ const form = parseTargetSpec(specTarget);
11011
+ if (form === null) return false;
11012
+ switch (form.form) {
11013
+ case "predicate":
11014
+ return form.kindOf.readsAffordance !== false;
11015
+ case "affordance":
11016
+ return true;
11017
+ case "body":
11018
+ return false;
11019
+ case "kind":
11020
+ return false;
11021
+ default: {
11022
+ return false;
11023
+ }
10719
11024
  }
10720
- return typeof specTarget === "string" && specTarget.startsWith("affordance:");
10721
11025
  }
10722
11026
  function specTargetOf(spec) {
10723
11027
  return "target" in spec ? spec.target : void 0;
@@ -10760,7 +11064,7 @@ function reportDeadClaim(owner, warn) {
10760
11064
  );
10761
11065
  }
10762
11066
  function specificity(spec) {
10763
- const t = targetRank("target" in spec ? spec.target : void 0);
11067
+ const t = targetRank(specTargetOf(spec));
10764
11068
  const mods = "mods" in spec ? spec.mods : void 0;
10765
11069
  const m = modsCount(mods);
10766
11070
  const p = "phase" in spec && spec.phase !== void 0 ? 1 : 0;
@@ -13633,21 +13937,18 @@ function textLineBoxes(pose, opts = {}) {
13633
13937
  const style = resolveTextStyle(pose.style);
13634
13938
  const source = pose.runs && pose.runs.length > 0 ? pose.runs : pose.text;
13635
13939
  const runs = resolveRuns(toRuns(source), style);
13636
- const laid = layoutRuns(
13637
- runs,
13638
- {
13639
- maxWidth: opts.maxWidth ?? pose.width,
13640
- lineHeight: style.lineHeight,
13641
- align: style.align
13642
- },
13643
- { x: pose.x, y: pose.y }
13644
- );
13645
- const dy = verticalAlignOffset(pose.verticalAlign, pose.height, laid.bounds.height);
13940
+ const laid = layoutRuns(runs, {
13941
+ maxWidth: opts.maxWidth ?? pose.width,
13942
+ lineHeight: style.lineHeight,
13943
+ align: style.align
13944
+ });
13945
+ const dx = pose.x;
13946
+ const dy = pose.y + verticalAlignOffset(pose.verticalAlign, pose.height, laid.bounds.height);
13646
13947
  const out = [];
13647
13948
  for (const line of laid.lines) {
13648
13949
  if (!opts.includeEmpty && line.x1 <= line.x0) continue;
13649
13950
  out.push({
13650
- x: line.x0 - padding,
13951
+ x: line.x0 + dx - padding,
13651
13952
  y: line.y0 + dy - padding,
13652
13953
  width: line.x1 - line.x0 + padding * 2,
13653
13954
  height: line.y1 - line.y0 + padding * 2
@@ -16630,6 +16931,15 @@ function wrapWithPoseRotation(cmds, pose) {
16630
16931
  children: cmds
16631
16932
  }];
16632
16933
  }
16934
+
16935
+ // src/canvas/wrapNodeOutput.ts
16936
+ function wrapNodeOutput(cmds, pose, alpha) {
16937
+ const rotated = wrapWithPoseRotation(cmds, pose);
16938
+ if (alpha !== 1 && rotated.length > 0) {
16939
+ return [{ kind: "group", alpha, children: rotated }];
16940
+ }
16941
+ return rotated;
16942
+ }
16633
16943
  function CursorCoordsHud({ canvasRef, viewRef, offset }) {
16634
16944
  const [state, setState] = useState({
16635
16945
  client: { x: 0, y: 0 },
@@ -16881,12 +17191,7 @@ function buildSceneLayer(cfg, adapter, debugSink, boundsOfFn, hideIds, slot) {
16881
17191
  const oy = pose.y ?? (b ? b.y : 0);
16882
17192
  debugSink.recordOrigin(obj.id, { x: ox, y: oy });
16883
17193
  }
16884
- const rotated = wrapWithPoseRotation(cmds, pose);
16885
- const alpha = cfg.alphaFor ? cfg.alphaFor(obj.id) : 1;
16886
- if (alpha !== 1 && rotated.length > 0) {
16887
- return [{ kind: "group", alpha, children: rotated }];
16888
- }
16889
- return rotated;
17194
+ return wrapNodeOutput(cmds, pose, cfg.alphaFor ? cfg.alphaFor(obj.id) : 1);
16890
17195
  };
16891
17196
  const hierarchicalAdapter = cfg.toPose ? {
16892
17197
  ...a,
@@ -16910,13 +17215,8 @@ function buildSceneLayer(cfg, adapter, debugSink, boundsOfFn, hideIds, slot) {
16910
17215
  const pose = toPose(obj);
16911
17216
  if (drawOne) {
16912
17217
  const cmds = drawOne(obj, pose, view);
16913
- const rotated = wrapWithPoseRotation(cmds, pose);
16914
- const alpha = cfg.alphaFor ? cfg.alphaFor(obj.id) : 1;
16915
- if (alpha !== 1 && rotated.length > 0) {
16916
- children.push({ kind: "group", alpha, children: rotated });
16917
- } else {
16918
- for (const cmd of rotated) children.push(cmd);
16919
- }
17218
+ const wrapped = wrapNodeOutput(cmds, pose, cfg.alphaFor ? cfg.alphaFor(obj.id) : 1);
17219
+ for (const cmd of wrapped) children.push(cmd);
16920
17220
  }
16921
17221
  if (debugSink) {
16922
17222
  const b = boundsOfFn ? boundsOfFn(obj.id) : null;
@@ -17630,6 +17930,7 @@ function createScene(options) {
17630
17930
  return i;
17631
17931
  }
17632
17932
  function rebuildLayerIndex() {
17933
+ invalidateOrder();
17633
17934
  state.layerIndex.clear();
17634
17935
  for (let i = 0; i < state.layers.length; i++) {
17635
17936
  state.layerIndex.set(state.layers[i].id, i);
@@ -17655,6 +17956,7 @@ function createScene(options) {
17655
17956
  const idx = sibs.indexOf(id);
17656
17957
  if (idx < 0) throw new Error(`Scene: node "${id}" not found in its parent's children`);
17657
17958
  sibs.splice(idx, 1);
17959
+ invalidateOrder();
17658
17960
  return { parent: node.parent, index: idx };
17659
17961
  }
17660
17962
  function attach(id, parent, index) {
@@ -17663,6 +17965,7 @@ function createScene(options) {
17663
17965
  sibs.splice(i, 0, id);
17664
17966
  const node = requireNode(id);
17665
17967
  node.parent = parent;
17968
+ invalidateOrder();
17666
17969
  }
17667
17970
  function descendants(id, out) {
17668
17971
  const n = state.nodes.get(id);
@@ -17742,9 +18045,11 @@ function createScene(options) {
17742
18045
  apply: (p) => {
17743
18046
  requireLayerIndex(p.to);
17744
18047
  requireNode(p.id).layer = p.to;
18048
+ invalidateOrder();
17745
18049
  },
17746
18050
  revert: (p) => {
17747
18051
  requireNode(p.id).layer = p.from;
18052
+ invalidateOrder();
17748
18053
  }
17749
18054
  });
17750
18055
  registerKitOp("kit:move", {
@@ -17883,21 +18188,98 @@ function createScene(options) {
17883
18188
  registered.set(k, h);
17884
18189
  }
17885
18190
  }
17886
- function* renderOrderInternal() {
17887
- for (const layer of state.layers) {
17888
- const stack = [...state.roots].reverse();
17889
- while (stack.length > 0) {
17890
- const id = stack.pop();
17891
- const node = state.nodes.get(id);
17892
- if (!node) continue;
17893
- if (node.layer === layer.id) yield id;
17894
- if (node.kind === "container") {
17895
- for (let i = node.children.length - 1; i >= 0; i--) {
17896
- stack.push(node.children[i]);
17897
- }
18191
+ function renderOrderInternal() {
18192
+ return state.layers.length === 1 ? renderOrderFlat() : renderOrderBucketed();
18193
+ }
18194
+ function renderOrderNodesInternal() {
18195
+ return state.layers.length === 1 ? renderOrderNodesFlat() : renderOrderNodesBucketed();
18196
+ }
18197
+ let structureGeneration = 0;
18198
+ let orderCache = { gen: 0 };
18199
+ function invalidateOrder() {
18200
+ structureGeneration++;
18201
+ }
18202
+ function currentOrderCache() {
18203
+ if (orderCache.gen !== structureGeneration) {
18204
+ orderCache = { gen: structureGeneration };
18205
+ }
18206
+ return orderCache;
18207
+ }
18208
+ function renderOrderFlat() {
18209
+ const only = state.layers[0].id;
18210
+ const out = [];
18211
+ const stack = [...state.roots].reverse();
18212
+ while (stack.length > 0) {
18213
+ const id = stack.pop();
18214
+ const node = state.nodes.get(id);
18215
+ if (!node) continue;
18216
+ if (node.layer === only) out.push(id);
18217
+ if (node.kind === "container") {
18218
+ for (let i = node.children.length - 1; i >= 0; i--) {
18219
+ stack.push(node.children[i]);
17898
18220
  }
17899
18221
  }
17900
18222
  }
18223
+ return out;
18224
+ }
18225
+ function renderOrderNodesFlat() {
18226
+ const only = state.layers[0].id;
18227
+ const out = [];
18228
+ const stack = [...state.roots].reverse();
18229
+ while (stack.length > 0) {
18230
+ const id = stack.pop();
18231
+ const node = state.nodes.get(id);
18232
+ if (!node) continue;
18233
+ if (node.layer === only) out.push(node);
18234
+ if (node.kind === "container") {
18235
+ for (let i = node.children.length - 1; i >= 0; i--) {
18236
+ stack.push(node.children[i]);
18237
+ }
18238
+ }
18239
+ }
18240
+ return out;
18241
+ }
18242
+ function renderOrderBucketed() {
18243
+ const buckets = state.layers.map(() => []);
18244
+ const stack = [...state.roots].reverse();
18245
+ while (stack.length > 0) {
18246
+ const id = stack.pop();
18247
+ const node = state.nodes.get(id);
18248
+ if (!node) continue;
18249
+ const bucket = buckets[state.layerIndex.get(node.layer) ?? -1];
18250
+ if (bucket) bucket.push(id);
18251
+ if (node.kind === "container") {
18252
+ for (let i = node.children.length - 1; i >= 0; i--) {
18253
+ stack.push(node.children[i]);
18254
+ }
18255
+ }
18256
+ }
18257
+ const out = [];
18258
+ for (const bucket of buckets) {
18259
+ for (const id of bucket) out.push(id);
18260
+ }
18261
+ return out;
18262
+ }
18263
+ function renderOrderNodesBucketed() {
18264
+ const buckets = state.layers.map(() => []);
18265
+ const stack = [...state.roots].reverse();
18266
+ while (stack.length > 0) {
18267
+ const id = stack.pop();
18268
+ const node = state.nodes.get(id);
18269
+ if (!node) continue;
18270
+ const bucket = buckets[state.layerIndex.get(node.layer) ?? -1];
18271
+ if (bucket) bucket.push(node);
18272
+ if (node.kind === "container") {
18273
+ for (let i = node.children.length - 1; i >= 0; i--) {
18274
+ stack.push(node.children[i]);
18275
+ }
18276
+ }
18277
+ }
18278
+ const out = [];
18279
+ for (const bucket of buckets) {
18280
+ for (const node of bucket) out.push(node);
18281
+ }
18282
+ return out;
17901
18283
  }
17902
18284
  const scene = {
17903
18285
  get nodes() {
@@ -17926,7 +18308,12 @@ function createScene(options) {
17926
18308
  return out;
17927
18309
  },
17928
18310
  renderOrder() {
17929
- return renderOrderInternal();
18311
+ const c = currentOrderCache();
18312
+ return c.ids ??= renderOrderInternal();
18313
+ },
18314
+ renderOrderNodes() {
18315
+ const c = currentOrderCache();
18316
+ return c.nodes ??= renderOrderNodesInternal();
17930
18317
  },
17931
18318
  add(spec) {
17932
18319
  const id = spec.id ?? generateId();
@@ -18234,9 +18621,8 @@ function createScene(options) {
18234
18621
  },
18235
18622
  toJSON() {
18236
18623
  const nodes = [];
18237
- for (const id of renderOrderInternal()) {
18238
- const n = state.nodes.get(id);
18239
- if (!n) continue;
18624
+ for (const n of renderOrderNodesInternal()) {
18625
+ const id = n.id;
18240
18626
  const out = {
18241
18627
  id,
18242
18628
  kind: n.kind,
@@ -18270,6 +18656,7 @@ function createScene(options) {
18270
18656
  state.roots.length = 0;
18271
18657
  state.layers.length = 0;
18272
18658
  state.layerIndex.clear();
18659
+ invalidateOrder();
18273
18660
  for (let i = 0; i < json.systemLayers.length; i++) {
18274
18661
  const spec = json.systemLayers[i];
18275
18662
  if (state.layerIndex.has(spec.id)) {
@@ -18567,9 +18954,8 @@ function sceneToAdapter(scene, options = {}) {
18567
18954
  getNodes() {
18568
18955
  const visible = visibleLayers();
18569
18956
  const out = [];
18570
- for (const id of scene.renderOrder()) {
18571
- const n = scene.get(id);
18572
- if (n && visible.has(n.layer)) out.push(n);
18957
+ for (const n of scene.renderOrderNodes()) {
18958
+ if (visible.has(n.layer)) out.push(n);
18573
18959
  }
18574
18960
  return out;
18575
18961
  },
@@ -18909,9 +19295,7 @@ function useSceneSelectTool(args) {
18909
19295
  // path descriptor for a tighter test.
18910
19296
  hitTestArea: (rect) => {
18911
19297
  const hits = [];
18912
- for (const nid of scene.renderOrder()) {
18913
- const n = scene.get(nid);
18914
- if (!n) continue;
19298
+ for (const n of scene.renderOrderNodes()) {
18915
19299
  if (isPathLike(n.pose) && pathPoseDescriptor.intersectsRect) {
18916
19300
  if (pathPoseDescriptor.intersectsRect(n.pose, rect)) hits.push(n.id);
18917
19301
  continue;
@@ -18949,9 +19333,8 @@ function useSceneSelectTool(args) {
18949
19333
  }
18950
19334
  const tolerance = pickTolerancePx / meanScale(getView?.()?.scale ?? { x: 1, y: 1 });
18951
19335
  const out = [];
18952
- for (const id of scene.renderOrder()) {
18953
- const n = scene.get(id);
18954
- if (!n || !poseContainsRotated(n.pose, wx, wy, tolerance)) continue;
19336
+ for (const n of scene.renderOrderNodes()) {
19337
+ if (!poseContainsRotated(n.pose, wx, wy, tolerance)) continue;
18955
19338
  if (shapePicking && !shapeCoversPoint(n, n.pose, wx, wy, { tolerance })) continue;
18956
19339
  out.push(n.id);
18957
19340
  }
@@ -20095,23 +20478,25 @@ function hitTestAreaPolygon(scene, area, areaBounds) {
20095
20478
  const ab = areaBounds ?? boundsOf(area);
20096
20479
  if (!ab) return [];
20097
20480
  const hits = [];
20098
- for (const id of scene.renderOrder()) {
20099
- const node = scene.get(id);
20100
- if (!node || node.kind === "container") continue;
20481
+ const order = scene.renderOrderNodes();
20482
+ for (let i = 0; i < order.length; i++) {
20483
+ const node = order[i];
20484
+ if (node.kind === "container") continue;
20101
20485
  const pose = node.pose;
20102
- const b = aabbOfPose(pose);
20486
+ const silhouette = pose !== null && typeof pose === "object" && pose.kind === "polygon";
20487
+ const b = silhouette ? nodeMemo(node, "aabb", pose, () => aabbOfPose(pose)) : aabbOfPose(pose);
20103
20488
  if (!Number.isFinite(b.x) || !Number.isFinite(b.y) || !Number.isFinite(b.width) || !Number.isFinite(b.height)) {
20104
20489
  continue;
20105
20490
  }
20106
20491
  if (b.x >= ab.x + ab.width || b.x + b.width <= ab.x || b.y >= ab.y + ab.height || b.y + b.height <= ab.y) {
20107
20492
  continue;
20108
20493
  }
20109
- if (!isPathLike(pose) || pose.kind === "rect") {
20110
- hits.push(id);
20494
+ if (!silhouette) {
20495
+ hits.push(node.id);
20111
20496
  continue;
20112
20497
  }
20113
20498
  if (silhouetteOverlapsArea(pose.coords, area)) {
20114
- hits.push(id);
20499
+ hits.push(node.id);
20115
20500
  }
20116
20501
  }
20117
20502
  return hits;
@@ -22345,14 +22730,25 @@ var inferredNodeProperties = inferredNodeRouting.map((e) => ({
22345
22730
 
22346
22731
  // src/canvas/sceneViewRender.ts
22347
22732
  var RENDERER_CACHE = /* @__PURE__ */ new WeakMap();
22348
- function buildSceneViewCommands(scene, view, drawOne, extraCommands) {
22349
- const children = [];
22350
- for (const id of scene.renderOrder()) {
22351
- const node = scene.get(id);
22352
- if (!node) continue;
22353
- const cmds = drawOne(node, node.pose, view);
22354
- for (const cmd of cmds) children.push(cmd);
22355
- }
22733
+ function sceneAsHierarchy(scene) {
22734
+ return {
22735
+ getLayers: () => scene.layers.map((l) => ({ id: l.id, visible: l.visible })),
22736
+ getNode: (id) => scene.get(id),
22737
+ getChildren: (parentId) => {
22738
+ if (parentId === null) return scene.roots;
22739
+ const node = scene.get(parentId);
22740
+ return node && node.kind === "container" ? node.children : [];
22741
+ },
22742
+ getPose: (id) => scene.get(id).pose
22743
+ };
22744
+ }
22745
+ function buildSceneViewCommands(scene, view, drawOne, extraCommands, alphaFor) {
22746
+ const wrappedDrawOne = (node, pose, v) => wrapNodeOutput(drawOne(node, pose, v), pose, alphaFor ? alphaFor(node.id) : 1);
22747
+ const children = buildSceneTree(
22748
+ sceneAsHierarchy(scene),
22749
+ wrappedDrawOne,
22750
+ view
22751
+ );
22356
22752
  if (extraCommands && extraCommands.length > 0) {
22357
22753
  for (const cmd of extraCommands) children.push(cmd);
22358
22754
  }
@@ -22363,7 +22759,7 @@ function buildSceneViewCommands(scene, view, drawOne, extraCommands) {
22363
22759
  }];
22364
22760
  }
22365
22761
  function renderSceneToCanvas(args) {
22366
- const { canvas, scene, view, width, height, drawOne, extraCommands } = args;
22762
+ const { canvas, scene, view, width, height, drawOne, extraCommands, alphaFor } = args;
22367
22763
  const dpr = args.dpr ?? (typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1);
22368
22764
  let entry = RENDERER_CACHE.get(canvas);
22369
22765
  if (!entry) {
@@ -22391,11 +22787,11 @@ function renderSceneToCanvas(args) {
22391
22787
  entry.height = height;
22392
22788
  entry.dpr = dpr;
22393
22789
  }
22394
- const commands = buildSceneViewCommands(scene, view, drawOne, extraCommands);
22790
+ const commands = buildSceneViewCommands(scene, view, drawOne, extraCommands, alphaFor);
22395
22791
  entry.renderer.render(commands, viewToMat3(view));
22396
22792
  }
22397
22793
  function SceneViewCanvasInner(props) {
22398
- const { scene, view, width, height, drawOne, extraCommands, className, canvasRef } = props;
22794
+ const { scene, view, width, height, drawOne, extraCommands, alphaFor, className, canvasRef } = props;
22399
22795
  useSyncExternalStore(scene.subscribe, scene.getVersion, scene.getVersion);
22400
22796
  const localRef = useRef(null);
22401
22797
  const setCanvasRef = (el) => {
@@ -22416,7 +22812,8 @@ function SceneViewCanvasInner(props) {
22416
22812
  width,
22417
22813
  height,
22418
22814
  drawOne,
22419
- extraCommands
22815
+ extraCommands,
22816
+ alphaFor
22420
22817
  });
22421
22818
  });
22422
22819
  return /* @__PURE__ */ jsx(
@@ -22515,6 +22912,7 @@ function MinimapCanvasInner(props) {
22515
22912
  width,
22516
22913
  height,
22517
22914
  drawOne,
22915
+ alphaFor,
22518
22916
  fit = "scene",
22519
22917
  poseBounds,
22520
22918
  indicatorStyle,
@@ -22626,6 +23024,7 @@ function MinimapCanvasInner(props) {
22626
23024
  height,
22627
23025
  drawOne,
22628
23026
  extraCommands,
23027
+ alphaFor,
22629
23028
  className,
22630
23029
  canvasRef: setCanvasRef
22631
23030
  }
@@ -22667,7 +23066,7 @@ function planPixelRender(args) {
22667
23066
  fill: { fill: "solid", color: args.background }
22668
23067
  });
22669
23068
  }
22670
- commands.push(...buildSceneViewCommands(args.scene, view, drawOne));
23069
+ commands.push(...buildSceneViewCommands(args.scene, view, drawOne, void 0, args.alphaFor));
22671
23070
  return { width, height, view, commands };
22672
23071
  }
22673
23072
  function defaultCreateCanvas(width, height) {
@@ -23019,15 +23418,11 @@ function measureText(ctx, text, maxWidth, style) {
23019
23418
  function measureTextBounds(text, style, opts) {
23020
23419
  const resolved = resolveTextStyle(style);
23021
23420
  const runs = resolveRuns([{ text }], resolved);
23022
- const { bounds } = layoutRuns(
23023
- runs,
23024
- {
23025
- maxWidth: opts?.maxWidth ?? Infinity,
23026
- lineHeight: opts?.lineHeight ?? resolved.lineHeight,
23027
- align: resolved.align
23028
- },
23029
- { x: 0, y: 0 }
23030
- );
23421
+ const { bounds } = layoutRuns(runs, {
23422
+ maxWidth: opts?.maxWidth ?? Infinity,
23423
+ lineHeight: opts?.lineHeight ?? resolved.lineHeight,
23424
+ align: resolved.align
23425
+ });
23031
23426
  return bounds;
23032
23427
  }
23033
23428
 
@@ -23940,10 +24335,9 @@ function useSceneTextEdit(scene, container, options = {}) {
23940
24335
  const cy = view ? canvasY / view.scale.y + view.y : canvasY;
23941
24336
  const readText = (data) => optsRef.current.getText ? optsRef.current.getText(data) : data.text ?? "";
23942
24337
  const readStyle = (data) => optsRef.current.getStyle ? optsRef.current.getStyle(data) : data.style;
23943
- const order = [...sceneRef.current.renderOrder()];
24338
+ const order = sceneRef.current.renderOrderNodes();
23944
24339
  for (let i = order.length - 1; i >= 0; i--) {
23945
- const node = sceneRef.current.get(order[i]);
23946
- if (!node) continue;
24340
+ const node = order[i];
23947
24341
  const text = readText(node.data);
23948
24342
  const pose = {
23949
24343
  x: node.pose.x,
@@ -27639,5 +28033,5 @@ function mergeContributions(...bundles) {
27639
28033
  }
27640
28034
 
27641
28035
  export { ALWAYS, ANCHOR_HIT_BASE_PX, ActionsProviderIfRoot, ActiveToolContextProvider, ActiveToolContextProviderIfRoot, BUNDLE_TOOLS, COARSE_TARGET_SCALE, CURVE_REPS, ColorOverrideRegistry, CropIcon, CursorCoordsHud, DEFAULT_ALPHA_DECAY, DEFAULT_ALPHA_MIN, DEFAULT_DEBUG_THEME, DEFAULT_DEVICE_PROFILE, DEFAULT_FILL_COLOR, DEFAULT_HANDLE_SIZE2 as DEFAULT_HANDLE_SIZE, DEFAULT_PALETTE, DEFAULT_ROTATION_HANDLE_DISTANCE, DEFAULT_STROKE_COLOR, DEFAULT_TEXT_STYLE, DEFAULT_VELOCITY_DECAY, DepRegistryProviderIfRoot, DeviceProfileProvider, DivideIcon, EASINGS, EllipseIcon, ExcludeIcon, EyedropperIcon, FALLBACK_FIT_VIEW, GHOST_STROKE, HANDLE_BASE_PX, HandIcon, IDENTITY_COLOR_MATRIX, IDENTITY_POSE_COMPOSITION, IntersectIcon, KIT_SHAPE_KINDS, LassoIcon, LineIcon, MIXED, MOVE_ANCHORS, MinimapCanvas, NEVER, OUTLINE_MIN_SCREEN_PX, 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, TextIcon, UnionIcon, UnknownIcon, VERSION, WeaselProvider, WeaselRenderer, aabbCenter, actionIs, alignDeltaFor, alignInsertBehavior, alignMoveBehavior, alignResizeBehavior, alignedStrokeRect, always, and, animateLifecycle, animateOnSetPose, annulusSemiAxes, applyBooleanOp, applyHitExistingGate, applyStyleToRange, areaSelectAction, arrayAdapter, asNodeId, bezierCubic, bezierQuadratic, 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, computeFitView2 as computeFitView, computeIndicatorCommand, computeWheelAction, cond, constrainTo45, containedThenNearest, countPathAnchors, createCellHighlightLayer, createChildrenLayer, createCornerResizeAffordance, createDebugOverlayLayer, createDebugSink, createDispatcher, createGridLayer, createGuidesLayer, createHistory2 as createHistory, createMarkdownRenderer, createMoveToIndexOp, createNodeProperties, createNodeRouting, createParallaxLayer, createPathAnchorAffordances, createPathEditingOverlayLayer, createPathLayer, createPenPreviewLayer, createReorderOp, createRotationAffordance, createScene, createSelectionHandlesLayer, createSelectionOutlineLayer, createSelectionOverlayLayer, createSetDataOp, createSetLayerOp, createSetPathOp, createSetTextOp, createTextLayer, createViewportLayer, cycleVertexColors, decomposePath, decomposeRectPose, defaultCommitAdapter, defaultDrawOne, defaultLabelTextRenderer, defaultNodeProperties, defaultNodeRouting, defaultVisibilityRules, deriveAlignmentGuides, deriveParallaxView, describeRule, domPositionToCharOffset, domToRuns, drawLayers, 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, easeOutCubic2 as easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, editAnchorsAction, enterTextEditAction, enumerateAnchors, evaluate, fillInPoseFrame, fillToBoundsFrame, findNodeShape, findShapeInk, findShapeSilhouette, fitTextPose, fitToBounds, fitViewToBounds, fitZoom, focused, fontString, forEachCoalesced, freeform, gateLayer, gesturing, getAlpha01, getImageBitmap, getNodeShapes, getStylusData, gradientForBounds, gradientGeometry, hexToRgba, hitAffordanceRegions, hitAnchor, hitRotationHandle, hovering, hoveringSelected, imageStatus, inferredNodeProperties, inferredNodeRouting, insertAction, isEditableTarget2 as isEditableTarget, isPatternSpec, lassoSelectAction, layoutMarkdown, lerpColorArray, lerpOklab, lerpOklch, linear, liveScope, makeViewportZoomAction, markdownToRuns, mat3, matchAlignment, matchesKeyBinding, meanScale, measureText, measureTextBounds, measuredWidth, mergeAlphaFromPrev, mergeContributions, modeIn, modeIs, modeNot, modifierHeld, momentum, moveAction, multiActive, nearest, nearestWithin, nestedHitTester, never, nodeHasFlag, none, normalizeHex, not, nurbs, oklabToOklch, oklabToSrgbU8, oklchToOklab, openFilePicker, or, 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, registerNodeShape, registerProgram, renderLabel, renderSceneToCanvas, renderSceneToPixels, resizeAction, resolveColor, resolveDeviceProfile, resolveFillPattern, resolvePatternSpec, resolveRuns, resolveTextStyle, resolveVisibility, rgbaToHex, rotateAction, rotateAroundAABBCenter, rotatePathAround, rotatePoint, rotatedRectCorners, rotationDegreesUnit, rotationHandle, roundToCell, runsToDom, runsToMarkdown, runsToPlainText, sampleGradientStops, sceneFromJSON, sceneNodeClientRect, sceneToAdapter, scopeBindings, screenToWorld, selectFromLasso, selectionAtLeast, selectionEmpty, selectionIs, setFlagOverRange, shapeCoversPoint, sliceAction, snapPoint, solidVertexColors, specificity, spiro, splitPathByLine, splitSubpaths, springPose, springVertexColors, srgbU8ToOklab, staggerVertexColors, startThresholdDrag, styleAtRange, subscribeImageReady, tessellate, tessellateStroke, textCommand, textLineBoxes, tileGrid, toHex8, toRuns, translatePoseViaDescriptor, translateRectPose, tweenPose, tweenVertexColors, 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, useHoverTracking, useImageTool, useKeybindings, useLassoTool, useLineTool, 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, useViewTween, useZoom, verticalAlignOffset, viewToMat3, viewToTransform, viewportDragPanAction, viewportZoomAction, when, withAlpha01, withCoord, withGradientKind, worldEditToStorage, worldPoseLookup, worldToScreen, zoomAt, zoomAtLeast };
27642
- //# sourceMappingURL=chunk-D2C6K6FO.js.map
27643
- //# sourceMappingURL=chunk-D2C6K6FO.js.map
28036
+ //# sourceMappingURL=chunk-FVNWK7U3.js.map
28037
+ //# sourceMappingURL=chunk-FVNWK7U3.js.map