@geml/geml 1.7.8 → 1.8.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/LICENSE +21 -21
- package/README.md +285 -285
- package/codemap/adapters/crg.mjs +120 -120
- package/codemap/adapters/joern.mjs +131 -131
- package/codemap/adapters/scip.mjs +658 -658
- package/codemap/browser-stub.mjs +34 -34
- package/codemap/build.mjs +629 -629
- package/codemap/cross-stack.mjs +303 -303
- package/codemap/detect.mjs +399 -399
- package/codemap/emit.mjs +510 -510
- package/codemap/entries.mjs +129 -129
- package/codemap/exclude.mjs +56 -56
- package/codemap/find.mjs +49 -49
- package/codemap/foldings.mjs +110 -110
- package/codemap/joern-export.sc +83 -83
- package/codemap/mcp-server.mjs +434 -431
- package/codemap/normalize.mjs +275 -275
- package/codemap/recipe-trust.mjs +103 -103
- package/codemap/refresh.mjs +313 -313
- package/codemap/render-all.mjs +90 -90
- package/codemap/serve.mjs +585 -585
- package/codemap/sfc-virtualize.mjs +367 -367
- package/codemap/verify.mjs +158 -158
- package/dist/cli.js +121 -121
- package/dist/diagnostics.d.ts +1 -1
- package/dist/diagnostics.js +2 -1
- package/dist/geml.js +45 -24
- package/dist/inline.d.ts +7 -1
- package/dist/inline.js +203 -137
- package/dist/mcp.js +32 -26
- package/dist/render-html.js +35 -35
- package/dist/render.js +157 -157
- package/package.json +1 -1
- package/skill/SKILL.md +167 -167
- package/skill/references/authoring.geml +365 -365
package/dist/render-html.js
CHANGED
|
@@ -36,43 +36,43 @@ function page(title, body, ctx, source) {
|
|
|
36
36
|
? `<script type="importmap">{"imports":{"node:fs":"${lg}_node-stub.js","node:path":"${lg}_node-stub.js","node:crypto":"${lg}_node-stub.js","node:url":"${lg}_node-stub.js","node:child_process":"${lg}_node-stub.js","node:os":"${lg}_node-stub.js"}}</script>\n`
|
|
37
37
|
: "";
|
|
38
38
|
const liveJs = wantLive
|
|
39
|
-
? `<script type="module">
|
|
40
|
-
globalThis.process ??= { argv: [], env: {} };
|
|
41
|
-
const { parse } = await import("${lg}geml.js");
|
|
42
|
-
const { codeGraphWaves } = await import("${lg}render.js");
|
|
43
|
-
const w = codeGraphWaves(async (rel) => {
|
|
44
|
-
try { const r = await fetch(rel, { cache: "no-cache" }); return r.ok ? await r.text() : null; } catch { return null; }
|
|
45
|
-
}, parse);
|
|
46
|
-
for (const m of document.querySelectorAll(".cg-mount[data-start]")) {
|
|
47
|
-
const start = m.getAttribute("data-start");
|
|
48
|
-
m._cgView = async (view) => {
|
|
49
|
-
// A directed view builds from the node's OWN document (its meta names the
|
|
50
|
-
// module and graph-depth); {doc} opens that document; else the mount's.
|
|
51
|
-
const src = view && view.doc ? view.doc
|
|
52
|
-
: view && view.node ? view.node.slice(0, view.node.lastIndexOf("#"))
|
|
53
|
-
: start;
|
|
54
|
-
const r = await w.build(src, view && view.doc ? undefined : view);
|
|
55
|
-
return r.error !== undefined ? null : r.data;
|
|
56
|
-
};
|
|
57
|
-
}
|
|
39
|
+
? `<script type="module">
|
|
40
|
+
globalThis.process ??= { argv: [], env: {} };
|
|
41
|
+
const { parse } = await import("${lg}geml.js");
|
|
42
|
+
const { codeGraphWaves } = await import("${lg}render.js");
|
|
43
|
+
const w = codeGraphWaves(async (rel) => {
|
|
44
|
+
try { const r = await fetch(rel, { cache: "no-cache" }); return r.ok ? await r.text() : null; } catch { return null; }
|
|
45
|
+
}, parse);
|
|
46
|
+
for (const m of document.querySelectorAll(".cg-mount[data-start]")) {
|
|
47
|
+
const start = m.getAttribute("data-start");
|
|
48
|
+
m._cgView = async (view) => {
|
|
49
|
+
// A directed view builds from the node's OWN document (its meta names the
|
|
50
|
+
// module and graph-depth); {doc} opens that document; else the mount's.
|
|
51
|
+
const src = view && view.doc ? view.doc
|
|
52
|
+
: view && view.node ? view.node.slice(0, view.node.lastIndexOf("#"))
|
|
53
|
+
: start;
|
|
54
|
+
const r = await w.build(src, view && view.doc ? undefined : view);
|
|
55
|
+
return r.error !== undefined ? null : r.data;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
58
|
</script>\n`
|
|
59
59
|
: "";
|
|
60
|
-
return `<!doctype html>
|
|
61
|
-
<html lang="en">
|
|
62
|
-
<head>
|
|
63
|
-
<meta charset="utf-8">
|
|
64
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
65
|
-
<title>${esc(title)}</title>
|
|
66
|
-
<style>${CSS}</style>
|
|
67
|
-
${importMap}${mathHead}${mermaidHead}</head>
|
|
68
|
-
<body>
|
|
69
|
-
<main>
|
|
70
|
-
${body}
|
|
71
|
-
</main>
|
|
72
|
-
${footer}
|
|
73
|
-
<script>${JS}</script>
|
|
74
|
-
${ctx.usedCodeGraph ? `<script>${CODE_GRAPH_JS}</script>\n` : ""}${liveJs}</body>
|
|
75
|
-
</html>
|
|
60
|
+
return `<!doctype html>
|
|
61
|
+
<html lang="en">
|
|
62
|
+
<head>
|
|
63
|
+
<meta charset="utf-8">
|
|
64
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
65
|
+
<title>${esc(title)}</title>
|
|
66
|
+
<style>${CSS}</style>
|
|
67
|
+
${importMap}${mathHead}${mermaidHead}</head>
|
|
68
|
+
<body>
|
|
69
|
+
<main>
|
|
70
|
+
${body}
|
|
71
|
+
</main>
|
|
72
|
+
${footer}
|
|
73
|
+
<script>${JS}</script>
|
|
74
|
+
${ctx.usedCodeGraph ? `<script>${CODE_GRAPH_JS}</script>\n` : ""}${liveJs}</body>
|
|
75
|
+
</html>
|
|
76
76
|
`;
|
|
77
77
|
}
|
|
78
78
|
export function renderHtml(doc, opts = {}) {
|
package/dist/render.js
CHANGED
|
@@ -1273,164 +1273,164 @@ function trunc(s, n) {
|
|
|
1273
1273
|
// ---------------------------------------------------------------------------
|
|
1274
1274
|
// Page shell, inline CSS, inline interactivity JS
|
|
1275
1275
|
// ---------------------------------------------------------------------------
|
|
1276
|
-
export const CSS = `
|
|
1277
|
-
:root { --fg:#1f2328; --muted:#656d76; --bd:#d0d7de; --bg:#fff; --accent:#2563eb; --code-bg:#f6f8fa; }
|
|
1278
|
-
* { box-sizing: border-box; }
|
|
1279
|
-
body { margin:0; color:var(--fg); background:#fafbfc; font:16px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,"PingFang SC","Microsoft Yahei",sans-serif; }
|
|
1280
|
-
main { max-width: 860px; margin: 0 auto; padding: 48px 24px 96px; background:var(--bg); }
|
|
1281
|
-
h1,h2,h3,h4,h5,h6 { line-height:1.25; margin:1.6em 0 .6em; scroll-margin-top:16px; }
|
|
1282
|
-
h1 { font-size:2em; border-bottom:1px solid var(--bd); padding-bottom:.3em; }
|
|
1283
|
-
h2 { font-size:1.5em; border-bottom:1px solid var(--bd); padding-bottom:.3em; }
|
|
1284
|
-
h3 { font-size:1.25em; } h4 { font-size:1em; }
|
|
1285
|
-
p { margin:.7em 0; }
|
|
1286
|
-
a { color:var(--accent); text-decoration:none; } a:hover { text-decoration:underline; }
|
|
1287
|
-
code { background:var(--code-bg); padding:.15em .35em; border-radius:6px; font:.88em ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; }
|
|
1288
|
-
pre { background:var(--code-bg); padding:14px 16px; border-radius:8px; overflow:auto; }
|
|
1289
|
-
pre code { background:none; padding:0; font-size:.85em; }
|
|
1290
|
-
pre.output { background:#0d1117; color:#e6edf3; }
|
|
1291
|
-
pre.output code { color:inherit; }
|
|
1292
|
-
ul,ol { padding-left:1.6em; } li { margin:.2em 0; }
|
|
1293
|
-
ul.task-list { list-style:none; padding-left:.2em; }
|
|
1294
|
-
li.task input[type=checkbox] { appearance:none; -webkit-appearance:none; width:1.1em; height:1.1em; margin:0 .5em 0 0; vertical-align:-.2em; border:1.5px solid #c8ccd0; border-radius:4px; background:#fff; position:relative; opacity:1; cursor:default; box-sizing:border-box; }
|
|
1295
|
-
li.task input[type=checkbox]:checked { background-color:#1f883d; border-color:#1f883d; }
|
|
1296
|
-
li.task input[type=checkbox]:checked::after { content:"✓"; position:absolute; top:0; right:0; bottom:0; left:0; display:flex; align-items:center; justify-content:center; color:#fff; font-size:.8em; line-height:1; font-weight:700; }
|
|
1297
|
-
aside.callout { border-left:4px solid var(--accent); background:#f0f6ff; padding:.4em 16px; border-radius:0 8px 8px 0; margin:1em 0; }
|
|
1298
|
-
aside.aside { border-left-color:#8b949e; background:#f6f8fa; }
|
|
1299
|
-
aside.warning { border-left-color:#d97706; background:#fff8f0; }
|
|
1300
|
-
aside.callout > :first-child { margin-top:0; } aside.callout > :last-child { margin-bottom:0; }
|
|
1301
|
-
figure { margin:1.2em 0; }
|
|
1302
|
-
figcaption { color:var(--muted); font-size:.86em; text-align:center; margin-top:.5em; }
|
|
1303
|
-
table.geml-table { border-collapse:collapse; width:100%; font-size:.92em; }
|
|
1304
|
-
table.geml-table th, table.geml-table td { border:1px solid var(--bd); padding:6px 12px; }
|
|
1305
|
-
table.geml-table thead th { background:var(--code-bg); cursor:pointer; user-select:none; white-space:nowrap; }
|
|
1306
|
-
table.geml-table thead th::after { content:" \\2195"; color:var(--muted); font-size:.8em; }
|
|
1307
|
-
table.geml-table thead th.asc::after { content:" \\2191"; color:var(--accent); }
|
|
1308
|
-
table.geml-table thead th.desc::after { content:" \\2193"; color:var(--accent); }
|
|
1309
|
-
table.geml-table tbody tr:nth-child(2n) { background:#fafbfc; }
|
|
1310
|
-
table.geml-table td.computed { color:#0a7c52; }
|
|
1311
|
-
table.geml-table tfoot td { background:var(--code-bg); font-weight:600; border-top:2px solid var(--bd); }
|
|
1312
|
-
.table-tools { margin-bottom:6px; } .table-filter { width:240px; max-width:100%; padding:5px 9px; border:1px solid var(--bd); border-radius:7px; font-size:.85em; }
|
|
1313
|
-
/* A table wider than the column scrolls INSIDE its own box — the page itself
|
|
1314
|
-
must never scroll sideways. Only the table is in here: the filter box above
|
|
1315
|
-
and the caption below stay put instead of sliding out of view with it. */
|
|
1316
|
-
.table-scroll { overflow-x:auto; max-width:100%; }
|
|
1317
|
-
.table-figure details > summary { cursor:pointer; color:var(--muted); font-size:.86em; padding:4px 0; }
|
|
1318
|
-
.table-note { color:var(--muted); font-size:.82em; margin:6px 0 0; }
|
|
1319
|
-
.geml-chart { width:100%; height:auto; background:var(--bg); border:1px solid var(--bd); border-radius:8px; }
|
|
1320
|
-
.c-title { font-size:15px; font-weight:600; fill:var(--fg); }
|
|
1321
|
-
.c-grid { stroke:#eaecef; } .c-axis { stroke:#aab1b8; } .c-tick { font-size:11px; fill:var(--muted); } .c-legend { font-size:12px; fill:var(--fg); }
|
|
1322
|
-
.media { max-width:100%; border-radius:8px; }
|
|
1323
|
-
.diagram-src { color:var(--muted); } .render-error { color:#cf222e; }
|
|
1324
|
-
.math-block { overflow-x:auto; padding:.4em 0; }
|
|
1325
|
-
sup.fn a { font-size:.75em; }
|
|
1326
|
-
.geml-footer { max-width:860px; margin:0 auto; padding:16px 24px 40px; color:var(--muted); font-size:.82em; }
|
|
1327
|
-
.geml-footer code { font-size:.95em; }
|
|
1328
|
-
.code-graph { margin:1.4em 0; }
|
|
1329
|
-
/* The graph is the widest artifact on the page: let it break out of main's
|
|
1330
|
-
860px reading column and take the viewport, centred, leaving the prose
|
|
1331
|
-
around it untouched. Negative inline margins, NOT transform — a transform
|
|
1332
|
-
would become the containing block for the fullscreen overlay below. */
|
|
1333
|
-
@media (min-width:900px) { .code-graph { margin-inline: calc((100% - min(96vw, 1600px)) / 2); } }
|
|
1334
|
-
.cg-mount { border:1px solid var(--bd); border-radius:8px; padding:10px 12px; background:var(--bg); }
|
|
1335
|
-
.cg-scroll { overflow:auto; min-height:60vh; max-height:84vh; }
|
|
1336
|
-
.cg-svg { display:block; }
|
|
1337
|
-
/* Fullscreen: one class drives the layout, whether the native Fullscreen API
|
|
1338
|
-
took (mount is in the top layer) or we fell back to a fixed overlay. The
|
|
1339
|
-
:fullscreen pseudo-class is deliberately NOT in these selectors — a browser
|
|
1340
|
-
that doesn't know it would drop the whole rule; the runtime keeps the class
|
|
1341
|
-
in sync with fullscreenchange instead. Toolbar/legend keep their height,
|
|
1342
|
-
the stage eats the rest. */
|
|
1343
|
-
.cg-mount.cg-full { position:fixed; inset:0; z-index:9999; margin:0; border:0; border-radius:0; padding:10px 14px; background:var(--bg); display:flex; flex-direction:column; }
|
|
1344
|
-
.cg-mount.cg-full > .cg-bar, .cg-mount.cg-full > .cg-legend, .cg-mount.cg-full > .cg-groups { flex:0 0 auto; }
|
|
1345
|
-
.cg-mount.cg-full .cg-stage { flex:1 1 auto; min-height:0; }
|
|
1346
|
-
.cg-mount.cg-full .cg-scroll { min-height:0; max-height:none; height:100%; }
|
|
1347
|
-
.cg-mount.cg-full .cg-src-body { max-height:none; }
|
|
1348
|
-
.cg-mount.cg-full .cg-frame { flex:1 1 auto; height:auto; }
|
|
1349
|
-
.cg-search-wrap { position:relative; display:inline-block; }
|
|
1350
|
-
.cg-search { font:12px/1.4 inherit; padding:2px 7px; border:1px solid var(--bd); border-radius:4px; background:var(--bg); color:var(--fg); min-width:13ch; }
|
|
1351
|
-
.cg-search-menu { position:absolute; z-index:30; top:calc(100% + 2px); left:0; min-width:24ch; max-width:52ch; max-height:52vh; overflow:auto; background:var(--bg); border:1px solid var(--bd); border-radius:6px; box-shadow:0 6px 20px rgba(0,0,0,.18); }
|
|
1352
|
-
.cg-search-row { display:block; width:100%; text-align:left; padding:4px 9px 4px 18px; border:0; background:none; color:var(--fg); cursor:pointer; font:12px/1.4 inherit; }
|
|
1353
|
-
.cg-search-row:hover { background:var(--bd); }
|
|
1354
|
-
.cg-search-count { position:sticky; top:0; padding:4px 9px; font-size:11px; opacity:.65; background:var(--bg); border-bottom:1px solid var(--bd); }
|
|
1355
|
-
.cg-search-grp { padding:6px 9px 2px; font-size:11px; font-weight:600; opacity:.7; border-top:1px solid var(--bd); }
|
|
1356
|
-
.cg-search-grp:first-of-type { border-top:0; }
|
|
1357
|
-
.cg-stage { display:flex; gap:10px; align-items:flex-start; }
|
|
1358
|
-
.cg-stage .cg-scroll { flex:1 1 auto; min-width:0; }
|
|
1359
|
-
.cg-src { flex:0 0 42%; max-width:46%; display:flex; flex-direction:column; border:1px solid var(--bd); border-radius:6px; overflow:hidden; background:var(--bg); }
|
|
1360
|
-
.cg-src-hd { display:flex; gap:8px; align-items:center; justify-content:space-between; padding:4px 8px; border-bottom:1px solid var(--bd); color:var(--muted); font:.76em ui-monospace,Consolas,monospace; word-break:break-all; }
|
|
1361
|
-
.cg-src-hd button { font:inherit; border:1px solid var(--bd); border-radius:5px; background:transparent; color:var(--muted); cursor:pointer; padding:0 6px; }
|
|
1362
|
-
.cg-src-body { margin:0; padding:8px 10px; overflow:auto; max-height:84vh; color:var(--fg); font:12px/1.5 ui-monospace,Consolas,monospace; white-space:pre; }
|
|
1363
|
-
.cg-src-note { color:var(--muted); font-style:italic; white-space:pre-wrap; }
|
|
1364
|
-
.cg-bar { display:flex; gap:8px; align-items:center; flex-wrap:wrap; font-size:.82em; color:var(--muted); margin-bottom:6px; }
|
|
1365
|
-
.cg-bar button { font:inherit; padding:1px 8px; border:1px solid var(--bd); border-radius:5px; background:transparent; cursor:pointer; }
|
|
1366
|
-
.cg-crumb .cg-seg { border:0; border-radius:0; padding:0; background:none; color:var(--accent); cursor:pointer; font:inherit; }
|
|
1367
|
-
.cg-crumb .cg-seg:hover { text-decoration:underline; }
|
|
1368
|
-
.cg-frame { display:block; width:100%; height:84vh; border:0; background:var(--bg); }
|
|
1369
|
-
.cg-flash { color:#b42318; }
|
|
1370
|
-
.cg-legend { display:flex; gap:14px; align-items:center; justify-content:space-between; flex-wrap:wrap; font-size:.75em; color:var(--muted); margin-top:6px; }
|
|
1371
|
-
.cg-upbtn { cursor:pointer; }
|
|
1372
|
-
.cg-upbtn circle { fill:#fff; stroke:#94a3b8; }
|
|
1373
|
-
.cg-upbtn text { font-size:11px; fill:#57606a; }
|
|
1374
|
-
.cg-upbtn:hover circle { stroke:var(--accent); stroke-width:1.6; }
|
|
1375
|
-
.cg-upbtn:hover text { fill:var(--accent); }
|
|
1376
|
-
.cg-uplink { fill:none; stroke:#94a3b8; stroke-dasharray:3 2.5; pointer-events:none; }
|
|
1377
|
-
.cg-groups { display:flex; flex-wrap:wrap; gap:4px 12px; margin-top:6px; font-size:.75em; color:var(--muted); }
|
|
1378
|
-
.cg-chip { display:inline-flex; align-items:center; gap:4px; }
|
|
1379
|
-
.cg-chip i { width:10px; height:10px; border-radius:2px; border:1px solid #94a3b8; display:inline-block; }
|
|
1380
|
-
.cg-note { font-size:.8em; color:#9a6700; }
|
|
1381
|
-
.cg-n rect { fill:#eef2f7; stroke:#94a3b8; }
|
|
1382
|
-
.cg-n text { font-size:12px; fill:var(--fg); font-family:ui-monospace,Consolas,monospace; }
|
|
1383
|
-
.cg-n { cursor:pointer; }
|
|
1384
|
-
.cg-n.root rect { fill:#dbeafe; stroke:#2563eb; stroke-width:2; }
|
|
1385
|
-
.cg-n.leaf { opacity:.45; }
|
|
1386
|
-
.cg-n.test rect { stroke-dasharray:3 2; }
|
|
1387
|
-
.cg-n.grp rect { stroke-width:1.8; }
|
|
1388
|
-
.cg-e { fill:none; stroke:#94a3b8; stroke-width:.9; }
|
|
1389
|
-
.cg-e.cand { stroke-dasharray:2 3; }
|
|
1390
|
-
.cg-e.back { stroke:#dc2626; stroke-dasharray:5 3; }
|
|
1391
|
-
.cg-e.http { stroke:#0891b2; stroke-width:1.5; stroke-dasharray:5 2; } /* cross-stack API link */
|
|
1392
|
-
.cg-e.soft { opacity:.55; }
|
|
1393
|
-
.cg-svg.hl .cg-n { opacity:.22; }
|
|
1394
|
-
.cg-svg.hl .cg-e { opacity:.1; }
|
|
1395
|
-
.cg-svg.hl .cg-n.hl { opacity:1; }
|
|
1396
|
-
.cg-svg.hl .cg-e.hl { opacity:1; stroke-width:1.6; }
|
|
1276
|
+
export const CSS = `
|
|
1277
|
+
:root { --fg:#1f2328; --muted:#656d76; --bd:#d0d7de; --bg:#fff; --accent:#2563eb; --code-bg:#f6f8fa; }
|
|
1278
|
+
* { box-sizing: border-box; }
|
|
1279
|
+
body { margin:0; color:var(--fg); background:#fafbfc; font:16px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,"PingFang SC","Microsoft Yahei",sans-serif; }
|
|
1280
|
+
main { max-width: 860px; margin: 0 auto; padding: 48px 24px 96px; background:var(--bg); }
|
|
1281
|
+
h1,h2,h3,h4,h5,h6 { line-height:1.25; margin:1.6em 0 .6em; scroll-margin-top:16px; }
|
|
1282
|
+
h1 { font-size:2em; border-bottom:1px solid var(--bd); padding-bottom:.3em; }
|
|
1283
|
+
h2 { font-size:1.5em; border-bottom:1px solid var(--bd); padding-bottom:.3em; }
|
|
1284
|
+
h3 { font-size:1.25em; } h4 { font-size:1em; }
|
|
1285
|
+
p { margin:.7em 0; }
|
|
1286
|
+
a { color:var(--accent); text-decoration:none; } a:hover { text-decoration:underline; }
|
|
1287
|
+
code { background:var(--code-bg); padding:.15em .35em; border-radius:6px; font:.88em ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; }
|
|
1288
|
+
pre { background:var(--code-bg); padding:14px 16px; border-radius:8px; overflow:auto; }
|
|
1289
|
+
pre code { background:none; padding:0; font-size:.85em; }
|
|
1290
|
+
pre.output { background:#0d1117; color:#e6edf3; }
|
|
1291
|
+
pre.output code { color:inherit; }
|
|
1292
|
+
ul,ol { padding-left:1.6em; } li { margin:.2em 0; }
|
|
1293
|
+
ul.task-list { list-style:none; padding-left:.2em; }
|
|
1294
|
+
li.task input[type=checkbox] { appearance:none; -webkit-appearance:none; width:1.1em; height:1.1em; margin:0 .5em 0 0; vertical-align:-.2em; border:1.5px solid #c8ccd0; border-radius:4px; background:#fff; position:relative; opacity:1; cursor:default; box-sizing:border-box; }
|
|
1295
|
+
li.task input[type=checkbox]:checked { background-color:#1f883d; border-color:#1f883d; }
|
|
1296
|
+
li.task input[type=checkbox]:checked::after { content:"✓"; position:absolute; top:0; right:0; bottom:0; left:0; display:flex; align-items:center; justify-content:center; color:#fff; font-size:.8em; line-height:1; font-weight:700; }
|
|
1297
|
+
aside.callout { border-left:4px solid var(--accent); background:#f0f6ff; padding:.4em 16px; border-radius:0 8px 8px 0; margin:1em 0; }
|
|
1298
|
+
aside.aside { border-left-color:#8b949e; background:#f6f8fa; }
|
|
1299
|
+
aside.warning { border-left-color:#d97706; background:#fff8f0; }
|
|
1300
|
+
aside.callout > :first-child { margin-top:0; } aside.callout > :last-child { margin-bottom:0; }
|
|
1301
|
+
figure { margin:1.2em 0; }
|
|
1302
|
+
figcaption { color:var(--muted); font-size:.86em; text-align:center; margin-top:.5em; }
|
|
1303
|
+
table.geml-table { border-collapse:collapse; width:100%; font-size:.92em; }
|
|
1304
|
+
table.geml-table th, table.geml-table td { border:1px solid var(--bd); padding:6px 12px; }
|
|
1305
|
+
table.geml-table thead th { background:var(--code-bg); cursor:pointer; user-select:none; white-space:nowrap; }
|
|
1306
|
+
table.geml-table thead th::after { content:" \\2195"; color:var(--muted); font-size:.8em; }
|
|
1307
|
+
table.geml-table thead th.asc::after { content:" \\2191"; color:var(--accent); }
|
|
1308
|
+
table.geml-table thead th.desc::after { content:" \\2193"; color:var(--accent); }
|
|
1309
|
+
table.geml-table tbody tr:nth-child(2n) { background:#fafbfc; }
|
|
1310
|
+
table.geml-table td.computed { color:#0a7c52; }
|
|
1311
|
+
table.geml-table tfoot td { background:var(--code-bg); font-weight:600; border-top:2px solid var(--bd); }
|
|
1312
|
+
.table-tools { margin-bottom:6px; } .table-filter { width:240px; max-width:100%; padding:5px 9px; border:1px solid var(--bd); border-radius:7px; font-size:.85em; }
|
|
1313
|
+
/* A table wider than the column scrolls INSIDE its own box — the page itself
|
|
1314
|
+
must never scroll sideways. Only the table is in here: the filter box above
|
|
1315
|
+
and the caption below stay put instead of sliding out of view with it. */
|
|
1316
|
+
.table-scroll { overflow-x:auto; max-width:100%; }
|
|
1317
|
+
.table-figure details > summary { cursor:pointer; color:var(--muted); font-size:.86em; padding:4px 0; }
|
|
1318
|
+
.table-note { color:var(--muted); font-size:.82em; margin:6px 0 0; }
|
|
1319
|
+
.geml-chart { width:100%; height:auto; background:var(--bg); border:1px solid var(--bd); border-radius:8px; }
|
|
1320
|
+
.c-title { font-size:15px; font-weight:600; fill:var(--fg); }
|
|
1321
|
+
.c-grid { stroke:#eaecef; } .c-axis { stroke:#aab1b8; } .c-tick { font-size:11px; fill:var(--muted); } .c-legend { font-size:12px; fill:var(--fg); }
|
|
1322
|
+
.media { max-width:100%; border-radius:8px; }
|
|
1323
|
+
.diagram-src { color:var(--muted); } .render-error { color:#cf222e; }
|
|
1324
|
+
.math-block { overflow-x:auto; padding:.4em 0; }
|
|
1325
|
+
sup.fn a { font-size:.75em; }
|
|
1326
|
+
.geml-footer { max-width:860px; margin:0 auto; padding:16px 24px 40px; color:var(--muted); font-size:.82em; }
|
|
1327
|
+
.geml-footer code { font-size:.95em; }
|
|
1328
|
+
.code-graph { margin:1.4em 0; }
|
|
1329
|
+
/* The graph is the widest artifact on the page: let it break out of main's
|
|
1330
|
+
860px reading column and take the viewport, centred, leaving the prose
|
|
1331
|
+
around it untouched. Negative inline margins, NOT transform — a transform
|
|
1332
|
+
would become the containing block for the fullscreen overlay below. */
|
|
1333
|
+
@media (min-width:900px) { .code-graph { margin-inline: calc((100% - min(96vw, 1600px)) / 2); } }
|
|
1334
|
+
.cg-mount { border:1px solid var(--bd); border-radius:8px; padding:10px 12px; background:var(--bg); }
|
|
1335
|
+
.cg-scroll { overflow:auto; min-height:60vh; max-height:84vh; }
|
|
1336
|
+
.cg-svg { display:block; }
|
|
1337
|
+
/* Fullscreen: one class drives the layout, whether the native Fullscreen API
|
|
1338
|
+
took (mount is in the top layer) or we fell back to a fixed overlay. The
|
|
1339
|
+
:fullscreen pseudo-class is deliberately NOT in these selectors — a browser
|
|
1340
|
+
that doesn't know it would drop the whole rule; the runtime keeps the class
|
|
1341
|
+
in sync with fullscreenchange instead. Toolbar/legend keep their height,
|
|
1342
|
+
the stage eats the rest. */
|
|
1343
|
+
.cg-mount.cg-full { position:fixed; inset:0; z-index:9999; margin:0; border:0; border-radius:0; padding:10px 14px; background:var(--bg); display:flex; flex-direction:column; }
|
|
1344
|
+
.cg-mount.cg-full > .cg-bar, .cg-mount.cg-full > .cg-legend, .cg-mount.cg-full > .cg-groups { flex:0 0 auto; }
|
|
1345
|
+
.cg-mount.cg-full .cg-stage { flex:1 1 auto; min-height:0; }
|
|
1346
|
+
.cg-mount.cg-full .cg-scroll { min-height:0; max-height:none; height:100%; }
|
|
1347
|
+
.cg-mount.cg-full .cg-src-body { max-height:none; }
|
|
1348
|
+
.cg-mount.cg-full .cg-frame { flex:1 1 auto; height:auto; }
|
|
1349
|
+
.cg-search-wrap { position:relative; display:inline-block; }
|
|
1350
|
+
.cg-search { font:12px/1.4 inherit; padding:2px 7px; border:1px solid var(--bd); border-radius:4px; background:var(--bg); color:var(--fg); min-width:13ch; }
|
|
1351
|
+
.cg-search-menu { position:absolute; z-index:30; top:calc(100% + 2px); left:0; min-width:24ch; max-width:52ch; max-height:52vh; overflow:auto; background:var(--bg); border:1px solid var(--bd); border-radius:6px; box-shadow:0 6px 20px rgba(0,0,0,.18); }
|
|
1352
|
+
.cg-search-row { display:block; width:100%; text-align:left; padding:4px 9px 4px 18px; border:0; background:none; color:var(--fg); cursor:pointer; font:12px/1.4 inherit; }
|
|
1353
|
+
.cg-search-row:hover { background:var(--bd); }
|
|
1354
|
+
.cg-search-count { position:sticky; top:0; padding:4px 9px; font-size:11px; opacity:.65; background:var(--bg); border-bottom:1px solid var(--bd); }
|
|
1355
|
+
.cg-search-grp { padding:6px 9px 2px; font-size:11px; font-weight:600; opacity:.7; border-top:1px solid var(--bd); }
|
|
1356
|
+
.cg-search-grp:first-of-type { border-top:0; }
|
|
1357
|
+
.cg-stage { display:flex; gap:10px; align-items:flex-start; }
|
|
1358
|
+
.cg-stage .cg-scroll { flex:1 1 auto; min-width:0; }
|
|
1359
|
+
.cg-src { flex:0 0 42%; max-width:46%; display:flex; flex-direction:column; border:1px solid var(--bd); border-radius:6px; overflow:hidden; background:var(--bg); }
|
|
1360
|
+
.cg-src-hd { display:flex; gap:8px; align-items:center; justify-content:space-between; padding:4px 8px; border-bottom:1px solid var(--bd); color:var(--muted); font:.76em ui-monospace,Consolas,monospace; word-break:break-all; }
|
|
1361
|
+
.cg-src-hd button { font:inherit; border:1px solid var(--bd); border-radius:5px; background:transparent; color:var(--muted); cursor:pointer; padding:0 6px; }
|
|
1362
|
+
.cg-src-body { margin:0; padding:8px 10px; overflow:auto; max-height:84vh; color:var(--fg); font:12px/1.5 ui-monospace,Consolas,monospace; white-space:pre; }
|
|
1363
|
+
.cg-src-note { color:var(--muted); font-style:italic; white-space:pre-wrap; }
|
|
1364
|
+
.cg-bar { display:flex; gap:8px; align-items:center; flex-wrap:wrap; font-size:.82em; color:var(--muted); margin-bottom:6px; }
|
|
1365
|
+
.cg-bar button { font:inherit; padding:1px 8px; border:1px solid var(--bd); border-radius:5px; background:transparent; cursor:pointer; }
|
|
1366
|
+
.cg-crumb .cg-seg { border:0; border-radius:0; padding:0; background:none; color:var(--accent); cursor:pointer; font:inherit; }
|
|
1367
|
+
.cg-crumb .cg-seg:hover { text-decoration:underline; }
|
|
1368
|
+
.cg-frame { display:block; width:100%; height:84vh; border:0; background:var(--bg); }
|
|
1369
|
+
.cg-flash { color:#b42318; }
|
|
1370
|
+
.cg-legend { display:flex; gap:14px; align-items:center; justify-content:space-between; flex-wrap:wrap; font-size:.75em; color:var(--muted); margin-top:6px; }
|
|
1371
|
+
.cg-upbtn { cursor:pointer; }
|
|
1372
|
+
.cg-upbtn circle { fill:#fff; stroke:#94a3b8; }
|
|
1373
|
+
.cg-upbtn text { font-size:11px; fill:#57606a; }
|
|
1374
|
+
.cg-upbtn:hover circle { stroke:var(--accent); stroke-width:1.6; }
|
|
1375
|
+
.cg-upbtn:hover text { fill:var(--accent); }
|
|
1376
|
+
.cg-uplink { fill:none; stroke:#94a3b8; stroke-dasharray:3 2.5; pointer-events:none; }
|
|
1377
|
+
.cg-groups { display:flex; flex-wrap:wrap; gap:4px 12px; margin-top:6px; font-size:.75em; color:var(--muted); }
|
|
1378
|
+
.cg-chip { display:inline-flex; align-items:center; gap:4px; }
|
|
1379
|
+
.cg-chip i { width:10px; height:10px; border-radius:2px; border:1px solid #94a3b8; display:inline-block; }
|
|
1380
|
+
.cg-note { font-size:.8em; color:#9a6700; }
|
|
1381
|
+
.cg-n rect { fill:#eef2f7; stroke:#94a3b8; }
|
|
1382
|
+
.cg-n text { font-size:12px; fill:var(--fg); font-family:ui-monospace,Consolas,monospace; }
|
|
1383
|
+
.cg-n { cursor:pointer; }
|
|
1384
|
+
.cg-n.root rect { fill:#dbeafe; stroke:#2563eb; stroke-width:2; }
|
|
1385
|
+
.cg-n.leaf { opacity:.45; }
|
|
1386
|
+
.cg-n.test rect { stroke-dasharray:3 2; }
|
|
1387
|
+
.cg-n.grp rect { stroke-width:1.8; }
|
|
1388
|
+
.cg-e { fill:none; stroke:#94a3b8; stroke-width:.9; }
|
|
1389
|
+
.cg-e.cand { stroke-dasharray:2 3; }
|
|
1390
|
+
.cg-e.back { stroke:#dc2626; stroke-dasharray:5 3; }
|
|
1391
|
+
.cg-e.http { stroke:#0891b2; stroke-width:1.5; stroke-dasharray:5 2; } /* cross-stack API link */
|
|
1392
|
+
.cg-e.soft { opacity:.55; }
|
|
1393
|
+
.cg-svg.hl .cg-n { opacity:.22; }
|
|
1394
|
+
.cg-svg.hl .cg-e { opacity:.1; }
|
|
1395
|
+
.cg-svg.hl .cg-n.hl { opacity:1; }
|
|
1396
|
+
.cg-svg.hl .cg-e.hl { opacity:1; stroke-width:1.6; }
|
|
1397
1397
|
`;
|
|
1398
|
-
export const JS = `
|
|
1399
|
-
(function () {
|
|
1400
|
-
function cmp(a, b) {
|
|
1401
|
-
var na = a.dataset.sort, nb = b.dataset.sort;
|
|
1402
|
-
if (na !== undefined && nb !== undefined) return parseFloat(na) - parseFloat(nb);
|
|
1403
|
-
return (a.textContent || "").localeCompare(b.textContent || "");
|
|
1404
|
-
}
|
|
1405
|
-
document.querySelectorAll("table.geml-table").forEach(function (table) {
|
|
1406
|
-
var tbody = table.tBodies[0];
|
|
1407
|
-
if (!tbody) return;
|
|
1408
|
-
// Sort on header click.
|
|
1409
|
-
var ths = table.tHead ? table.tHead.rows[0].cells : [];
|
|
1410
|
-
Array.prototype.forEach.call(ths, function (th, col) {
|
|
1411
|
-
th.addEventListener("click", function () {
|
|
1412
|
-
var dir = th.classList.contains("asc") ? "desc" : "asc";
|
|
1413
|
-
Array.prototype.forEach.call(ths, function (h) { h.classList.remove("asc", "desc"); });
|
|
1414
|
-
th.classList.add(dir);
|
|
1415
|
-
var rows = Array.prototype.slice.call(tbody.rows);
|
|
1416
|
-
rows.sort(function (r1, r2) {
|
|
1417
|
-
var c = cmp(r1.cells[col], r2.cells[col]);
|
|
1418
|
-
return dir === "asc" ? c : -c;
|
|
1419
|
-
});
|
|
1420
|
-
rows.forEach(function (r) { tbody.appendChild(r); });
|
|
1421
|
-
});
|
|
1422
|
-
});
|
|
1423
|
-
// Filter rows.
|
|
1424
|
-
var fig = table.closest(".table-figure");
|
|
1425
|
-
var input = fig ? fig.querySelector(".table-filter") : null;
|
|
1426
|
-
if (input) input.addEventListener("input", function () {
|
|
1427
|
-
var q = input.value.toLowerCase();
|
|
1428
|
-
Array.prototype.forEach.call(tbody.rows, function (r) {
|
|
1429
|
-
r.style.display = (r.textContent || "").toLowerCase().indexOf(q) >= 0 ? "" : "none";
|
|
1430
|
-
});
|
|
1431
|
-
});
|
|
1432
|
-
});
|
|
1433
|
-
})();
|
|
1398
|
+
export const JS = `
|
|
1399
|
+
(function () {
|
|
1400
|
+
function cmp(a, b) {
|
|
1401
|
+
var na = a.dataset.sort, nb = b.dataset.sort;
|
|
1402
|
+
if (na !== undefined && nb !== undefined) return parseFloat(na) - parseFloat(nb);
|
|
1403
|
+
return (a.textContent || "").localeCompare(b.textContent || "");
|
|
1404
|
+
}
|
|
1405
|
+
document.querySelectorAll("table.geml-table").forEach(function (table) {
|
|
1406
|
+
var tbody = table.tBodies[0];
|
|
1407
|
+
if (!tbody) return;
|
|
1408
|
+
// Sort on header click.
|
|
1409
|
+
var ths = table.tHead ? table.tHead.rows[0].cells : [];
|
|
1410
|
+
Array.prototype.forEach.call(ths, function (th, col) {
|
|
1411
|
+
th.addEventListener("click", function () {
|
|
1412
|
+
var dir = th.classList.contains("asc") ? "desc" : "asc";
|
|
1413
|
+
Array.prototype.forEach.call(ths, function (h) { h.classList.remove("asc", "desc"); });
|
|
1414
|
+
th.classList.add(dir);
|
|
1415
|
+
var rows = Array.prototype.slice.call(tbody.rows);
|
|
1416
|
+
rows.sort(function (r1, r2) {
|
|
1417
|
+
var c = cmp(r1.cells[col], r2.cells[col]);
|
|
1418
|
+
return dir === "asc" ? c : -c;
|
|
1419
|
+
});
|
|
1420
|
+
rows.forEach(function (r) { tbody.appendChild(r); });
|
|
1421
|
+
});
|
|
1422
|
+
});
|
|
1423
|
+
// Filter rows.
|
|
1424
|
+
var fig = table.closest(".table-figure");
|
|
1425
|
+
var input = fig ? fig.querySelector(".table-filter") : null;
|
|
1426
|
+
if (input) input.addEventListener("input", function () {
|
|
1427
|
+
var q = input.value.toLowerCase();
|
|
1428
|
+
Array.prototype.forEach.call(tbody.rows, function (r) {
|
|
1429
|
+
r.style.display = (r.textContent || "").toLowerCase().indexOf(q) >= 0 ? "" : "none";
|
|
1430
|
+
});
|
|
1431
|
+
});
|
|
1432
|
+
});
|
|
1433
|
+
})();
|
|
1434
1434
|
`;
|
|
1435
1435
|
// geml-code-graph runtime: layered layout AT DRAW TIME (GEP-0003 / v2-D8) so
|
|
1436
1436
|
// clicking a node re-roots the view inside the embedded slice. Algorithm as
|