@nodegx/mcp 0.2.0 → 0.2.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.
- package/dist/backend/cli.js +182 -182
- package/dist/cloud-bundle.cjs +747 -745
- package/dist/kit-extract.cjs +228 -228
- package/dist/kit-extract.inputs.json +113 -113
- package/dist/noodl-mcp.cjs +1705 -918
- package/dist/package.json +1 -1
- package/dist/render-harness/data/node-catalog-enriched.json +242 -9
- package/dist/render-harness/drive-steps.js +156 -16
- package/dist/render-harness/measure-from-disk.js +112 -2
- package/dist/render-harness/render-from-disk.js +136 -136
- package/dist/src/external/viewer/noodl.viewer.js +1 -1
- package/dist/starter-assets/noodl_modules/inter/Inter-Bold.woff2 +0 -0
- package/dist/starter-assets/noodl_modules/inter/Inter-Medium.woff2 +0 -0
- package/dist/starter-assets/noodl_modules/inter/Inter-Regular.woff2 +0 -0
- package/dist/starter-assets/noodl_modules/inter/Inter-SemiBold.woff2 +0 -0
- package/dist/starter-assets/noodl_modules/inter/LICENSE.txt +93 -0
- package/dist/starter-assets/noodl_modules/inter/manifest.json +7 -0
- package/dist/starter-assets/noodl_modules/inter/styles.css +49 -0
- package/dist/starter-assets/noodl_modules/lucide-icons/lucide.woff2 +0 -0
- package/dist/starter-assets/noodl_modules/lucide-icons/manifest.json +229 -0
- package/dist/starter-assets/noodl_modules/lucide-icons/styles.css +2007 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/LICENCES.json +445 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/animal-cat.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/animal-dog.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/animal-horse.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/animal-sheep.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/avatar-1.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/avatar-2.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/avatar-3.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/avatar-4.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/avatar-5.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/avatar-6.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/food-bakery.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/food-board.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/food-bread.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/food-carrots.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/food-grocer.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/food-market.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/food-plate.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/ground-aurora.svg +28 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/ground-canyon.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/ground-city-dusk.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/ground-coast.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/ground-dune.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/ground-forest-fog.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/ground-red-rock.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/ground-ridge.svg +28 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/ground-shore.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/ground-skyline.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/manifest.json +4 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/people-cafe.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/people-coffee-shop.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/people-desk.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/people-market.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/people-meeting.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/portrait.svg +11 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/texture-brick.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/texture-coffee.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/texture-earth.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/texture-pebbles.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/texture-snow.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/texture-soil.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/tile-1.svg +17 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/tile-2.svg +17 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/tile-3.svg +17 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/work-baker.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/work-carpenter.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/work-chef.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/work-hands-clay.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/work-leather-bench.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/work-machine-shop.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/work-potter.webp +0 -0
- package/dist/starter-assets/noodl_modules/starter-imagery/work-welder.webp +0 -0
- package/package.json +4 -3
|
@@ -492,6 +492,78 @@ var require_src = __commonJS({
|
|
|
492
492
|
// viewport height with content laid out past it is clipped; a page that is
|
|
493
493
|
// simply short has nothing past it. That difference is the whole of
|
|
494
494
|
// \`clipped-page\`, and neither number alone can tell them apart.
|
|
495
|
+
// P112-OWN019-LAPTOPGAPS (R39) \u2014 where the INK is, not where the boxes are. Richard on RL1's laptop page: a
|
|
496
|
+
// feature number in a Columns "1 4" row had a fifth of the page to itself, ~215 px of nothing before its title;
|
|
497
|
+
// L1's pricing toggle and price sat ~260 px left of their centred heading. Every box there is well-formed \u2014 the
|
|
498
|
+
// number's column box abuts the title's \u2014 so the measure is each child's ink: the union of its shown text runs
|
|
499
|
+
// (a Range, so a full-width text block counts only as far as its words reach) and its painted boxes. Desktop only:
|
|
500
|
+
// that is where a row has room to open a hole. Raw numbers here; the judgement is summarise's.
|
|
501
|
+
const layout = { rowGaps: [], drifts: [] };
|
|
502
|
+
// (The API guard is for the fake DOMs the unit suites hand this expression; every browser has both.)
|
|
503
|
+
if (vw >= 1024 && typeof document.createRange === 'function' && typeof document.createTreeWalker === 'function') {
|
|
504
|
+
const shown = new Set(visible);
|
|
505
|
+
const textInk = [];
|
|
506
|
+
const range = document.createRange();
|
|
507
|
+
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
|
|
508
|
+
for (let n = walker.nextNode(); n; n = walker.nextNode()) {
|
|
509
|
+
if (!n.textContent.trim() || !shown.has(n.parentElement)) continue;
|
|
510
|
+
range.selectNodeContents(n);
|
|
511
|
+
const r = range.getBoundingClientRect();
|
|
512
|
+
if (r.width > 0 && r.height > 0) textInk.push({ node: n, r });
|
|
513
|
+
}
|
|
514
|
+
const PAINTED_TAGS = ['IMG', 'SVG', 'CANVAS', 'VIDEO', 'INPUT', 'TEXTAREA', 'SELECT', 'BUTTON', 'svg'];
|
|
515
|
+
const paintedInk = visible
|
|
516
|
+
.filter((el) => {
|
|
517
|
+
if (PAINTED_TAGS.includes(el.tagName)) return true;
|
|
518
|
+
const cs = styleOf(el);
|
|
519
|
+
const bg = cs.backgroundColor;
|
|
520
|
+
return (bg && bg !== 'transparent' && !/rgba\\([^)]*,\\s*0\\)$/.test(bg)) || cs.backgroundImage !== 'none' || parseFloat(cs.borderTopWidth) > 0;
|
|
521
|
+
})
|
|
522
|
+
.map((el) => ({ el, r: el.getBoundingClientRect() }))
|
|
523
|
+
.filter((p) => p.r.width > 0 && p.r.height > 0 && p.r.width < vw * 0.98);
|
|
524
|
+
const inkOf = (el) => {
|
|
525
|
+
let l = Infinity, t = Infinity, rr = -Infinity, b = -Infinity, text = '';
|
|
526
|
+
const take = (r) => { l = Math.min(l, r.left); t = Math.min(t, r.top); rr = Math.max(rr, r.right); b = Math.max(b, r.bottom); };
|
|
527
|
+
for (const x of textInk) if (el.contains(x.node)) { take(x.r); if (!text) text = x.node.textContent.trim().slice(0, 50); }
|
|
528
|
+
for (const p of paintedInk) if (el === p.el || el.contains(p.el)) take(p.r);
|
|
529
|
+
return l === Infinity ? null : { left: l, top: t, right: rr, bottom: b, width: rr - l, height: b - t, text };
|
|
530
|
+
};
|
|
531
|
+
const ROW_GAP_MIN_PX = 160;
|
|
532
|
+
for (const parent of visible) {
|
|
533
|
+
const kids = [...parent.children].filter((k) => shown.has(k));
|
|
534
|
+
if (kids.length < 2) continue;
|
|
535
|
+
const pr = parent.getBoundingClientRect();
|
|
536
|
+
if (pr.width < 600) continue;
|
|
537
|
+
const inks = kids.map(inkOf).filter(Boolean);
|
|
538
|
+
if (inks.length < 2) continue;
|
|
539
|
+
// Side by side: consecutive by left edge, overlapping vertically, the second starting after the first ends.
|
|
540
|
+
const byLeft = [...inks].sort((a, b) => a.left - b.left);
|
|
541
|
+
for (let i = 1; i < byLeft.length; i++) {
|
|
542
|
+
const a = byLeft[i - 1], b = byLeft[i];
|
|
543
|
+
const overlap = Math.min(a.bottom, b.bottom) - Math.max(a.top, b.top);
|
|
544
|
+
const gap = b.left - a.right;
|
|
545
|
+
if (overlap <= 0 || gap < ROW_GAP_MIN_PX) continue;
|
|
546
|
+
layout.rowGaps.push({ cls: cls(parent), gap: round(gap), rowWidth: round(pr.width), tallest: round(Math.max(a.height, b.height)), left: a.text, right: b.text });
|
|
547
|
+
}
|
|
548
|
+
// Stacked, each starting below the one before, and the parts of ONE section: every part under 240 px tall (a
|
|
549
|
+
// page whose children are whole sections compared section against section \u2014 measured on L1 and GL1, s5). The
|
|
550
|
+
// section's axis is set by a centred part among its first two (its heading, usually); L1's pricing is just
|
|
551
|
+
// two parts, a centred heading block and a toggle-and-price group ~260 px left of it.
|
|
552
|
+
const byTop = [...inks].sort((a, b) => a.top - b.top);
|
|
553
|
+
if (!byTop.every((k, i) => i === 0 || k.top >= byTop[i - 1].bottom - 2)) continue;
|
|
554
|
+
if (byTop.some((k) => k.height > 240)) continue;
|
|
555
|
+
const centre = pr.left + pr.width / 2;
|
|
556
|
+
const offOf = (k) => k.left + k.width / 2 - centre;
|
|
557
|
+
const centred = byTop.filter((k) => Math.abs(offOf(k)) <= 12 && k.width < pr.width * 0.9);
|
|
558
|
+
if (!centred.some((k) => byTop.indexOf(k) < 2)) continue;
|
|
559
|
+
for (const k of byTop) {
|
|
560
|
+
if (Math.abs(offOf(k)) > 40 && k.width < pr.width * 0.8) {
|
|
561
|
+
layout.drifts.push({ cls: cls(parent), offBy: round(offOf(k)), sectionWidth: round(pr.width), text: k.text, centredText: centred[0].text });
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
495
567
|
const contentBottom = visible.reduce((lowest, el) => {
|
|
496
568
|
const r = el.getBoundingClientRect();
|
|
497
569
|
return r.width > 0 && r.height > 0 && r.bottom > lowest ? r.bottom : lowest;
|
|
@@ -499,6 +571,7 @@ var require_src = __commonJS({
|
|
|
499
571
|
|
|
500
572
|
return {
|
|
501
573
|
lists,
|
|
574
|
+
layout,
|
|
502
575
|
// FLD-012 AC3 \u2014 the SIZE of the population every rule below is computed over, and the number of
|
|
503
576
|
// elements the visibility correction took out of it. Reported directly because a rule's finding
|
|
504
577
|
// count can move for several reasons at once, and 'the fix changed what is measured' is a
|
|
@@ -637,9 +710,19 @@ var require_src = __commonJS({
|
|
|
637
710
|
*/
|
|
638
711
|
SingleGround: "single-ground",
|
|
639
712
|
NoImagery: "no-imagery",
|
|
640
|
-
NoDisplayType: "no-display-type"
|
|
713
|
+
NoDisplayType: "no-display-type",
|
|
714
|
+
/**
|
|
715
|
+
* P112-OWN019-LAPTOPGAPS (R39, Richard 2026-10-14). On a laptop, two things side by side in one row with a hole
|
|
716
|
+
* between their ink — RL1's feature number given a fifth of the page by a Columns "1 4" row, L1's text and card
|
|
717
|
+
* pushed to the row's two edges. A warning: a hole can be meant, and the screenshots are the arbiter.
|
|
718
|
+
*/
|
|
719
|
+
RowGap: "row-gap",
|
|
720
|
+
/** P112-OWN019-LAPTOPGAPS — a section that centres its heading and leaves another part off that centre (L1's price). */
|
|
721
|
+
AlignmentDrift: "alignment-drift"
|
|
641
722
|
};
|
|
642
723
|
var SEVERITY_ORDER = { error: 0, warning: 1, info: 2 };
|
|
724
|
+
var ROW_GAP_MIN_TALLEST = 48;
|
|
725
|
+
var ROW_GAP_MIN_SHARE = 0.12;
|
|
643
726
|
var POVERTY_FINDINGS = [RenderFinding.SingleGround, RenderFinding.NoImagery, RenderFinding.NoDisplayType];
|
|
644
727
|
function isPovertyFinding(finding) {
|
|
645
728
|
return Boolean(finding) && POVERTY_FINDINGS.indexOf(finding.code) !== -1;
|
|
@@ -855,6 +938,32 @@ var require_src = __commonJS({
|
|
|
855
938
|
evidence: v.grounds.values
|
|
856
939
|
});
|
|
857
940
|
}
|
|
941
|
+
if (isDesktop && v.layout) {
|
|
942
|
+
const gaps = (v.layout.rowGaps || []).filter((g) => g.tallest >= ROW_GAP_MIN_TALLEST && g.gap >= g.rowWidth * ROW_GAP_MIN_SHARE);
|
|
943
|
+
const seen2 = /* @__PURE__ */ new Set();
|
|
944
|
+
for (const g of gaps) {
|
|
945
|
+
const key = `${g.left}|${g.right}`;
|
|
946
|
+
if (seen2.has(key) || seen2.size >= 3) continue;
|
|
947
|
+
seen2.add(key);
|
|
948
|
+
add({
|
|
949
|
+
code: RenderFinding.RowGap,
|
|
950
|
+
severity: "warning",
|
|
951
|
+
viewport: name,
|
|
952
|
+
message: `"${g.left}" and "${g.right}" sit side by side with ${g.gap}px of empty space between them (${Math.round(g.gap / g.rowWidth * 100)}% of the ${g.rowWidth}px row). A label or number beside text reads as one thing at its own width with a small gap \u2014 a row Group, the label at its natural width and the text growing \u2014 not a column given a share of the page (Columns widths are proportions); two parts pushed to a wide row's edges leave the same hole: give the row a max width and a gap. Look at this width's screenshot.`,
|
|
953
|
+
evidence: g
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
const drifts = (v.layout.drifts || []).slice(0, 3);
|
|
957
|
+
for (const d of drifts) {
|
|
958
|
+
add({
|
|
959
|
+
code: RenderFinding.AlignmentDrift,
|
|
960
|
+
severity: "warning",
|
|
961
|
+
viewport: name,
|
|
962
|
+
message: `This section centres "${d.centredText}", but "${d.text}" sits ${Math.abs(d.offBy)}px ${d.offBy < 0 ? "left" : "right"} of that centre. One section, one alignment: centre the rest too, or align everything to the same left edge. Look at this width's screenshot.`,
|
|
963
|
+
evidence: d
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
}
|
|
858
967
|
if (v.consoleErrors && v.consoleErrors.length) {
|
|
859
968
|
add({
|
|
860
969
|
code: RenderFinding.ConsoleError,
|
|
@@ -964,6 +1073,7 @@ var require_render_report = __commonJS({
|
|
|
964
1073
|
var WS_MODULE = HARNESS_PATHS.WS_MODULE.path;
|
|
965
1074
|
var {
|
|
966
1075
|
DEFAULT_VIEWPORTS,
|
|
1076
|
+
NAMED_VIEWPORTS,
|
|
967
1077
|
DESKTOP_WIDTH,
|
|
968
1078
|
RenderFinding,
|
|
969
1079
|
SEVERITY_ORDER,
|
|
@@ -1499,7 +1609,7 @@ var require_render_report = __commonJS({
|
|
|
1499
1609
|
}
|
|
1500
1610
|
function parseViewports2(spec) {
|
|
1501
1611
|
if (!spec) return DEFAULT_VIEWPORTS;
|
|
1502
|
-
const known = new Map(
|
|
1612
|
+
const known = new Map(NAMED_VIEWPORTS.map((v) => [v.name, v]));
|
|
1503
1613
|
return spec.split(",").map((token) => {
|
|
1504
1614
|
const trimmed = token.trim();
|
|
1505
1615
|
if (known.has(trimmed)) return known.get(trimmed);
|