hyperframes 0.8.73 → 0.8.74
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +2415 -1449
- package/dist/commands/layout-audit.browser.js +116 -0
- package/dist/fontLocalizeCli.js +640 -334
- package/dist/hyperframe-runtime.js +17 -17
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +17 -17
- package/dist/hyperframes-player.global.js +3 -3
- package/dist/renderSetupWorker.js +242 -0
- package/dist/skills/hyperframes-cli/references/lint-validate-inspect.md +1 -1
- package/dist/studio/assets/hyperframes-player-Bs9qG2hg.js +464 -0
- package/dist/studio/assets/{index-CMFPPAGq.js → index-B86QTmD2.js} +1 -1
- package/dist/studio/assets/{index-DxIHGgGZ.js → index-DhTyI9zH.js} +107 -107
- package/dist/studio/assets/{index-DKInxDDv.js → index-vccPjvCl.js} +1 -1
- package/dist/studio/index.html +1 -1
- package/dist/studio/index.js +1387 -1306
- package/dist/studio/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/studio/assets/hyperframes-player-BUauoSsb.js +0 -464
|
@@ -1274,6 +1274,121 @@
|
|
|
1274
1274
|
return issues;
|
|
1275
1275
|
}
|
|
1276
1276
|
|
|
1277
|
+
// Pixels drawn into a <canvas> have no DOM box, so text the frame edge cuts is invisible to
|
|
1278
|
+
// canvas_overflow. Read a thin band just inside each covered frame edge and count sharp steps.
|
|
1279
|
+
const CANVAS_EDGE_STEP = 96;
|
|
1280
|
+
const CANVAS_EDGE_MIN_STEPS = 2;
|
|
1281
|
+
// Monospace and bitmap glyph cells end in a gap, so the outermost column alone can read empty.
|
|
1282
|
+
const CANVAS_EDGE_BAND_PX = 4;
|
|
1283
|
+
|
|
1284
|
+
function coveredFrameEdges(rect, rootRect, tolerance) {
|
|
1285
|
+
const top = Math.max(rect.top, rootRect.top);
|
|
1286
|
+
const bottom = Math.min(rect.bottom, rootRect.bottom);
|
|
1287
|
+
const left = Math.max(rect.left, rootRect.left);
|
|
1288
|
+
const right = Math.min(rect.right, rootRect.right);
|
|
1289
|
+
if (bottom - top < CANVAS_EDGE_BAND_PX || right - left < CANVAS_EDGE_BAND_PX) return [];
|
|
1290
|
+
const band = CANVAS_EDGE_BAND_PX;
|
|
1291
|
+
const rows = { y: top, height: bottom - top, width: band, vertical: true };
|
|
1292
|
+
const cols = { x: left, width: right - left, height: band, vertical: false };
|
|
1293
|
+
const edges = [];
|
|
1294
|
+
if (rect.left <= rootRect.left + tolerance) edges.push({ side: "left", x: left, ...rows });
|
|
1295
|
+
if (rect.right >= rootRect.right - tolerance)
|
|
1296
|
+
edges.push({ side: "right", x: right - band, ...rows });
|
|
1297
|
+
if (rect.top <= rootRect.top + tolerance) edges.push({ side: "top", y: top, ...cols });
|
|
1298
|
+
if (rect.bottom >= rootRect.bottom - tolerance)
|
|
1299
|
+
edges.push({ side: "bottom", y: bottom - band, ...cols });
|
|
1300
|
+
return edges;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
// Draw the band into a scratch canvas (never getContext on the film's own canvas, which would
|
|
1304
|
+
// claim it as 2D). A WebGL canvas without preserveDrawingBuffer reads blank: no finding.
|
|
1305
|
+
function canvasEdgeBand(canvas, rect, edge) {
|
|
1306
|
+
const scaleX = canvas.width / rect.width;
|
|
1307
|
+
const scaleY = canvas.height / rect.height;
|
|
1308
|
+
const off = document.createElement("canvas");
|
|
1309
|
+
off.width = Math.max(1, Math.round(edge.width * scaleX));
|
|
1310
|
+
off.height = Math.max(1, Math.round(edge.height * scaleY));
|
|
1311
|
+
const ctx = off.getContext("2d");
|
|
1312
|
+
if (!ctx) return null;
|
|
1313
|
+
const sx = Math.max(0, Math.floor((edge.x - rect.left) * scaleX));
|
|
1314
|
+
const sy = Math.max(0, Math.floor((edge.y - rect.top) * scaleY));
|
|
1315
|
+
ctx.drawImage(canvas, sx, sy, off.width, off.height, 0, 0, off.width, off.height);
|
|
1316
|
+
return {
|
|
1317
|
+
data: ctx.getImageData(0, 0, off.width, off.height).data,
|
|
1318
|
+
width: off.width,
|
|
1319
|
+
height: off.height,
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
// Collapse the band across its thickness (brightest wins), then count steps along the edge:
|
|
1324
|
+
// luma over black plus alpha, compared two pixels back so anti-aliased glyph edges still step.
|
|
1325
|
+
function sharpSteps(band, vertical) {
|
|
1326
|
+
const length = vertical ? band.height : band.width;
|
|
1327
|
+
const thickness = vertical ? band.width : band.height;
|
|
1328
|
+
const levels = [];
|
|
1329
|
+
for (let along = 0; along < length; along++) {
|
|
1330
|
+
let luma = 0;
|
|
1331
|
+
let alpha = 0;
|
|
1332
|
+
for (let across = 0; across < thickness; across++) {
|
|
1333
|
+
const i = 4 * (vertical ? along * band.width + across : across * band.width + along);
|
|
1334
|
+
const a = band.data[i + 3];
|
|
1335
|
+
const l =
|
|
1336
|
+
(0.299 * band.data[i] + 0.587 * band.data[i + 1] + 0.114 * band.data[i + 2]) * (a / 255);
|
|
1337
|
+
luma = Math.max(luma, l);
|
|
1338
|
+
alpha = Math.max(alpha, a);
|
|
1339
|
+
}
|
|
1340
|
+
levels.push([luma, alpha]);
|
|
1341
|
+
}
|
|
1342
|
+
let steps = 0;
|
|
1343
|
+
for (let i = 2; i < levels.length; i++) {
|
|
1344
|
+
const [luma, alpha] = levels[i];
|
|
1345
|
+
const [backLuma, backAlpha] = levels[i - 2];
|
|
1346
|
+
if (Math.max(Math.abs(luma - backLuma), Math.abs(alpha - backAlpha)) < CANVAS_EDGE_STEP)
|
|
1347
|
+
continue;
|
|
1348
|
+
steps++;
|
|
1349
|
+
i += 2;
|
|
1350
|
+
}
|
|
1351
|
+
return steps;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
function canvasEdgeHasContent(canvas, rect, edge) {
|
|
1355
|
+
try {
|
|
1356
|
+
const band = canvasEdgeBand(canvas, rect, edge);
|
|
1357
|
+
return !!band && sharpSteps(band, edge.vertical) >= CANVAS_EDGE_MIN_STEPS;
|
|
1358
|
+
} catch {
|
|
1359
|
+
return false;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
// ponytail: maps through the bounding box, exact for scale and translate; a rotated or skewed
|
|
1364
|
+
// canvas reads the wrong pixels. Add a matrix inverse if that bites.
|
|
1365
|
+
function canvasEdgeIssues(root, rootRect, time, tolerance) {
|
|
1366
|
+
const issues = [];
|
|
1367
|
+
for (const canvas of Array.from(root.querySelectorAll("canvas"))) {
|
|
1368
|
+
if (!isVisibleElement(canvas) || hasAllowOverflowFlag(canvas)) continue;
|
|
1369
|
+
if (!canvas.width || !canvas.height) continue;
|
|
1370
|
+
const rect = toRect(canvas.getBoundingClientRect());
|
|
1371
|
+
if (!rect.width || !rect.height) continue;
|
|
1372
|
+
const sides = coveredFrameEdges(rect, rootRect, tolerance)
|
|
1373
|
+
.filter((edge) => canvasEdgeHasContent(canvas, rect, edge))
|
|
1374
|
+
.map((edge) => edge.side);
|
|
1375
|
+
if (sides.length === 0) continue;
|
|
1376
|
+
issues.push({
|
|
1377
|
+
code: "canvas_content_at_edge",
|
|
1378
|
+
severity: "warning",
|
|
1379
|
+
time,
|
|
1380
|
+
selector: selectorFor(canvas),
|
|
1381
|
+
containerSelector: selectorFor(root),
|
|
1382
|
+
message: `Canvas content touches the frame edge (${sides.join(", ")}).`,
|
|
1383
|
+
rect,
|
|
1384
|
+
containerRect: rootRect,
|
|
1385
|
+
fixHint:
|
|
1386
|
+
"Keep drawn text inside the visible part of the canvas, or mark intentional full-bleed art with data-layout-allow-overflow.",
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1389
|
+
return issues;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1277
1392
|
// Soft prior only — the counterfactual attach test (below) is what makes detachment a finding.
|
|
1278
1393
|
const CONNECTOR_NAME = /\b(conn(ector)?|arrow|edge|link|flow|wire)\b/i;
|
|
1279
1394
|
const CONNECTOR_SKIP_CONTAINERS = "defs, marker, clipPath, mask, symbol, pattern";
|
|
@@ -1646,6 +1761,7 @@
|
|
|
1646
1761
|
const escaped = escapedContainerIssues(root, time);
|
|
1647
1762
|
issues.push(...escaped.issues);
|
|
1648
1763
|
issues.push(...panelOutOfCanvasIssues(root, rootRect, time, tolerance, escaped.flagged));
|
|
1764
|
+
issues.push(...canvasEdgeIssues(root, rootRect, time, tolerance));
|
|
1649
1765
|
issues.push(...connectorDetachmentIssues(root, rootRect, time));
|
|
1650
1766
|
issues.push(...connectorOrphanIssues(root, rootRect, time));
|
|
1651
1767
|
return issues;
|