@vectojs/core 1.16.1 → 1.16.3

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.
@@ -827,8 +827,6 @@ function fromCanvas(css) {
827
827
  function parseColorToRGBA(css) {
828
828
  const hit = cache.get(css);
829
829
  if (hit) {
830
- cache.delete(css);
831
- cache.set(css, hit);
832
830
  return hit;
833
831
  }
834
832
  const trimmed = css.trim();
@@ -843,12 +841,26 @@ function parseColorToRGBA(css) {
843
841
  // src/renderer/WebGLPointRenderer.ts
844
842
  var FLOATS_PER_POINT = 7;
845
843
  var POINT_STRIDE = FLOATS_PER_POINT * 4;
844
+ var VERTS_PER_QUAD = 4;
845
+ var INDICES_PER_QUAD = 6;
846
846
  var FLOATS_PER_RECT_VERT = 6;
847
847
  var RECT_VERT_STRIDE = FLOATS_PER_RECT_VERT * 4;
848
- var VERTS_PER_RECT = 6;
849
848
  var FLOATS_PER_SPRITE_VERT = 8;
850
849
  var SPRITE_VERT_STRIDE = FLOATS_PER_SPRITE_VERT * 4;
851
- var VERTS_PER_SPRITE = 6;
850
+ function buildQuadIndices(quads) {
851
+ const out = new Uint32Array(quads * INDICES_PER_QUAD);
852
+ for (let i = 0; i < quads; i++) {
853
+ const v = i * VERTS_PER_QUAD;
854
+ const o = i * INDICES_PER_QUAD;
855
+ out[o] = v;
856
+ out[o + 1] = v + 1;
857
+ out[o + 2] = v + 2;
858
+ out[o + 3] = v;
859
+ out[o + 4] = v + 2;
860
+ out[o + 5] = v + 3;
861
+ }
862
+ return out;
863
+ }
852
864
  var POINT_VERT = `#version 300 es
853
865
  in vec2 a_pos;
854
866
  in float a_radius;
@@ -976,6 +988,120 @@ function link(gl, vsSrc, fsSrc) {
976
988
  }
977
989
  return program;
978
990
  }
991
+ function writeQuad(out, o, x, y, width, height, u0, v0, u1, v1, r, g, b, al, rotation) {
992
+ let x1;
993
+ let y1;
994
+ let x2;
995
+ let y2;
996
+ let x3;
997
+ let y3;
998
+ if (rotation === 0) {
999
+ x1 = x + width;
1000
+ y1 = y;
1001
+ x2 = x + width;
1002
+ y2 = y + height;
1003
+ x3 = x;
1004
+ y3 = y + height;
1005
+ } else {
1006
+ const s = Math.sin(rotation);
1007
+ const c = Math.cos(rotation);
1008
+ const wc = width * c;
1009
+ const ws = width * s;
1010
+ const hc = height * c;
1011
+ const hs = height * s;
1012
+ x1 = x + wc;
1013
+ y1 = y + ws;
1014
+ x2 = x + wc - hs;
1015
+ y2 = y + ws + hc;
1016
+ x3 = x - hs;
1017
+ y3 = y + hc;
1018
+ }
1019
+ out[o] = x;
1020
+ out[o + 1] = y;
1021
+ out[o + 2] = u0;
1022
+ out[o + 3] = v0;
1023
+ out[o + 4] = r;
1024
+ out[o + 5] = g;
1025
+ out[o + 6] = b;
1026
+ out[o + 7] = al;
1027
+ out[o + 8] = x1;
1028
+ out[o + 9] = y1;
1029
+ out[o + 10] = u1;
1030
+ out[o + 11] = v0;
1031
+ out[o + 12] = r;
1032
+ out[o + 13] = g;
1033
+ out[o + 14] = b;
1034
+ out[o + 15] = al;
1035
+ out[o + 16] = x2;
1036
+ out[o + 17] = y2;
1037
+ out[o + 18] = u1;
1038
+ out[o + 19] = v1;
1039
+ out[o + 20] = r;
1040
+ out[o + 21] = g;
1041
+ out[o + 22] = b;
1042
+ out[o + 23] = al;
1043
+ out[o + 24] = x3;
1044
+ out[o + 25] = y3;
1045
+ out[o + 26] = u0;
1046
+ out[o + 27] = v1;
1047
+ out[o + 28] = r;
1048
+ out[o + 29] = g;
1049
+ out[o + 30] = b;
1050
+ out[o + 31] = al;
1051
+ }
1052
+ function writeRectQuad(out, o, x, y, width, height, r, g, b, al, rotation) {
1053
+ let x1;
1054
+ let y1;
1055
+ let x2;
1056
+ let y2;
1057
+ let x3;
1058
+ let y3;
1059
+ if (rotation === 0) {
1060
+ x1 = x + width;
1061
+ y1 = y;
1062
+ x2 = x + width;
1063
+ y2 = y + height;
1064
+ x3 = x;
1065
+ y3 = y + height;
1066
+ } else {
1067
+ const s = Math.sin(rotation);
1068
+ const c = Math.cos(rotation);
1069
+ const wc = width * c;
1070
+ const ws = width * s;
1071
+ const hc = height * c;
1072
+ const hs = height * s;
1073
+ x1 = x + wc;
1074
+ y1 = y + ws;
1075
+ x2 = x + wc - hs;
1076
+ y2 = y + ws + hc;
1077
+ x3 = x - hs;
1078
+ y3 = y + hc;
1079
+ }
1080
+ out[o] = x;
1081
+ out[o + 1] = y;
1082
+ out[o + 2] = r;
1083
+ out[o + 3] = g;
1084
+ out[o + 4] = b;
1085
+ out[o + 5] = al;
1086
+ out[o + 6] = x1;
1087
+ out[o + 7] = y1;
1088
+ out[o + 8] = r;
1089
+ out[o + 9] = g;
1090
+ out[o + 10] = b;
1091
+ out[o + 11] = al;
1092
+ out[o + 12] = x2;
1093
+ out[o + 13] = y2;
1094
+ out[o + 14] = r;
1095
+ out[o + 15] = g;
1096
+ out[o + 16] = b;
1097
+ out[o + 17] = al;
1098
+ out[o + 18] = x3;
1099
+ out[o + 19] = y3;
1100
+ out[o + 20] = r;
1101
+ out[o + 21] = g;
1102
+ out[o + 22] = b;
1103
+ out[o + 23] = al;
1104
+ }
979
1105
  function grow(data, needed) {
980
1106
  if (needed <= data.length) return data;
981
1107
  let cap = data.length;
@@ -1070,6 +1196,22 @@ function createWebGLPointRenderer(canvas) {
1070
1196
  gl.enableVertexAttribArray(cATint);
1071
1197
  gl.vertexAttribPointer(cATint, 4, gl.FLOAT, false, SPRITE_VERT_STRIDE, 16);
1072
1198
  gl.bindVertexArray(null);
1199
+ const quadIndexBuffer = gl.createBuffer();
1200
+ let quadIndexCapacity = 0;
1201
+ const ensureQuadIndices = (quads) => {
1202
+ if (quads <= quadIndexCapacity) return;
1203
+ let cap = quadIndexCapacity || 256;
1204
+ while (cap < quads) cap *= 2;
1205
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, quadIndexBuffer);
1206
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, buildQuadIndices(cap), gl.STATIC_DRAW);
1207
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
1208
+ quadIndexCapacity = cap;
1209
+ };
1210
+ for (const vao of [rectVAO, spriteVAO, glyphVAO, circleQuadVAO]) {
1211
+ gl.bindVertexArray(vao);
1212
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, quadIndexBuffer);
1213
+ gl.bindVertexArray(null);
1214
+ }
1073
1215
  let texture = null;
1074
1216
  let textureSource = null;
1075
1217
  let msdfTexture = null;
@@ -1077,15 +1219,13 @@ function createWebGLPointRenderer(canvas) {
1077
1219
  let distanceRange = 4;
1078
1220
  let pointData = new Float32Array(FLOATS_PER_POINT * 1024);
1079
1221
  let pointCount = 0;
1080
- let rectData = new Float32Array(FLOATS_PER_RECT_VERT * VERTS_PER_RECT * 256);
1222
+ let rectData = new Float32Array(FLOATS_PER_RECT_VERT * VERTS_PER_QUAD * 256);
1081
1223
  let rectCount = 0;
1082
- let spriteData = new Float32Array(FLOATS_PER_SPRITE_VERT * VERTS_PER_SPRITE * 256);
1224
+ let spriteData = new Float32Array(FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD * 256);
1083
1225
  let spriteCount = 0;
1084
- let glyphData = new Float32Array(FLOATS_PER_SPRITE_VERT * VERTS_PER_SPRITE * 1024);
1226
+ let glyphData = new Float32Array(FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD * 1024);
1085
1227
  let glyphCount = 0;
1086
- let circleQuadData = new Float32Array(
1087
- FLOATS_PER_SPRITE_VERT * VERTS_PER_SPRITE * 64
1088
- );
1228
+ let circleQuadData = new Float32Array(FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD * 64);
1089
1229
  let circleQuadCount = 0;
1090
1230
  let logicalW = 0;
1091
1231
  let logicalH = 0;
@@ -1095,7 +1235,7 @@ function createWebGLPointRenderer(canvas) {
1095
1235
  const maxPointSize = pointSizeRange ? pointSizeRange[1] : Infinity;
1096
1236
  const drawGlyphs = () => {
1097
1237
  if (glyphCount === 0 || !msdfTexture) return;
1098
- const floats = glyphCount * VERTS_PER_SPRITE * FLOATS_PER_SPRITE_VERT;
1238
+ const floats = glyphCount * VERTS_PER_QUAD * FLOATS_PER_SPRITE_VERT;
1099
1239
  gl.useProgram(msdfProgram);
1100
1240
  gl.bindVertexArray(glyphVAO);
1101
1241
  gl.bindBuffer(gl.ARRAY_BUFFER, glyphBuffer);
@@ -1105,7 +1245,8 @@ function createWebGLPointRenderer(canvas) {
1105
1245
  gl.uniform1i(gUTex, 0);
1106
1246
  gl.uniform2f(gURes, logicalW, logicalH);
1107
1247
  gl.uniform1f(gURange, distanceRange);
1108
- gl.drawArrays(gl.TRIANGLES, 0, glyphCount * VERTS_PER_SPRITE);
1248
+ ensureQuadIndices(glyphCount);
1249
+ gl.drawElements(gl.TRIANGLES, glyphCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
1109
1250
  gl.bindVertexArray(null);
1110
1251
  glyphCount = 0;
1111
1252
  };
@@ -1148,36 +1289,26 @@ function createWebGLPointRenderer(canvas) {
1148
1289
  },
1149
1290
  addSprite(x, y, width, height, u0, v0, u1, v1, color = "#ffffff", alpha = 1, rotation = 0) {
1150
1291
  if (!texture) return;
1151
- const stride = FLOATS_PER_SPRITE_VERT * VERTS_PER_SPRITE;
1292
+ const stride = FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD;
1152
1293
  spriteData = grow(spriteData, (spriteCount + 1) * stride);
1153
- const [r, g, b, a] = parseColorToRGBA(color);
1154
- const al = a * alpha;
1155
- const s = Math.sin(rotation);
1156
- const c = Math.cos(rotation);
1157
- const corner = (lx, ly) => [
1158
- x + lx * c - ly * s,
1159
- y + lx * s + ly * c
1160
- ];
1161
- const quad = [
1162
- [corner(0, 0), [u0, v0]],
1163
- [corner(width, 0), [u1, v0]],
1164
- [corner(width, height), [u1, v1]],
1165
- [corner(0, height), [u0, v1]]
1166
- ];
1167
- const order = [0, 1, 2, 0, 2, 3];
1168
- let o = spriteCount * stride;
1169
- for (const i of order) {
1170
- const [[vx, vy], [vu, vv]] = quad[i];
1171
- spriteData[o] = vx;
1172
- spriteData[o + 1] = vy;
1173
- spriteData[o + 2] = vu;
1174
- spriteData[o + 3] = vv;
1175
- spriteData[o + 4] = r;
1176
- spriteData[o + 5] = g;
1177
- spriteData[o + 6] = b;
1178
- spriteData[o + 7] = al;
1179
- o += FLOATS_PER_SPRITE_VERT;
1180
- }
1294
+ const rgba = parseColorToRGBA(color);
1295
+ writeQuad(
1296
+ spriteData,
1297
+ spriteCount * stride,
1298
+ x,
1299
+ y,
1300
+ width,
1301
+ height,
1302
+ u0,
1303
+ v0,
1304
+ u1,
1305
+ v1,
1306
+ rgba[0],
1307
+ rgba[1],
1308
+ rgba[2],
1309
+ rgba[3] * alpha,
1310
+ rotation
1311
+ );
1181
1312
  spriteCount++;
1182
1313
  },
1183
1314
  setMSDFTexture(source, range) {
@@ -1202,65 +1333,52 @@ function createWebGLPointRenderer(canvas) {
1202
1333
  },
1203
1334
  addGlyph(x, y, width, height, u0, v0, u1, v1, color = "#ffffff", alpha = 1, rotation = 0) {
1204
1335
  if (!msdfTexture) return;
1205
- const stride = FLOATS_PER_SPRITE_VERT * VERTS_PER_SPRITE;
1336
+ const stride = FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD;
1206
1337
  glyphData = grow(glyphData, (glyphCount + 1) * stride);
1207
- const [r, g, b, a] = parseColorToRGBA(color);
1208
- const al = a * alpha;
1209
- const s = Math.sin(rotation);
1210
- const c = Math.cos(rotation);
1211
- const corner = (lx, ly) => [
1212
- x + lx * c - ly * s,
1213
- y + lx * s + ly * c
1214
- ];
1215
- const quad = [
1216
- [corner(0, 0), [u0, v0]],
1217
- [corner(width, 0), [u1, v0]],
1218
- [corner(width, height), [u1, v1]],
1219
- [corner(0, height), [u0, v1]]
1220
- ];
1221
- const order = [0, 1, 2, 0, 2, 3];
1222
- let o = glyphCount * stride;
1223
- for (const i of order) {
1224
- const [[vx, vy], [vu, vv]] = quad[i];
1225
- glyphData[o] = vx;
1226
- glyphData[o + 1] = vy;
1227
- glyphData[o + 2] = vu;
1228
- glyphData[o + 3] = vv;
1229
- glyphData[o + 4] = r;
1230
- glyphData[o + 5] = g;
1231
- glyphData[o + 6] = b;
1232
- glyphData[o + 7] = al;
1233
- o += FLOATS_PER_SPRITE_VERT;
1234
- }
1338
+ const rgba = parseColorToRGBA(color);
1339
+ writeQuad(
1340
+ glyphData,
1341
+ glyphCount * stride,
1342
+ x,
1343
+ y,
1344
+ width,
1345
+ height,
1346
+ u0,
1347
+ v0,
1348
+ u1,
1349
+ v1,
1350
+ rgba[0],
1351
+ rgba[1],
1352
+ rgba[2],
1353
+ rgba[3] * alpha,
1354
+ rotation
1355
+ );
1235
1356
  glyphCount++;
1236
1357
  },
1237
1358
  addCircle(x, y, radius, color, alpha = 1) {
1238
1359
  const needsQuad = logicalW > 0 && (x < radius || y < radius || x > logicalW - radius || y > logicalH - radius) || radius * 2 * dpr > maxPointSize;
1239
1360
  if (needsQuad) {
1240
- const stride = FLOATS_PER_SPRITE_VERT * VERTS_PER_SPRITE;
1361
+ const stride = FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD;
1241
1362
  circleQuadData = grow(circleQuadData, (circleQuadCount + 1) * stride);
1242
- const [qr, qg, qb, qa] = parseColorToRGBA(color);
1243
- const al = qa * alpha;
1244
- const quad = [
1245
- [x - radius, y - radius, 0, 0],
1246
- [x + radius, y - radius, 1, 0],
1247
- [x + radius, y + radius, 1, 1],
1248
- [x - radius, y + radius, 0, 1]
1249
- ];
1250
- const order = [0, 1, 2, 0, 2, 3];
1251
- let o2 = circleQuadCount * stride;
1252
- for (const i of order) {
1253
- const [vx, vy, vu, vv] = quad[i];
1254
- circleQuadData[o2] = vx;
1255
- circleQuadData[o2 + 1] = vy;
1256
- circleQuadData[o2 + 2] = vu;
1257
- circleQuadData[o2 + 3] = vv;
1258
- circleQuadData[o2 + 4] = qr;
1259
- circleQuadData[o2 + 5] = qg;
1260
- circleQuadData[o2 + 6] = qb;
1261
- circleQuadData[o2 + 7] = al;
1262
- o2 += FLOATS_PER_SPRITE_VERT;
1263
- }
1363
+ const rgba = parseColorToRGBA(color);
1364
+ const d = radius * 2;
1365
+ writeQuad(
1366
+ circleQuadData,
1367
+ circleQuadCount * stride,
1368
+ x - radius,
1369
+ y - radius,
1370
+ d,
1371
+ d,
1372
+ 0,
1373
+ 0,
1374
+ 1,
1375
+ 1,
1376
+ rgba[0],
1377
+ rgba[1],
1378
+ rgba[2],
1379
+ rgba[3] * alpha,
1380
+ 0
1381
+ );
1264
1382
  circleQuadCount++;
1265
1383
  return;
1266
1384
  }
@@ -1277,42 +1395,34 @@ function createWebGLPointRenderer(canvas) {
1277
1395
  pointCount++;
1278
1396
  },
1279
1397
  addRect(x, y, width, height, color, alpha = 1, rotation = 0) {
1280
- const stride = FLOATS_PER_RECT_VERT * VERTS_PER_RECT;
1398
+ const stride = FLOATS_PER_RECT_VERT * VERTS_PER_QUAD;
1281
1399
  rectData = grow(rectData, (rectCount + 1) * stride);
1282
- const [r, g, b, a] = parseColorToRGBA(color);
1283
- const al = a * alpha;
1284
- const s = Math.sin(rotation);
1285
- const c = Math.cos(rotation);
1286
- const corner = (lx, ly) => [
1287
- x + lx * c - ly * s,
1288
- y + lx * s + ly * c
1289
- ];
1290
- const p0 = corner(0, 0);
1291
- const p1 = corner(width, 0);
1292
- const p2 = corner(width, height);
1293
- const p3 = corner(0, height);
1294
- const verts = [p0, p1, p2, p0, p2, p3];
1295
- let o = rectCount * stride;
1296
- for (const [vx, vy] of verts) {
1297
- rectData[o] = vx;
1298
- rectData[o + 1] = vy;
1299
- rectData[o + 2] = r;
1300
- rectData[o + 3] = g;
1301
- rectData[o + 4] = b;
1302
- rectData[o + 5] = al;
1303
- o += FLOATS_PER_RECT_VERT;
1304
- }
1400
+ const rgba = parseColorToRGBA(color);
1401
+ writeRectQuad(
1402
+ rectData,
1403
+ rectCount * stride,
1404
+ x,
1405
+ y,
1406
+ width,
1407
+ height,
1408
+ rgba[0],
1409
+ rgba[1],
1410
+ rgba[2],
1411
+ rgba[3] * alpha,
1412
+ rotation
1413
+ );
1305
1414
  rectCount++;
1306
1415
  },
1307
1416
  flush() {
1308
1417
  if (rectCount > 0) {
1309
- const floats = rectCount * VERTS_PER_RECT * FLOATS_PER_RECT_VERT;
1418
+ const floats = rectCount * VERTS_PER_QUAD * FLOATS_PER_RECT_VERT;
1310
1419
  gl.useProgram(rectProgram);
1311
1420
  gl.bindVertexArray(rectVAO);
1312
1421
  gl.bindBuffer(gl.ARRAY_BUFFER, rectBuffer);
1313
1422
  gl.bufferData(gl.ARRAY_BUFFER, rectData.subarray(0, floats), gl.DYNAMIC_DRAW);
1314
1423
  gl.uniform2f(rURes, logicalW, logicalH);
1315
- gl.drawArrays(gl.TRIANGLES, 0, rectCount * VERTS_PER_RECT);
1424
+ ensureQuadIndices(rectCount);
1425
+ gl.drawElements(gl.TRIANGLES, rectCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
1316
1426
  }
1317
1427
  if (pointCount > 0) {
1318
1428
  gl.useProgram(pointProgram);
@@ -1328,16 +1438,17 @@ function createWebGLPointRenderer(canvas) {
1328
1438
  gl.drawArrays(gl.POINTS, 0, pointCount);
1329
1439
  }
1330
1440
  if (circleQuadCount > 0) {
1331
- const floats = circleQuadCount * VERTS_PER_SPRITE * FLOATS_PER_SPRITE_VERT;
1441
+ const floats = circleQuadCount * VERTS_PER_QUAD * FLOATS_PER_SPRITE_VERT;
1332
1442
  gl.useProgram(circleQuadProgram);
1333
1443
  gl.bindVertexArray(circleQuadVAO);
1334
1444
  gl.bindBuffer(gl.ARRAY_BUFFER, circleQuadBuffer);
1335
1445
  gl.bufferData(gl.ARRAY_BUFFER, circleQuadData.subarray(0, floats), gl.DYNAMIC_DRAW);
1336
1446
  gl.uniform2f(cURes, logicalW, logicalH);
1337
- gl.drawArrays(gl.TRIANGLES, 0, circleQuadCount * VERTS_PER_SPRITE);
1447
+ ensureQuadIndices(circleQuadCount);
1448
+ gl.drawElements(gl.TRIANGLES, circleQuadCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
1338
1449
  }
1339
1450
  if (spriteCount > 0 && texture) {
1340
- const floats = spriteCount * VERTS_PER_SPRITE * FLOATS_PER_SPRITE_VERT;
1451
+ const floats = spriteCount * VERTS_PER_QUAD * FLOATS_PER_SPRITE_VERT;
1341
1452
  gl.useProgram(spriteProgram);
1342
1453
  gl.bindVertexArray(spriteVAO);
1343
1454
  gl.bindBuffer(gl.ARRAY_BUFFER, spriteBuffer);
@@ -1346,7 +1457,8 @@ function createWebGLPointRenderer(canvas) {
1346
1457
  gl.bindTexture(gl.TEXTURE_2D, texture);
1347
1458
  gl.uniform1i(sUTex, 0);
1348
1459
  gl.uniform2f(sURes, logicalW, logicalH);
1349
- gl.drawArrays(gl.TRIANGLES, 0, spriteCount * VERTS_PER_SPRITE);
1460
+ ensureQuadIndices(spriteCount);
1461
+ gl.drawElements(gl.TRIANGLES, spriteCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
1350
1462
  }
1351
1463
  gl.bindVertexArray(null);
1352
1464
  drawGlyphs();
@@ -1359,6 +1471,7 @@ function createWebGLPointRenderer(canvas) {
1359
1471
  gl.deleteBuffer(spriteBuffer);
1360
1472
  gl.deleteBuffer(glyphBuffer);
1361
1473
  gl.deleteBuffer(circleQuadBuffer);
1474
+ gl.deleteBuffer(quadIndexBuffer);
1362
1475
  gl.deleteVertexArray(pointVAO);
1363
1476
  gl.deleteVertexArray(rectVAO);
1364
1477
  gl.deleteVertexArray(spriteVAO);
@@ -825,8 +825,6 @@ function fromCanvas(css) {
825
825
  function parseColorToRGBA(css) {
826
826
  const hit = cache.get(css);
827
827
  if (hit) {
828
- cache.delete(css);
829
- cache.set(css, hit);
830
828
  return hit;
831
829
  }
832
830
  const trimmed = css.trim();
@@ -841,12 +839,26 @@ function parseColorToRGBA(css) {
841
839
  // src/renderer/WebGLPointRenderer.ts
842
840
  var FLOATS_PER_POINT = 7;
843
841
  var POINT_STRIDE = FLOATS_PER_POINT * 4;
842
+ var VERTS_PER_QUAD = 4;
843
+ var INDICES_PER_QUAD = 6;
844
844
  var FLOATS_PER_RECT_VERT = 6;
845
845
  var RECT_VERT_STRIDE = FLOATS_PER_RECT_VERT * 4;
846
- var VERTS_PER_RECT = 6;
847
846
  var FLOATS_PER_SPRITE_VERT = 8;
848
847
  var SPRITE_VERT_STRIDE = FLOATS_PER_SPRITE_VERT * 4;
849
- var VERTS_PER_SPRITE = 6;
848
+ function buildQuadIndices(quads) {
849
+ const out = new Uint32Array(quads * INDICES_PER_QUAD);
850
+ for (let i = 0; i < quads; i++) {
851
+ const v = i * VERTS_PER_QUAD;
852
+ const o = i * INDICES_PER_QUAD;
853
+ out[o] = v;
854
+ out[o + 1] = v + 1;
855
+ out[o + 2] = v + 2;
856
+ out[o + 3] = v;
857
+ out[o + 4] = v + 2;
858
+ out[o + 5] = v + 3;
859
+ }
860
+ return out;
861
+ }
850
862
  var POINT_VERT = `#version 300 es
851
863
  in vec2 a_pos;
852
864
  in float a_radius;
@@ -974,6 +986,120 @@ function link(gl, vsSrc, fsSrc) {
974
986
  }
975
987
  return program;
976
988
  }
989
+ function writeQuad(out, o, x, y, width, height, u0, v0, u1, v1, r, g, b, al, rotation) {
990
+ let x1;
991
+ let y1;
992
+ let x2;
993
+ let y2;
994
+ let x3;
995
+ let y3;
996
+ if (rotation === 0) {
997
+ x1 = x + width;
998
+ y1 = y;
999
+ x2 = x + width;
1000
+ y2 = y + height;
1001
+ x3 = x;
1002
+ y3 = y + height;
1003
+ } else {
1004
+ const s = Math.sin(rotation);
1005
+ const c = Math.cos(rotation);
1006
+ const wc = width * c;
1007
+ const ws = width * s;
1008
+ const hc = height * c;
1009
+ const hs = height * s;
1010
+ x1 = x + wc;
1011
+ y1 = y + ws;
1012
+ x2 = x + wc - hs;
1013
+ y2 = y + ws + hc;
1014
+ x3 = x - hs;
1015
+ y3 = y + hc;
1016
+ }
1017
+ out[o] = x;
1018
+ out[o + 1] = y;
1019
+ out[o + 2] = u0;
1020
+ out[o + 3] = v0;
1021
+ out[o + 4] = r;
1022
+ out[o + 5] = g;
1023
+ out[o + 6] = b;
1024
+ out[o + 7] = al;
1025
+ out[o + 8] = x1;
1026
+ out[o + 9] = y1;
1027
+ out[o + 10] = u1;
1028
+ out[o + 11] = v0;
1029
+ out[o + 12] = r;
1030
+ out[o + 13] = g;
1031
+ out[o + 14] = b;
1032
+ out[o + 15] = al;
1033
+ out[o + 16] = x2;
1034
+ out[o + 17] = y2;
1035
+ out[o + 18] = u1;
1036
+ out[o + 19] = v1;
1037
+ out[o + 20] = r;
1038
+ out[o + 21] = g;
1039
+ out[o + 22] = b;
1040
+ out[o + 23] = al;
1041
+ out[o + 24] = x3;
1042
+ out[o + 25] = y3;
1043
+ out[o + 26] = u0;
1044
+ out[o + 27] = v1;
1045
+ out[o + 28] = r;
1046
+ out[o + 29] = g;
1047
+ out[o + 30] = b;
1048
+ out[o + 31] = al;
1049
+ }
1050
+ function writeRectQuad(out, o, x, y, width, height, r, g, b, al, rotation) {
1051
+ let x1;
1052
+ let y1;
1053
+ let x2;
1054
+ let y2;
1055
+ let x3;
1056
+ let y3;
1057
+ if (rotation === 0) {
1058
+ x1 = x + width;
1059
+ y1 = y;
1060
+ x2 = x + width;
1061
+ y2 = y + height;
1062
+ x3 = x;
1063
+ y3 = y + height;
1064
+ } else {
1065
+ const s = Math.sin(rotation);
1066
+ const c = Math.cos(rotation);
1067
+ const wc = width * c;
1068
+ const ws = width * s;
1069
+ const hc = height * c;
1070
+ const hs = height * s;
1071
+ x1 = x + wc;
1072
+ y1 = y + ws;
1073
+ x2 = x + wc - hs;
1074
+ y2 = y + ws + hc;
1075
+ x3 = x - hs;
1076
+ y3 = y + hc;
1077
+ }
1078
+ out[o] = x;
1079
+ out[o + 1] = y;
1080
+ out[o + 2] = r;
1081
+ out[o + 3] = g;
1082
+ out[o + 4] = b;
1083
+ out[o + 5] = al;
1084
+ out[o + 6] = x1;
1085
+ out[o + 7] = y1;
1086
+ out[o + 8] = r;
1087
+ out[o + 9] = g;
1088
+ out[o + 10] = b;
1089
+ out[o + 11] = al;
1090
+ out[o + 12] = x2;
1091
+ out[o + 13] = y2;
1092
+ out[o + 14] = r;
1093
+ out[o + 15] = g;
1094
+ out[o + 16] = b;
1095
+ out[o + 17] = al;
1096
+ out[o + 18] = x3;
1097
+ out[o + 19] = y3;
1098
+ out[o + 20] = r;
1099
+ out[o + 21] = g;
1100
+ out[o + 22] = b;
1101
+ out[o + 23] = al;
1102
+ }
977
1103
  function grow(data, needed) {
978
1104
  if (needed <= data.length) return data;
979
1105
  let cap = data.length;
@@ -1068,6 +1194,22 @@ function createWebGLPointRenderer(canvas) {
1068
1194
  gl.enableVertexAttribArray(cATint);
1069
1195
  gl.vertexAttribPointer(cATint, 4, gl.FLOAT, false, SPRITE_VERT_STRIDE, 16);
1070
1196
  gl.bindVertexArray(null);
1197
+ const quadIndexBuffer = gl.createBuffer();
1198
+ let quadIndexCapacity = 0;
1199
+ const ensureQuadIndices = (quads) => {
1200
+ if (quads <= quadIndexCapacity) return;
1201
+ let cap = quadIndexCapacity || 256;
1202
+ while (cap < quads) cap *= 2;
1203
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, quadIndexBuffer);
1204
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, buildQuadIndices(cap), gl.STATIC_DRAW);
1205
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
1206
+ quadIndexCapacity = cap;
1207
+ };
1208
+ for (const vao of [rectVAO, spriteVAO, glyphVAO, circleQuadVAO]) {
1209
+ gl.bindVertexArray(vao);
1210
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, quadIndexBuffer);
1211
+ gl.bindVertexArray(null);
1212
+ }
1071
1213
  let texture = null;
1072
1214
  let textureSource = null;
1073
1215
  let msdfTexture = null;
@@ -1075,15 +1217,13 @@ function createWebGLPointRenderer(canvas) {
1075
1217
  let distanceRange = 4;
1076
1218
  let pointData = new Float32Array(FLOATS_PER_POINT * 1024);
1077
1219
  let pointCount = 0;
1078
- let rectData = new Float32Array(FLOATS_PER_RECT_VERT * VERTS_PER_RECT * 256);
1220
+ let rectData = new Float32Array(FLOATS_PER_RECT_VERT * VERTS_PER_QUAD * 256);
1079
1221
  let rectCount = 0;
1080
- let spriteData = new Float32Array(FLOATS_PER_SPRITE_VERT * VERTS_PER_SPRITE * 256);
1222
+ let spriteData = new Float32Array(FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD * 256);
1081
1223
  let spriteCount = 0;
1082
- let glyphData = new Float32Array(FLOATS_PER_SPRITE_VERT * VERTS_PER_SPRITE * 1024);
1224
+ let glyphData = new Float32Array(FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD * 1024);
1083
1225
  let glyphCount = 0;
1084
- let circleQuadData = new Float32Array(
1085
- FLOATS_PER_SPRITE_VERT * VERTS_PER_SPRITE * 64
1086
- );
1226
+ let circleQuadData = new Float32Array(FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD * 64);
1087
1227
  let circleQuadCount = 0;
1088
1228
  let logicalW = 0;
1089
1229
  let logicalH = 0;
@@ -1093,7 +1233,7 @@ function createWebGLPointRenderer(canvas) {
1093
1233
  const maxPointSize = pointSizeRange ? pointSizeRange[1] : Infinity;
1094
1234
  const drawGlyphs = () => {
1095
1235
  if (glyphCount === 0 || !msdfTexture) return;
1096
- const floats = glyphCount * VERTS_PER_SPRITE * FLOATS_PER_SPRITE_VERT;
1236
+ const floats = glyphCount * VERTS_PER_QUAD * FLOATS_PER_SPRITE_VERT;
1097
1237
  gl.useProgram(msdfProgram);
1098
1238
  gl.bindVertexArray(glyphVAO);
1099
1239
  gl.bindBuffer(gl.ARRAY_BUFFER, glyphBuffer);
@@ -1103,7 +1243,8 @@ function createWebGLPointRenderer(canvas) {
1103
1243
  gl.uniform1i(gUTex, 0);
1104
1244
  gl.uniform2f(gURes, logicalW, logicalH);
1105
1245
  gl.uniform1f(gURange, distanceRange);
1106
- gl.drawArrays(gl.TRIANGLES, 0, glyphCount * VERTS_PER_SPRITE);
1246
+ ensureQuadIndices(glyphCount);
1247
+ gl.drawElements(gl.TRIANGLES, glyphCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
1107
1248
  gl.bindVertexArray(null);
1108
1249
  glyphCount = 0;
1109
1250
  };
@@ -1146,36 +1287,26 @@ function createWebGLPointRenderer(canvas) {
1146
1287
  },
1147
1288
  addSprite(x, y, width, height, u0, v0, u1, v1, color = "#ffffff", alpha = 1, rotation = 0) {
1148
1289
  if (!texture) return;
1149
- const stride = FLOATS_PER_SPRITE_VERT * VERTS_PER_SPRITE;
1290
+ const stride = FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD;
1150
1291
  spriteData = grow(spriteData, (spriteCount + 1) * stride);
1151
- const [r, g, b, a] = parseColorToRGBA(color);
1152
- const al = a * alpha;
1153
- const s = Math.sin(rotation);
1154
- const c = Math.cos(rotation);
1155
- const corner = (lx, ly) => [
1156
- x + lx * c - ly * s,
1157
- y + lx * s + ly * c
1158
- ];
1159
- const quad = [
1160
- [corner(0, 0), [u0, v0]],
1161
- [corner(width, 0), [u1, v0]],
1162
- [corner(width, height), [u1, v1]],
1163
- [corner(0, height), [u0, v1]]
1164
- ];
1165
- const order = [0, 1, 2, 0, 2, 3];
1166
- let o = spriteCount * stride;
1167
- for (const i of order) {
1168
- const [[vx, vy], [vu, vv]] = quad[i];
1169
- spriteData[o] = vx;
1170
- spriteData[o + 1] = vy;
1171
- spriteData[o + 2] = vu;
1172
- spriteData[o + 3] = vv;
1173
- spriteData[o + 4] = r;
1174
- spriteData[o + 5] = g;
1175
- spriteData[o + 6] = b;
1176
- spriteData[o + 7] = al;
1177
- o += FLOATS_PER_SPRITE_VERT;
1178
- }
1292
+ const rgba = parseColorToRGBA(color);
1293
+ writeQuad(
1294
+ spriteData,
1295
+ spriteCount * stride,
1296
+ x,
1297
+ y,
1298
+ width,
1299
+ height,
1300
+ u0,
1301
+ v0,
1302
+ u1,
1303
+ v1,
1304
+ rgba[0],
1305
+ rgba[1],
1306
+ rgba[2],
1307
+ rgba[3] * alpha,
1308
+ rotation
1309
+ );
1179
1310
  spriteCount++;
1180
1311
  },
1181
1312
  setMSDFTexture(source, range) {
@@ -1200,65 +1331,52 @@ function createWebGLPointRenderer(canvas) {
1200
1331
  },
1201
1332
  addGlyph(x, y, width, height, u0, v0, u1, v1, color = "#ffffff", alpha = 1, rotation = 0) {
1202
1333
  if (!msdfTexture) return;
1203
- const stride = FLOATS_PER_SPRITE_VERT * VERTS_PER_SPRITE;
1334
+ const stride = FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD;
1204
1335
  glyphData = grow(glyphData, (glyphCount + 1) * stride);
1205
- const [r, g, b, a] = parseColorToRGBA(color);
1206
- const al = a * alpha;
1207
- const s = Math.sin(rotation);
1208
- const c = Math.cos(rotation);
1209
- const corner = (lx, ly) => [
1210
- x + lx * c - ly * s,
1211
- y + lx * s + ly * c
1212
- ];
1213
- const quad = [
1214
- [corner(0, 0), [u0, v0]],
1215
- [corner(width, 0), [u1, v0]],
1216
- [corner(width, height), [u1, v1]],
1217
- [corner(0, height), [u0, v1]]
1218
- ];
1219
- const order = [0, 1, 2, 0, 2, 3];
1220
- let o = glyphCount * stride;
1221
- for (const i of order) {
1222
- const [[vx, vy], [vu, vv]] = quad[i];
1223
- glyphData[o] = vx;
1224
- glyphData[o + 1] = vy;
1225
- glyphData[o + 2] = vu;
1226
- glyphData[o + 3] = vv;
1227
- glyphData[o + 4] = r;
1228
- glyphData[o + 5] = g;
1229
- glyphData[o + 6] = b;
1230
- glyphData[o + 7] = al;
1231
- o += FLOATS_PER_SPRITE_VERT;
1232
- }
1336
+ const rgba = parseColorToRGBA(color);
1337
+ writeQuad(
1338
+ glyphData,
1339
+ glyphCount * stride,
1340
+ x,
1341
+ y,
1342
+ width,
1343
+ height,
1344
+ u0,
1345
+ v0,
1346
+ u1,
1347
+ v1,
1348
+ rgba[0],
1349
+ rgba[1],
1350
+ rgba[2],
1351
+ rgba[3] * alpha,
1352
+ rotation
1353
+ );
1233
1354
  glyphCount++;
1234
1355
  },
1235
1356
  addCircle(x, y, radius, color, alpha = 1) {
1236
1357
  const needsQuad = logicalW > 0 && (x < radius || y < radius || x > logicalW - radius || y > logicalH - radius) || radius * 2 * dpr > maxPointSize;
1237
1358
  if (needsQuad) {
1238
- const stride = FLOATS_PER_SPRITE_VERT * VERTS_PER_SPRITE;
1359
+ const stride = FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD;
1239
1360
  circleQuadData = grow(circleQuadData, (circleQuadCount + 1) * stride);
1240
- const [qr, qg, qb, qa] = parseColorToRGBA(color);
1241
- const al = qa * alpha;
1242
- const quad = [
1243
- [x - radius, y - radius, 0, 0],
1244
- [x + radius, y - radius, 1, 0],
1245
- [x + radius, y + radius, 1, 1],
1246
- [x - radius, y + radius, 0, 1]
1247
- ];
1248
- const order = [0, 1, 2, 0, 2, 3];
1249
- let o2 = circleQuadCount * stride;
1250
- for (const i of order) {
1251
- const [vx, vy, vu, vv] = quad[i];
1252
- circleQuadData[o2] = vx;
1253
- circleQuadData[o2 + 1] = vy;
1254
- circleQuadData[o2 + 2] = vu;
1255
- circleQuadData[o2 + 3] = vv;
1256
- circleQuadData[o2 + 4] = qr;
1257
- circleQuadData[o2 + 5] = qg;
1258
- circleQuadData[o2 + 6] = qb;
1259
- circleQuadData[o2 + 7] = al;
1260
- o2 += FLOATS_PER_SPRITE_VERT;
1261
- }
1361
+ const rgba = parseColorToRGBA(color);
1362
+ const d = radius * 2;
1363
+ writeQuad(
1364
+ circleQuadData,
1365
+ circleQuadCount * stride,
1366
+ x - radius,
1367
+ y - radius,
1368
+ d,
1369
+ d,
1370
+ 0,
1371
+ 0,
1372
+ 1,
1373
+ 1,
1374
+ rgba[0],
1375
+ rgba[1],
1376
+ rgba[2],
1377
+ rgba[3] * alpha,
1378
+ 0
1379
+ );
1262
1380
  circleQuadCount++;
1263
1381
  return;
1264
1382
  }
@@ -1275,42 +1393,34 @@ function createWebGLPointRenderer(canvas) {
1275
1393
  pointCount++;
1276
1394
  },
1277
1395
  addRect(x, y, width, height, color, alpha = 1, rotation = 0) {
1278
- const stride = FLOATS_PER_RECT_VERT * VERTS_PER_RECT;
1396
+ const stride = FLOATS_PER_RECT_VERT * VERTS_PER_QUAD;
1279
1397
  rectData = grow(rectData, (rectCount + 1) * stride);
1280
- const [r, g, b, a] = parseColorToRGBA(color);
1281
- const al = a * alpha;
1282
- const s = Math.sin(rotation);
1283
- const c = Math.cos(rotation);
1284
- const corner = (lx, ly) => [
1285
- x + lx * c - ly * s,
1286
- y + lx * s + ly * c
1287
- ];
1288
- const p0 = corner(0, 0);
1289
- const p1 = corner(width, 0);
1290
- const p2 = corner(width, height);
1291
- const p3 = corner(0, height);
1292
- const verts = [p0, p1, p2, p0, p2, p3];
1293
- let o = rectCount * stride;
1294
- for (const [vx, vy] of verts) {
1295
- rectData[o] = vx;
1296
- rectData[o + 1] = vy;
1297
- rectData[o + 2] = r;
1298
- rectData[o + 3] = g;
1299
- rectData[o + 4] = b;
1300
- rectData[o + 5] = al;
1301
- o += FLOATS_PER_RECT_VERT;
1302
- }
1398
+ const rgba = parseColorToRGBA(color);
1399
+ writeRectQuad(
1400
+ rectData,
1401
+ rectCount * stride,
1402
+ x,
1403
+ y,
1404
+ width,
1405
+ height,
1406
+ rgba[0],
1407
+ rgba[1],
1408
+ rgba[2],
1409
+ rgba[3] * alpha,
1410
+ rotation
1411
+ );
1303
1412
  rectCount++;
1304
1413
  },
1305
1414
  flush() {
1306
1415
  if (rectCount > 0) {
1307
- const floats = rectCount * VERTS_PER_RECT * FLOATS_PER_RECT_VERT;
1416
+ const floats = rectCount * VERTS_PER_QUAD * FLOATS_PER_RECT_VERT;
1308
1417
  gl.useProgram(rectProgram);
1309
1418
  gl.bindVertexArray(rectVAO);
1310
1419
  gl.bindBuffer(gl.ARRAY_BUFFER, rectBuffer);
1311
1420
  gl.bufferData(gl.ARRAY_BUFFER, rectData.subarray(0, floats), gl.DYNAMIC_DRAW);
1312
1421
  gl.uniform2f(rURes, logicalW, logicalH);
1313
- gl.drawArrays(gl.TRIANGLES, 0, rectCount * VERTS_PER_RECT);
1422
+ ensureQuadIndices(rectCount);
1423
+ gl.drawElements(gl.TRIANGLES, rectCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
1314
1424
  }
1315
1425
  if (pointCount > 0) {
1316
1426
  gl.useProgram(pointProgram);
@@ -1326,16 +1436,17 @@ function createWebGLPointRenderer(canvas) {
1326
1436
  gl.drawArrays(gl.POINTS, 0, pointCount);
1327
1437
  }
1328
1438
  if (circleQuadCount > 0) {
1329
- const floats = circleQuadCount * VERTS_PER_SPRITE * FLOATS_PER_SPRITE_VERT;
1439
+ const floats = circleQuadCount * VERTS_PER_QUAD * FLOATS_PER_SPRITE_VERT;
1330
1440
  gl.useProgram(circleQuadProgram);
1331
1441
  gl.bindVertexArray(circleQuadVAO);
1332
1442
  gl.bindBuffer(gl.ARRAY_BUFFER, circleQuadBuffer);
1333
1443
  gl.bufferData(gl.ARRAY_BUFFER, circleQuadData.subarray(0, floats), gl.DYNAMIC_DRAW);
1334
1444
  gl.uniform2f(cURes, logicalW, logicalH);
1335
- gl.drawArrays(gl.TRIANGLES, 0, circleQuadCount * VERTS_PER_SPRITE);
1445
+ ensureQuadIndices(circleQuadCount);
1446
+ gl.drawElements(gl.TRIANGLES, circleQuadCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
1336
1447
  }
1337
1448
  if (spriteCount > 0 && texture) {
1338
- const floats = spriteCount * VERTS_PER_SPRITE * FLOATS_PER_SPRITE_VERT;
1449
+ const floats = spriteCount * VERTS_PER_QUAD * FLOATS_PER_SPRITE_VERT;
1339
1450
  gl.useProgram(spriteProgram);
1340
1451
  gl.bindVertexArray(spriteVAO);
1341
1452
  gl.bindBuffer(gl.ARRAY_BUFFER, spriteBuffer);
@@ -1344,7 +1455,8 @@ function createWebGLPointRenderer(canvas) {
1344
1455
  gl.bindTexture(gl.TEXTURE_2D, texture);
1345
1456
  gl.uniform1i(sUTex, 0);
1346
1457
  gl.uniform2f(sURes, logicalW, logicalH);
1347
- gl.drawArrays(gl.TRIANGLES, 0, spriteCount * VERTS_PER_SPRITE);
1458
+ ensureQuadIndices(spriteCount);
1459
+ gl.drawElements(gl.TRIANGLES, spriteCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
1348
1460
  }
1349
1461
  gl.bindVertexArray(null);
1350
1462
  drawGlyphs();
@@ -1357,6 +1469,7 @@ function createWebGLPointRenderer(canvas) {
1357
1469
  gl.deleteBuffer(spriteBuffer);
1358
1470
  gl.deleteBuffer(glyphBuffer);
1359
1471
  gl.deleteBuffer(circleQuadBuffer);
1472
+ gl.deleteBuffer(quadIndexBuffer);
1360
1473
  gl.deleteVertexArray(pointVAO);
1361
1474
  gl.deleteVertexArray(rectVAO);
1362
1475
  gl.deleteVertexArray(spriteVAO);
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
 
10
10
 
11
- var _chunkIYPLG4Z4js = require('./chunk-IYPLG4Z4.js');
11
+ var _chunkL4SWVP2Hjs = require('./chunk-L4SWVP2H.js');
12
12
 
13
13
 
14
14
 
@@ -2162,17 +2162,17 @@ var Scene = (_class6 = class _Scene {
2162
2162
  this._devFrameCount++;
2163
2163
  if (this._devFrameCount % 120 !== 0) return;
2164
2164
  if (this.a11yElements) {
2165
- let interactiveCount = 0;
2165
+ let projectableCount = 0;
2166
2166
  const walk = (node) => {
2167
- if (node.interactive && node.width > 0) interactiveCount++;
2167
+ if (this.shouldProjectA11y(node)) projectableCount++;
2168
2168
  for (const c of node.children) walk(c);
2169
2169
  };
2170
2170
  walk(this.root);
2171
2171
  for (const c of this.overlayRoot.children) walk(c);
2172
2172
  const shadowCount = this.a11yElements.size;
2173
- if (shadowCount > interactiveCount + 2) {
2173
+ if (shadowCount > projectableCount) {
2174
2174
  this._devWarn(
2175
- `a11yElements (${shadowCount}) exceeds interactive entities (${interactiveCount}). Call scene.detachA11y(entity) before removing interactive children from the tree, or their shadow nodes leak.`
2175
+ `a11yElements (${shadowCount}) exceeds projectable entities (${projectableCount}). Call scene.detachA11y(entity) before removing interactive children from the tree, or their shadow nodes leak.`
2176
2176
  );
2177
2177
  }
2178
2178
  }
@@ -2247,7 +2247,7 @@ var Scene = (_class6 = class _Scene {
2247
2247
  if (options.renderer) {
2248
2248
  this.renderer = options.renderer;
2249
2249
  } else {
2250
- this.renderer = new (0, _chunkIYPLG4Z4js.CanvasRenderer)(
2250
+ this.renderer = new (0, _chunkL4SWVP2Hjs.CanvasRenderer)(
2251
2251
  canvas,
2252
2252
  this.disableWindowResize ? { width: this.width, height: this.height } : void 0,
2253
2253
  this.maxDPR
@@ -2643,7 +2643,19 @@ var Scene = (_class6 = class _Scene {
2643
2643
  * without removing it from the scene graph. Components that manage dynamic
2644
2644
  * interactive *child* entities (e.g. a {@link Entity}'s per-link hotspots) call
2645
2645
  * this before discarding those children so their shadow `<a>`/controls don't
2646
- * leak (the per-frame `syncA11y` only creates/updates, it never prunes).
2646
+ * leak.
2647
+ *
2648
+ * `syncA11y` itself only creates and updates, never prunes — but it is always
2649
+ * followed by `enforceA11yDomOrder`, whose prune pass removes any element
2650
+ * whose entity is no longer reachable in the tree or no longer satisfies
2651
+ * {@link shouldProjectA11y}. So an entity that is `remove()`d, or whose
2652
+ * `interactive` flips to `false`, has its element torn down on the next synced
2653
+ * frame without any explicit call.
2654
+ *
2655
+ * This method is for the case that pass cannot see: a child dropped from a
2656
+ * component's own bookkeeping while still parented, or one discarded before
2657
+ * the next sync runs. Calling it is always safe and is the right habit for
2658
+ * pooled children.
2647
2659
  *
2648
2660
  * @param entity - The subtree whose shadow nodes should be removed.
2649
2661
  */
@@ -2932,12 +2944,34 @@ var Scene = (_class6 = class _Scene {
2932
2944
  }
2933
2945
  if (element.getAttribute(name) !== value) element.setAttribute(name, value);
2934
2946
  }
2947
+ /**
2948
+ * Whether `node` should have an a11y shadow element projected for it.
2949
+ *
2950
+ * The single authority for that decision. It was previously inlined verbatim
2951
+ * at four call sites — `syncA11y` (create/update), `enforceA11yDomOrder`
2952
+ * (which ids survive pruning), `getA11yTree` (the public snapshot) and
2953
+ * `render` (z-index / reading-order assignment). Four copies of one predicate
2954
+ * is a standing correctness hazard: if any of them drifts, elements either
2955
+ * leak (created but never marked active, so pruned every frame and rebuilt) or
2956
+ * go missing from the semantic tree while still present in the DOM.
2957
+ *
2958
+ * A box is required because a zero-size element is unfocusable and
2959
+ * unhittable; `a11yFullViewport` is the deliberate exception, since those
2960
+ * nodes are boundless interaction surfaces mounted behind everything else.
2961
+ *
2962
+ * Keep this the only place the rule is written. A planned per-entity
2963
+ * `a11yProjection` mode ('eager' | 'onDemand' | 'never') extends exactly this
2964
+ * predicate, which is only tractable while it has one home.
2965
+ */
2966
+ shouldProjectA11y(node) {
2967
+ return node.interactive && (node.width > 0 || node.a11yFullViewport);
2968
+ }
2935
2969
  syncA11y(node) {
2936
2970
  if (!this.a11yRoot) return;
2937
2971
  if (node.isDOMPortal) {
2938
2972
  return;
2939
2973
  }
2940
- if (node.interactive && (node.width > 0 || node.a11yFullViewport)) {
2974
+ if (this.shouldProjectA11y(node)) {
2941
2975
  let el = this.a11yElements.get(node.id);
2942
2976
  const attrs = node.getA11yAttributes();
2943
2977
  const expectedTag = attrs.tag || "div";
@@ -3136,7 +3170,7 @@ var Scene = (_class6 = class _Scene {
3136
3170
  this.syncOptionalAttribute(
3137
3171
  el,
3138
3172
  "href",
3139
- attrs.href === void 0 ? void 0 : _chunkIYPLG4Z4js.sanitizeUrl.call(void 0, attrs.href)
3173
+ attrs.href === void 0 ? void 0 : _chunkL4SWVP2Hjs.sanitizeUrl.call(void 0, attrs.href)
3140
3174
  );
3141
3175
  this.syncOptionalAttribute(el, "target", attrs.target);
3142
3176
  }
@@ -3713,7 +3747,7 @@ var Scene = (_class6 = class _Scene {
3713
3747
  if (node.a11yFullViewport) this.fullViewportElements.push(contentEl);
3714
3748
  else this.normalElements.push(contentEl);
3715
3749
  }
3716
- if (node.interactive && (node.width > 0 || node.a11yFullViewport)) {
3750
+ if (this.shouldProjectA11y(node)) {
3717
3751
  const el = this.a11yElements.get(node.id);
3718
3752
  if (el) {
3719
3753
  this.activeIds.add(node.id);
@@ -3852,7 +3886,7 @@ var Scene = (_class6 = class _Scene {
3852
3886
  const traverse = (node, parentNode) => {
3853
3887
  if (node.isDOMPortal) return;
3854
3888
  let currentA11yNode = null;
3855
- if (node.interactive && (node.width > 0 || node.a11yFullViewport)) {
3889
+ if (this.shouldProjectA11y(node)) {
3856
3890
  const el = this.a11yElements.get(node.id);
3857
3891
  if (el) {
3858
3892
  const attrs = node.getA11yAttributes();
@@ -4222,7 +4256,7 @@ var Scene = (_class6 = class _Scene {
4222
4256
  const orthogonalTolerance = Math.max(1, worldScaleX * worldScaleY) * 1e-6;
4223
4257
  const isSimilarityTransform = Number.isFinite(worldScaleX) && Number.isFinite(worldScaleY) && Math.abs(worldScaleX - worldScaleY) <= scaleTolerance && Math.abs(a * c + b * d) <= orthogonalTolerance;
4224
4258
  const a11yEl = isMainRenderer ? this.a11yElements.get(node.id) : void 0;
4225
- const willProjectA11y = isMainRenderer && node.interactive && (node.width > 0 || node.a11yFullViewport);
4259
+ const willProjectA11y = isMainRenderer && this.shouldProjectA11y(node);
4226
4260
  if (a11yEl || willProjectA11y) {
4227
4261
  const renderOrder = this.renderOrderCounter++;
4228
4262
  if (willProjectA11y) this.a11yRenderOrders.set(node.id, renderOrder);
@@ -4344,7 +4378,7 @@ var Scene = (_class6 = class _Scene {
4344
4378
  * Export the current scene state to a lightweight, flat SVG XML string.
4345
4379
  */
4346
4380
  toSVG() {
4347
- const renderer = new (0, _chunkIYPLG4Z4js.SVGRenderer)(this.width, this.height);
4381
+ const renderer = new (0, _chunkL4SWVP2Hjs.SVGRenderer)(this.width, this.height);
4348
4382
  this.render(renderer, 0, 0);
4349
4383
  return renderer.toXMLString();
4350
4384
  }
@@ -5370,8 +5404,8 @@ var DOMPortalEntity = (_class10 = class extends _chunkDUYB4GX4js.Entity {
5370
5404
  }, _class10);
5371
5405
 
5372
5406
  // src/index.ts
5373
- Scene.registerWebGLPointRendererCreator(_chunkIYPLG4Z4js.createWebGLPointRenderer);
5374
- Scene.registerWebGPUParticleSystemManager(_chunkIYPLG4Z4js.WebGPUParticleSystemManager);
5407
+ Scene.registerWebGLPointRendererCreator(_chunkL4SWVP2Hjs.createWebGLPointRenderer);
5408
+ Scene.registerWebGPUParticleSystemManager(_chunkL4SWVP2Hjs.WebGPUParticleSystemManager);
5375
5409
 
5376
5410
 
5377
5411
 
@@ -5406,4 +5440,4 @@ Scene.registerWebGPUParticleSystemManager(_chunkIYPLG4Z4js.WebGPUParticleSystemM
5406
5440
 
5407
5441
 
5408
5442
 
5409
- exports.CanvasRenderer = _chunkIYPLG4Z4js.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DOMPortalEntity = DOMPortalEntity; exports.Entity = _chunkDUYB4GX4js.Entity; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.MSDFTextEntity = _chunkDUYB4GX4js.MSDFTextEntity; exports.PARTICLE_OFFSET_LIFE = PARTICLE_OFFSET_LIFE; exports.PARTICLE_OFFSET_ORIGIN_X = PARTICLE_OFFSET_ORIGIN_X; exports.PARTICLE_OFFSET_ORIGIN_Y = PARTICLE_OFFSET_ORIGIN_Y; exports.PARTICLE_OFFSET_POSITION_X = PARTICLE_OFFSET_POSITION_X; exports.PARTICLE_OFFSET_POSITION_Y = PARTICLE_OFFSET_POSITION_Y; exports.PARTICLE_OFFSET_SIZE = PARTICLE_OFFSET_SIZE; exports.PARTICLE_OFFSET_VELOCITY_X = PARTICLE_OFFSET_VELOCITY_X; exports.PARTICLE_OFFSET_VELOCITY_Y = PARTICLE_OFFSET_VELOCITY_Y; exports.PARTICLE_STRIDE_FLOATS = PARTICLE_STRIDE_FLOATS; exports.REDUCED_MOTION_FPS = REDUCED_MOTION_FPS; exports.Rect = Rect; exports.SVGEntity = _chunkDUYB4GX4js.SVGEntity; exports.SVGRenderer = _chunkIYPLG4Z4js.SVGRenderer; exports.Scene = Scene; exports.SplineEntity = SplineEntity; exports.TextEntity = TextEntity; exports.TextRasterCache = _chunkIYPLG4Z4js.TextRasterCache; exports.VectoJSEvent = _chunkDUYB4GX4js.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkIYPLG4Z4js.WebGPUParticleSystemManager; exports.createWebGLPointRenderer = _chunkIYPLG4Z4js.createWebGLPointRenderer; exports.isSafeUrl = _chunkIYPLG4Z4js.isSafeUrl; exports.loadSpline = loadSpline; exports.parseColorToRGBA = _chunkIYPLG4Z4js.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkIYPLG4Z4js.sanitizeUrl;
5443
+ exports.CanvasRenderer = _chunkL4SWVP2Hjs.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DOMPortalEntity = DOMPortalEntity; exports.Entity = _chunkDUYB4GX4js.Entity; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.MSDFTextEntity = _chunkDUYB4GX4js.MSDFTextEntity; exports.PARTICLE_OFFSET_LIFE = PARTICLE_OFFSET_LIFE; exports.PARTICLE_OFFSET_ORIGIN_X = PARTICLE_OFFSET_ORIGIN_X; exports.PARTICLE_OFFSET_ORIGIN_Y = PARTICLE_OFFSET_ORIGIN_Y; exports.PARTICLE_OFFSET_POSITION_X = PARTICLE_OFFSET_POSITION_X; exports.PARTICLE_OFFSET_POSITION_Y = PARTICLE_OFFSET_POSITION_Y; exports.PARTICLE_OFFSET_SIZE = PARTICLE_OFFSET_SIZE; exports.PARTICLE_OFFSET_VELOCITY_X = PARTICLE_OFFSET_VELOCITY_X; exports.PARTICLE_OFFSET_VELOCITY_Y = PARTICLE_OFFSET_VELOCITY_Y; exports.PARTICLE_STRIDE_FLOATS = PARTICLE_STRIDE_FLOATS; exports.REDUCED_MOTION_FPS = REDUCED_MOTION_FPS; exports.Rect = Rect; exports.SVGEntity = _chunkDUYB4GX4js.SVGEntity; exports.SVGRenderer = _chunkL4SWVP2Hjs.SVGRenderer; exports.Scene = Scene; exports.SplineEntity = SplineEntity; exports.TextEntity = TextEntity; exports.TextRasterCache = _chunkL4SWVP2Hjs.TextRasterCache; exports.VectoJSEvent = _chunkDUYB4GX4js.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkL4SWVP2Hjs.WebGPUParticleSystemManager; exports.createWebGLPointRenderer = _chunkL4SWVP2Hjs.createWebGLPointRenderer; exports.isSafeUrl = _chunkL4SWVP2Hjs.isSafeUrl; exports.loadSpline = loadSpline; exports.parseColorToRGBA = _chunkL4SWVP2Hjs.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkL4SWVP2Hjs.sanitizeUrl;
package/dist/index.mjs CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  isSafeUrl,
8
8
  parseColorToRGBA,
9
9
  sanitizeUrl
10
- } from "./chunk-JFU56BX4.mjs";
10
+ } from "./chunk-QS3CUV7H.mjs";
11
11
  import {
12
12
  Entity,
13
13
  MSDFTextEntity,
@@ -2161,17 +2161,17 @@ var Scene = class _Scene {
2161
2161
  this._devFrameCount++;
2162
2162
  if (this._devFrameCount % 120 !== 0) return;
2163
2163
  if (this.a11yElements) {
2164
- let interactiveCount = 0;
2164
+ let projectableCount = 0;
2165
2165
  const walk = (node) => {
2166
- if (node.interactive && node.width > 0) interactiveCount++;
2166
+ if (this.shouldProjectA11y(node)) projectableCount++;
2167
2167
  for (const c of node.children) walk(c);
2168
2168
  };
2169
2169
  walk(this.root);
2170
2170
  for (const c of this.overlayRoot.children) walk(c);
2171
2171
  const shadowCount = this.a11yElements.size;
2172
- if (shadowCount > interactiveCount + 2) {
2172
+ if (shadowCount > projectableCount) {
2173
2173
  this._devWarn(
2174
- `a11yElements (${shadowCount}) exceeds interactive entities (${interactiveCount}). Call scene.detachA11y(entity) before removing interactive children from the tree, or their shadow nodes leak.`
2174
+ `a11yElements (${shadowCount}) exceeds projectable entities (${projectableCount}). Call scene.detachA11y(entity) before removing interactive children from the tree, or their shadow nodes leak.`
2175
2175
  );
2176
2176
  }
2177
2177
  }
@@ -2642,7 +2642,19 @@ var Scene = class _Scene {
2642
2642
  * without removing it from the scene graph. Components that manage dynamic
2643
2643
  * interactive *child* entities (e.g. a {@link Entity}'s per-link hotspots) call
2644
2644
  * this before discarding those children so their shadow `<a>`/controls don't
2645
- * leak (the per-frame `syncA11y` only creates/updates, it never prunes).
2645
+ * leak.
2646
+ *
2647
+ * `syncA11y` itself only creates and updates, never prunes — but it is always
2648
+ * followed by `enforceA11yDomOrder`, whose prune pass removes any element
2649
+ * whose entity is no longer reachable in the tree or no longer satisfies
2650
+ * {@link shouldProjectA11y}. So an entity that is `remove()`d, or whose
2651
+ * `interactive` flips to `false`, has its element torn down on the next synced
2652
+ * frame without any explicit call.
2653
+ *
2654
+ * This method is for the case that pass cannot see: a child dropped from a
2655
+ * component's own bookkeeping while still parented, or one discarded before
2656
+ * the next sync runs. Calling it is always safe and is the right habit for
2657
+ * pooled children.
2646
2658
  *
2647
2659
  * @param entity - The subtree whose shadow nodes should be removed.
2648
2660
  */
@@ -2931,12 +2943,34 @@ var Scene = class _Scene {
2931
2943
  }
2932
2944
  if (element.getAttribute(name) !== value) element.setAttribute(name, value);
2933
2945
  }
2946
+ /**
2947
+ * Whether `node` should have an a11y shadow element projected for it.
2948
+ *
2949
+ * The single authority for that decision. It was previously inlined verbatim
2950
+ * at four call sites — `syncA11y` (create/update), `enforceA11yDomOrder`
2951
+ * (which ids survive pruning), `getA11yTree` (the public snapshot) and
2952
+ * `render` (z-index / reading-order assignment). Four copies of one predicate
2953
+ * is a standing correctness hazard: if any of them drifts, elements either
2954
+ * leak (created but never marked active, so pruned every frame and rebuilt) or
2955
+ * go missing from the semantic tree while still present in the DOM.
2956
+ *
2957
+ * A box is required because a zero-size element is unfocusable and
2958
+ * unhittable; `a11yFullViewport` is the deliberate exception, since those
2959
+ * nodes are boundless interaction surfaces mounted behind everything else.
2960
+ *
2961
+ * Keep this the only place the rule is written. A planned per-entity
2962
+ * `a11yProjection` mode ('eager' | 'onDemand' | 'never') extends exactly this
2963
+ * predicate, which is only tractable while it has one home.
2964
+ */
2965
+ shouldProjectA11y(node) {
2966
+ return node.interactive && (node.width > 0 || node.a11yFullViewport);
2967
+ }
2934
2968
  syncA11y(node) {
2935
2969
  if (!this.a11yRoot) return;
2936
2970
  if (node.isDOMPortal) {
2937
2971
  return;
2938
2972
  }
2939
- if (node.interactive && (node.width > 0 || node.a11yFullViewport)) {
2973
+ if (this.shouldProjectA11y(node)) {
2940
2974
  let el = this.a11yElements.get(node.id);
2941
2975
  const attrs = node.getA11yAttributes();
2942
2976
  const expectedTag = attrs.tag || "div";
@@ -3712,7 +3746,7 @@ var Scene = class _Scene {
3712
3746
  if (node.a11yFullViewport) this.fullViewportElements.push(contentEl);
3713
3747
  else this.normalElements.push(contentEl);
3714
3748
  }
3715
- if (node.interactive && (node.width > 0 || node.a11yFullViewport)) {
3749
+ if (this.shouldProjectA11y(node)) {
3716
3750
  const el = this.a11yElements.get(node.id);
3717
3751
  if (el) {
3718
3752
  this.activeIds.add(node.id);
@@ -3851,7 +3885,7 @@ var Scene = class _Scene {
3851
3885
  const traverse = (node, parentNode) => {
3852
3886
  if (node.isDOMPortal) return;
3853
3887
  let currentA11yNode = null;
3854
- if (node.interactive && (node.width > 0 || node.a11yFullViewport)) {
3888
+ if (this.shouldProjectA11y(node)) {
3855
3889
  const el = this.a11yElements.get(node.id);
3856
3890
  if (el) {
3857
3891
  const attrs = node.getA11yAttributes();
@@ -4221,7 +4255,7 @@ var Scene = class _Scene {
4221
4255
  const orthogonalTolerance = Math.max(1, worldScaleX * worldScaleY) * 1e-6;
4222
4256
  const isSimilarityTransform = Number.isFinite(worldScaleX) && Number.isFinite(worldScaleY) && Math.abs(worldScaleX - worldScaleY) <= scaleTolerance && Math.abs(a * c + b * d) <= orthogonalTolerance;
4223
4257
  const a11yEl = isMainRenderer ? this.a11yElements.get(node.id) : void 0;
4224
- const willProjectA11y = isMainRenderer && node.interactive && (node.width > 0 || node.a11yFullViewport);
4258
+ const willProjectA11y = isMainRenderer && this.shouldProjectA11y(node);
4225
4259
  if (a11yEl || willProjectA11y) {
4226
4260
  const renderOrder = this.renderOrderCounter++;
4227
4261
  if (willProjectA11y) this.a11yRenderOrders.set(node.id, renderOrder);
package/dist/renderer.js CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
 
8
8
 
9
- var _chunkIYPLG4Z4js = require('./chunk-IYPLG4Z4.js');
9
+ var _chunkL4SWVP2Hjs = require('./chunk-L4SWVP2H.js');
10
10
 
11
11
 
12
12
 
@@ -14,4 +14,4 @@ var _chunkIYPLG4Z4js = require('./chunk-IYPLG4Z4.js');
14
14
 
15
15
 
16
16
 
17
- exports.CanvasRenderer = _chunkIYPLG4Z4js.CanvasRenderer; exports.SVGRenderer = _chunkIYPLG4Z4js.SVGRenderer; exports.TextRasterCache = _chunkIYPLG4Z4js.TextRasterCache; exports.WebGPUParticleSystemManager = _chunkIYPLG4Z4js.WebGPUParticleSystemManager; exports.createWebGLPointRenderer = _chunkIYPLG4Z4js.createWebGLPointRenderer; exports.parseColorToRGBA = _chunkIYPLG4Z4js.parseColorToRGBA;
17
+ exports.CanvasRenderer = _chunkL4SWVP2Hjs.CanvasRenderer; exports.SVGRenderer = _chunkL4SWVP2Hjs.SVGRenderer; exports.TextRasterCache = _chunkL4SWVP2Hjs.TextRasterCache; exports.WebGPUParticleSystemManager = _chunkL4SWVP2Hjs.WebGPUParticleSystemManager; exports.createWebGLPointRenderer = _chunkL4SWVP2Hjs.createWebGLPointRenderer; exports.parseColorToRGBA = _chunkL4SWVP2Hjs.parseColorToRGBA;
package/dist/renderer.mjs CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  WebGPUParticleSystemManager,
6
6
  createWebGLPointRenderer,
7
7
  parseColorToRGBA
8
- } from "./chunk-JFU56BX4.mjs";
8
+ } from "./chunk-QS3CUV7H.mjs";
9
9
  export {
10
10
  CanvasRenderer,
11
11
  SVGRenderer,
@@ -673,7 +673,19 @@ export declare class Scene {
673
673
  * without removing it from the scene graph. Components that manage dynamic
674
674
  * interactive *child* entities (e.g. a {@link Entity}'s per-link hotspots) call
675
675
  * this before discarding those children so their shadow `<a>`/controls don't
676
- * leak (the per-frame `syncA11y` only creates/updates, it never prunes).
676
+ * leak.
677
+ *
678
+ * `syncA11y` itself only creates and updates, never prunes — but it is always
679
+ * followed by `enforceA11yDomOrder`, whose prune pass removes any element
680
+ * whose entity is no longer reachable in the tree or no longer satisfies
681
+ * {@link shouldProjectA11y}. So an entity that is `remove()`d, or whose
682
+ * `interactive` flips to `false`, has its element torn down on the next synced
683
+ * frame without any explicit call.
684
+ *
685
+ * This method is for the case that pass cannot see: a child dropped from a
686
+ * component's own bookkeeping while still parented, or one discarded before
687
+ * the next sync runs. Calling it is always safe and is the right habit for
688
+ * pooled children.
677
689
  *
678
690
  * @param entity - The subtree whose shadow nodes should be removed.
679
691
  */
@@ -756,6 +768,26 @@ export declare class Scene {
756
768
  /** True when any node in the subtree has a pending animation. */
757
769
  /** True when any node in the subtree is interactive (drives a11y sync). */
758
770
  private syncOptionalAttribute;
771
+ /**
772
+ * Whether `node` should have an a11y shadow element projected for it.
773
+ *
774
+ * The single authority for that decision. It was previously inlined verbatim
775
+ * at four call sites — `syncA11y` (create/update), `enforceA11yDomOrder`
776
+ * (which ids survive pruning), `getA11yTree` (the public snapshot) and
777
+ * `render` (z-index / reading-order assignment). Four copies of one predicate
778
+ * is a standing correctness hazard: if any of them drifts, elements either
779
+ * leak (created but never marked active, so pruned every frame and rebuilt) or
780
+ * go missing from the semantic tree while still present in the DOM.
781
+ *
782
+ * A box is required because a zero-size element is unfocusable and
783
+ * unhittable; `a11yFullViewport` is the deliberate exception, since those
784
+ * nodes are boundless interaction surfaces mounted behind everything else.
785
+ *
786
+ * Keep this the only place the rule is written. A planned per-entity
787
+ * `a11yProjection` mode ('eager' | 'onDemand' | 'never') extends exactly this
788
+ * predicate, which is only tractable while it has one home.
789
+ */
790
+ private shouldProjectA11y;
759
791
  private syncA11y;
760
792
  /**
761
793
  * Mirror one entity's static text ({@link Entity.getContentProjection}) as a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectojs/core",
3
- "version": "1.16.1",
3
+ "version": "1.16.3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },