Mermaid diagrams in markdown are drawn at build time by Pele 1.6.0 and land in the page as inline SVG, coloured by the stylesheet's tokens through --pele-*, so they follow light and dark mode. No diagram script ships any more: mermaid and its plumbing are gone.
- Labels are measured with the sans font's own widths (
lib/), not Pele's Helvetica table, so they fit.text-widths.sh - The SVG passes a sanitizer of its own that keeps only what Pele draws with; scenario
diagram-hostilefreezes what hostile diagram text can do. - A workaround for Pele reporting syntax error lines in its preprocessed text (
errorLine, scenariodiagram-error-line), to drop when Pele fixes it.
| 1/3 | Draw mermaid diagrams at build time, with Pele | +465 | -1297 | ✓ |
| 2/3 | Draw a wide diagram again for phones | +34 | -8 | ✓ |
| 3/3 | Name the line a diagram's syntax error is on | +56 | -2 | ✓ |
revisions2
| r2 | cat | d08b3da | what changed since r1 | |
| r1 | cat | d0e7b0b |
No comments yet: forge comment 15 adds one.
1/3 Draw mermaid diagrams at build time, with Pele
A ```mermaid fence in markdown was a <pre> that ui.ts filled in the browser, loading mermaid and picking a light or dark theme once. Now the build draws it as inline SVG with Pele 1.6.0 (pinned), and pages ship no diagram script: mermaid, its asset, the data-mermaid attribute and CI's check for it are gone. style.css maps Pele's --pele-* variables to the page's tokens (nodes on --raised outlined in --border-strong, square corners, the sans and mono, bold at --fw-strong, the chart and syntax colours for series), so a diagram follows light and dark mode without being drawn again. Pele sizes nodes by measuring their labels: with a canvas in a browser, and at build time with a table of Helvetica's widths. The labels are set in the page's sans, so diagrams.ts gives it a measurer with that font's widths instead (regular, and bold at 650), made by lib/text-widths.sh; the mono is 0.6em, as Pele assumes. Over 174 labels of the diagram types in a browser, its widths are within 0.2% of Open Sans as drawn; Pele's own table is off by up to 14%. Markdown is anyone's who can push a repo. Pele escapes a diagram's text, drops HTML in labels, ignores init directives' CSS and fonts, and keeps links to safe schemes. Its SVG then goes through a sanitizer of its own, which keeps only the tags, attributes and style properties Pele draws with, no images and no url(), and Pele's own classes; raw SVG in the markdown is still dropped. The markdown's sanitizer sees a placeholder named by a random token where each diagram goes. A diagram that doesn't parse shows its source and Pele's reason, and the page builds. The demo wiki has a diagram, and CI checks it is drawn; link previews leave diagrams' labels out of their text.
| M | .forge/ | 4 | +- |
| M | README.md | 15 | +- |
| M | demo/ | 14 | + |
| M | guide/ | 2 | +- |
| M | lib/ | 6 | +- |
| A | lib/ | 111 | ++ |
| M | lib/ | 9 | +- |
| M | lib/ | 29 | +- |
| A | lib/ | 32 | + |
| M | package-lock.json | 1375 | +++--------------------------- |
| M | package.json | 2 | +- |
| M | site/ | 2 | +- |
| M | site/ | 14 | +- |
| M | site/ | 17 | +- |
| A | test/ | 35 | + |
| A | test/ | 58 | + |
| A | test/ | 37 | + |
@@ -24,6 +24,8 @@ www=demo/root/.forge/www
2424 grep -q 'class="hljs-title class_"' $www/dami/greet/blob/main/web/greet.js/index.html2525 grep -q 'style="--w:4".*class="hljs-' $www/dami/greet/reviews/7/index.html2626 grep -q '<code class="language-go">resp, err := http.Get(<span class="hljs-string">' $www/dami/greet/blob/main/docs/examples.md/index.html27+# A mermaid diagram in the wiki, drawn at build time.28+grep -q '<figure class="diagram"><svg xmlns="http://www.w3.org/2000/svg" class="pele pele-flowchart"' $www/dami/greet/wiki/Design/index.html2729 grep -q '"reviews":\[\[7,"Regional notes"' $www/dami/greet/search.json2830 grep -q '"dami/greet","say hello, with flags"' $www/repos.json2931 grep -q 'data-search="/dami/greet/search.json"' $www/dami/greet/index.html@@ -39,7 +41,7 @@ for f in events builds reviews ci; do test "$(head -c 4 $www/insights/$f.parquet
3941 tmp=$(mktemp -d)4042 FORGE_ROOT=$PWD/demo/root FORGE_SSH=git@forge.local FORGE_CACHE=$tmp FORGE_STRICT=1 node lib/render-page.ts check $www4143 rm -rf "$tmp"42-for a in $(grep -rhoE '(href|src|data-mermaid)="/assets/[^"]+"' $www --include=index.html | sed -E 's/^[a-z-]+="//; s/"$//' | sort -u); do44+for a in $(grep -rhoE '(href|src)="/assets/[^"]+"' $www --include=index.html | sed -E 's/^[a-z-]+="//; s/"$//' | sort -u); do4345 test -f "$www$a" || { echo "missing asset $a"; exit 1; }4446 done4547 grep -q '<meta property="og:title" content="#3 Usage and --help - dami/greet">' $www/dami/greet/reviews/3/index.html@@ -704,6 +704,19 @@ files are cached in `.forge/cache/highlight/` by blob id, so pushing a new
704704 branch doesn't redo files that other branches already have. Token colors are705705 CSS variables (`--tok-*`) in `style.css`.706706 707+Mermaid diagrams in markdown (` ```mermaid ` fences) are drawn at build708+time too, as inline SVG, by [Pele](https://pele.run), so pages ship no709+diagram script. `style.css` colours them through Pele's `--pele-*`710+variables, which follow light and dark mode without drawing them again.711+There is no browser at build time to measure text in, so labels are sized712+from a table of the sans font's character widths in `lib/diagrams.ts`713+(`lib/text-widths.sh` makes it from the font). A diagram that doesn't parse714+shows its source and the reason instead. Markdown is anyone's who can push715+a repo: Pele escapes a diagram's text and filters its links and styles,716+then the SVG goes through a sanitizer of its own that keeps only the717+shapes, text and links Pele draws with (no images, no `url()`), while raw718+SVG in the markdown itself is still dropped.719+707720 ## The lake and insights708721 709722 The forge's facts are append-only, but they don't last: `events.jsonl` is@@ -1332,7 +1345,7 @@ rendered, `/assets/<hash>.css`, shared by pages of a kind. Import
13321345 `macros.njk` with context, or blocks in macros don't reach the bundle.13331346 13341347 Assets ship under content-hashed names (`/assets/style.<hash>.css`,1335-`ui.<hash>.js`, mermaid, sqlite-wasm), so browsers can cache them for good;1348+`ui.<hash>.js`, sqlite-wasm), so browsers can cache them for good;13361349 the stylesheet's `url(./fonts/open-sans.woff2)` becomes the font's hashed name. The same13371350 sources give the same names, and pages' fingerprints include them, so13381351 unchanged pages are still hard-linked. A build keeps the files of the last@@ -131,6 +131,20 @@ cat >wiki/Design.md <<'EOF'
131131 - Flags are parsed by hand; no getopt.132132 - Output goes to stdout, errors to stderr.133133 134+How a greeting is made:135+136+```mermaid137+flowchart LR138+ args[command line] --> name{--name given?}139+ name -->|yes| who[that name]140+ name -->|no| world[world]141+ who --> greet[hello, name]142+ world --> greet143+ greet --> shout{--shout?}144+ shout -->|yes| upper[UPPERCASE] --> out[stdout]145+ shout -->|no| out146+```147+134148 Back to [Home](Home.md).135149 EOF136150 commit "add a wiki"@@ -237,7 +237,7 @@ deleted 1788292812 dami <span class="dim"># only o
237237 <header><p class="ref"><b>08</b> hacking</p><h2 id="hacking-h"><a href="#hacking">Working on forge</a></h2></header>238238 <div class="cheat">239239 <section aria-labelledby="hk-run"><h3 id="hk-run">run it</h3><dl>240-<div><dt>npm install</dt><dd>11ty, markdown-it, sanitize-html, mermaid; TypeScript only for checking.</dd></div>240+<div><dt>npm install</dt><dd>11ty, markdown-it, sanitize-html, Pele; TypeScript only for checking.</dd></div>241241 <div><dt>sh demo/make-demo.sh</dt><dd>A throwaway forge in <code>demo/root</code>: two users, stacked reviews with revisions, CI that fails and passes, a wiki, sites and previews, all through the real hooks over file:// pushes.</dd></div>242242 <div><dt>node demo/serve.ts 4817</dt><dd>Serves it with live updates and cache headers, the way Caddy and the events server do.</dd></div>243243 <div><dt>npm run dev</dt><dd>Serves the demo from <code>_site/</code> on :8080 and rebuilds it, reloading the browser, when templates, assets or <code>lib/</code> change. It never writes to <code>demo/root</code>.</dd></div>@@ -1,7 +1,7 @@
11 // Assets under content-hashed names, /assets/style.<hash>.css, so browsers22 // can cache them forever: the stylesheet, the logo, the fonts (in fonts/,33 // shipped next to the rest), the vendored scripts, our TypeScript with its4-// types stripped, mermaid, sqlite-wasm (its script and4+// types stripped, sqlite-wasm (its script and55 // its .wasm, for code search), and @simplewebauthn/browser (passkeys, on66 // the sign-in page). 11ty's bundle plugin77 // writes the templates' own CSS next to them, also named by a hash.@@ -19,12 +19,10 @@ import { fileURLToPath } from 'node:url';
1919 const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');2020 const src = path.join(root, 'site', 'assets');2121 const fonts = path.join(src, 'fonts');22-const MERMAID = path.join(root, 'node_modules', 'mermaid', 'dist', 'mermaid.min.js');2322 const SQLITE = path.join(root, 'node_modules', '@sqlite.org', 'sqlite-wasm', 'dist');2423 const WEBAUTHN = path.join(root, 'node_modules', '@simplewebauthn', 'browser', 'dist', 'bundle', 'index.umd.min.js');2524 // Files from node_modules: short name, the name they ship as, the source.2625 const VENDOR: [string, string, string][] = [27- ['mermaid', 'mermaid.js', MERMAID],2826 // The browser build, an ES module that codesearch.ts imports in its worker.2927 ['sqlite3', 'sqlite3.js', path.join(SQLITE, 'index.mjs')],3028 ['sqlite3wasm', 'sqlite3.wasm', path.join(SQLITE, 'sqlite3.wasm')],@@ -46,7 +44,7 @@ export interface Asset {
4644 4745 // By short name: style, logo, open-sans, noto-sans-mono, noto-serif,4846 // noto-serif-italic, fixi, ssexi, diffs, keys, live,49-// search, query, codesearch, login, ui, mermaid, sqlite3, sqlite3wasm, webauthn.47+// search, query, codesearch, login, ui, sqlite3, sqlite3wasm, webauthn.5048 export type Assets = Record<string, Asset>;5149 5250 let memo: { key: string; assets: Assets } | null = null;@@ -0,0 +1,111 @@
1+// Mermaid diagrams, drawn at build time: a ```mermaid fence in markdown2+// becomes inline SVG (Pele, https://pele.run) that the stylesheet colours3+// through its --pele-* variables, so it follows light and dark with no4+// script. A diagram that doesn't parse stays its source, with a note.5+//6+// The markdown is anyone's who can push a repo. Pele escapes the text in a7+// diagram and filters its links and styles; then the SVG goes through a8+// sanitizer of its own that keeps only what Pele draws with, apart from9+// the markdown's, whose raw HTML still can't have SVG.10+import { createHash } from 'node:crypto';11+import { PeleError, render, type TextMeasurer } from 'pele';12+import sanitize from 'sanitize-html';13+14+// Text widths of the sans (Open Sans), regular and bold (--fw-strong,15+// which style.css sets diagrams' bold in), in thousandths of an em:16+// U+0020-007E then U+00A0-017F, two base-36 digits each, made by17+// lib/text-widths.sh. Pele measures labels to size nodes, with a canvas in18+// a browser; at build time there is none, and its own table is Helvetica's.19+const REGULAR = '787cb2hyfwmzk9638787fbfw778y7ba7fwfwfwfwfwfwfwfwfwfw7b7bfwfwfwc0owhkhyhik6fgeck7kh7r7hh0eiozkxlmgqlmh5f8fbk9gkpng2fjfw93a793fwc67pfgh0dbh0fm9cf3h17070el70pqh1gqh0h0bdd99wh1dwljejdxd1aff9affw787cfwfwfwfwf9eag4n49tdsfw8yn4dwbwfw9o9o7ph6i77b669oaedskklclmc0hkhkhkhkhkhko4hifgfgfgfg7r7r7r7rk6kxlmlmlmlmlmfwlmk9k9k9k9fjgqhbfgfgfgfgfgfgnydbfmfmfmfm70707070goh1gqgqgqgqgqfwgqh1h1h1h1dxh0dxhkfghkfghkfghidbhidbhidbhidbk6h0k6h1fgfmfgfmfgfmfgfmfgfmk7f3k7f3k7f3k7f3khh1khh17r707r707r707r707r70f8e17h70h0elelei70ei70ei70ei79ei70kxh1kxh1kxh1iskxh1lmgqlmgqlmgqppqch5bdh5bdh5bdf8d9f8d9f8d9f8d9fb9wfb9wfb9wk9h1k9h1k9h1k9h1k9h1k9h1pnljfjdxfjfwd1fwd1fwd18z';20+const BOLD = '787tcmhyfwoikp729494f6fw7r8y7sb5fwfwfwfwfwfwfwfwfwfw7s7sfwfwfwcyoxisiihnkhfjf1k5l28u8ri3fepwm6m0ham0i1fafwkthpqkhxgwg296b596fwbm9hgghfe1hfg7affkhy8484gl84qwhyh3hfhfcbdobjhyfcn8fofcdfatfaatfw787tfwfwfwfwfadpgon4afg9fw8yn4dwbwfwabab9hi2i77s5tabapg9mgnkn1cyisisisisisispvhnfjfjfjfj8u8u8u8ukhm6m0m0m0m0m0fwm0ktktktktgwhaj5ggggggggggggp3e1g7g7g7g784848484h2hyh3h3h3h3h3fwh3hyhyhyhyfchffcisggisggisgghne1hne1hne1hne1khhfkhhsfjg7fjg7fjg7fjg7fjg7k5fkk5fkk5fkk5fkl2hyl2hy8u848u848u848u848u84hmg78r84i3glglfe84fe84fe84fe9dfe84m6hym6hym6hykrm6hym0h3m0h3m0h3qpqzi1cbi1cbi1cbfadofadofadofadofwbjfwbjfwbjkthykthykthykthykthykthyqkn8gwfcgwg2dfg2dfg2dfa8';21+const table = (s: string): Map<number, number> => {22+ const codes = [...Array(0x7f - 0x20).keys()].map((i) => i + 0x20).concat([...Array(0x180 - 0xa0).keys()].map((i) => i + 0xa0));23+ return new Map(codes.map((c, i) => [c, parseInt(s.slice(i * 2, i * 2 + 2), 36)]));24+};25+const widths = [table(REGULAR), table(BOLD)];26+// Characters the sans lacks come from other fonts, as in Pele's own table:27+// marks and zero-width spaces take no room, East Asian wide ones an em,28+// the rest a guess. The mono (Noto Sans Mono) is 0.6em throughout.29+const ZERO = /[̀-ͯ-︀-️]/;30+const WIDE = /[ᄀ-ᅟ⺀-가-힣豈-︰-﹏-⦆¢-₩]|[^\0-]/u;31+const measurer: TextMeasurer = {32+ width(text, size, style) {33+ const mono = (style & 4) !== 0, w = widths[style & 1];34+ let sum = 0;35+ for (const ch of text) {36+ const c = ch.codePointAt(0)!;37+ if (c < 0x20 || ZERO.test(ch)) continue;38+ sum += WIDE.test(ch) ? (mono ? 1200 : 1000) : mono ? 600 : (w.get(c) ?? 580);39+ }40+ return (sum * size) / 1000;41+ },42+};43+44+// What Pele draws with, and no more: shapes, text, links. No images, no45+// foreign objects, no <style>, no use or xlink references; inline styles only46+// with these properties, and no url() in them or in any attribute.47+const PRESENTATION = ['class', 'style', 'transform', 'fill', 'fill-opacity', 'stroke', 'stroke-width', 'stroke-dasharray', 'stroke-linecap', 'stroke-linejoin', 'opacity'];48+const SHAPE = [...PRESENTATION, 'x', 'y', 'width', 'height', 'rx', 'ry', 'cx', 'cy', 'r', 'd', 'points'];49+const TEXT = [...PRESENTATION, 'x', 'y', 'font-size', 'font-weight', 'font-style', 'font-family', 'text-anchor', 'xml:space'];50+const SAFE = /^(?![\s\S]*url\s*\()[-\w\s#%.,()+*/"']*$/i;51+const STYLES = [52+ '--_a', '--_b', '--_bg', '--_c', '--_fg', '--_fm', '--_hw', '--_l', '--_m', '--_r', '--_s', '--_tw', '--_w',53+ 'max-width', 'height', 'color', 'direction', 'font', 'font-family', 'font-size', 'font-style', 'font-weight',54+ 'letter-spacing', 'word-spacing', 'text-transform', 'text-decoration', 'fill', 'stroke', 'stroke-width',55+ 'stroke-dasharray', 'opacity', 'rx',56+];57+const SVG: sanitize.IOptions = {58+ allowedTags: ['svg', 'g', 'path', 'rect', 'circle', 'ellipse', 'polygon', 'text', 'tspan', 'title', 'desc', 'a'],59+ allowedAttributes: {60+ svg: [...SHAPE, 'xmlns', 'viewBox', 'preserveAspectRatio', 'role', 'aria-roledescription', 'aria-labelledby', 'aria-describedby', 'aria-hidden'],61+ g: [...TEXT],62+ path: SHAPE, rect: SHAPE, circle: SHAPE, ellipse: SHAPE, polygon: SHAPE,63+ text: TEXT, tspan: TEXT,64+ title: ['id'], desc: ['id'],65+ a: ['href', 'rel', 'target'],66+ },67+ // Pele's own classes; a diagram's classDef names aren't kept, so they68+ // can't pick up the page's styles.69+ allowedClasses: { '*': [/^pele(-[\w-]+)?$/, 'clickable'] },70+ allowedStyles: { '*': Object.fromEntries(STYLES.map((p) => [p, [SAFE]])) },71+ allowedSchemes: ['http', 'https', 'mailto'],72+ allowProtocolRelative: false,73+ // SVG's attribute names have capitals (viewBox); keep them as Pele wrote them.74+ parser: { lowerCaseAttributeNames: false },75+};76+77+const escape = (s: string): string => s.replace(/[&<>"]/g, (c) => `&${{ '&': 'amp', '<': 'lt', '>': 'gt', '"': 'quot' }[c]};`);78+79+// One diagram's HTML. `href` rewrites a link in it, as the markdown's own80+// links are (relative ones resolve to forge pages).81+export function diagram(src: string, href: (url: string) => string = (u) => u): string {82+ // Ids (for the title and description) from the source, so a page that83+ // didn't change builds the same.84+ const idPrefix = 'diagram-' + createHash('sha256').update(src).digest('hex').slice(0, 8);85+ const options = { measurer, images: false, linkSchemes: ['http', 'https', 'mailto'] };86+ let svg;87+ try {88+ svg = render(src, { ...options, idPrefix }).svg;89+ } catch (e) {90+ // Pele's message names the line, and can quote it: shown as text.91+ let why = 'Pele failed on it';92+ if (e instanceof PeleError) why = e.message.split('\n')[0].replace(/[.:]$/, '').slice(0, 160);93+ return `<figure class="diagram-source"><pre><code class="language-mermaid">${escape(src)}</code></pre><figcaption>Not drawn: ${escape(why)}.</figcaption></figure>\n`;94+ }95+ const clean = sanitize(svg, {96+ ...SVG,97+ // Every tag: no url() in any attribute; a link's href rewritten. (A98+ // transform for '*' replaces one for the tag, so it is all here.)99+ transformTags: {100+ '*': (tag, attribs) => ({101+ tagName: tag,102+ attribs: Object.fromEntries(103+ Object.entries(attribs)104+ .filter(([, v]) => !/url\s*\(/i.test(v))105+ .map(([k, v]) => [k, tag === 'a' && k === 'href' ? href(v) : v]),106+ ),107+ }),108+ },109+ });110+ return `<figure class="diagram">${clean}</figure>\n`;111+}@@ -167,8 +167,8 @@ export function loadForge(env: NodeJS.ProcessEnv = process.env): Forge {
167167 // fingerprint matches the last build (and whose file still exists) isn't168168 // rendered; its file is hard-linked from the live site instead.169169 const live = env.FORGE_LIVE_WWW || null;170- // Pages link assets by hashed name, so a new name (mermaid or Node updated,171- // say) must re-render them too.170+ // Pages link assets by hashed name, so a new name (sqlite-wasm or Node171+ // updated, say) must re-render them too.172172 const code = fingerprint(codeVersion(), Object.values(assets()).map((a) => a.file));173173 const skipped: string[] = [];174174 const pageCaches: Forge['pageCaches'] = [];@@ -556,10 +556,11 @@ export const filters = {
556556 },557557 // A page's description, for link previews: one line of plain text, cut at558558 // a word near n characters. With html, the text of rendered markdown559- // (markdown-it puts a newline between blocks, so tags can just go).559+ // (markdown-it puts a newline between blocks, so tags can just go), less560+ // its diagrams, whose labels are no sentence.560561 excerpt(s: string | null | undefined, html = false, n = 200): string {561562 let text = String(s ?? '');562- if (html) text = decode(text.replace(/<[^>]*>/g, ''));563+ if (html) text = decode(text.replace(/<figure class="diagram">.*?<\/figure>/gs, '').replace(/<[^>]*>/g, ''));563564 text = text.replace(/\s+/g, ' ').trim();564565 if (text.length <= n) return text;565566 const cut = text.lastIndexOf(' ', n);@@ -2,10 +2,12 @@
22 //33 // Raw HTML is allowed through a sanitizer (the common README idioms:44 // centered paragraphs, sized images, <details>, <picture>), never scripts,5-// styles or event handlers. Relative links and images resolve to forge pages6-// and raw files, in markdown and in raw HTML alike.5+// styles, event handlers or SVG. Relative links and images resolve to forge6+// pages and raw files, in markdown and in raw HTML alike.7+import { randomUUID } from 'node:crypto';78 import MarkdownIt from 'markdown-it';89 import sanitize from 'sanitize-html';10+import { diagram } from './diagrams.ts';911 import { highlight, resolve } from './highlight.ts';1012 1113 type Token = ReturnType<MarkdownIt['parse']>[number];@@ -15,12 +17,20 @@ const md = new MarkdownIt({ html: true, linkify: true, typographer: false, highl
1517 // "NOTICE.md" is not a website, even though .md is a TLD.1618 md.linkify.set({ fuzzyLink: false });1719 18-// ```mermaid blocks become diagrams in the browser (site/assets/diagrams.ts).20+// ```mermaid blocks become SVG diagrams (lib/diagrams.ts), sanitized on21+// their own. In their place the markdown's HTML has a placeholder, which22+// names a random token the source can't know, until it has been sanitized.23+interface Env {24+ token: string;25+ diagrams: string[];26+ href: (url: string) => string;27+}1928 const fence = md.renderer.rules.fence!;20-md.renderer.rules.fence = (tokens, idx, options, env, self) => {29+md.renderer.rules.fence = (tokens, idx, options, env: Env, self) => {2130 const t = tokens[idx];22- if (t.info.trim() === 'mermaid') return `<pre class="mermaid">${md.utils.escapeHtml(t.content)}</pre>\n`;23- return fence(tokens, idx, options, env, self);31+ if (t.info.trim() !== 'mermaid') return fence(tokens, idx, options, env, self);32+ env.diagrams.push(diagram(t.content, env.href));33+ return `<div>${env.token}:${env.diagrams.length - 1}</div>\n`;2434 };2535 2636 const isRelative = (u: string | null | undefined): u is string => !!u && !/^([a-z][a-z0-9+.-]*:|\/\/|#|\/)/i.test(u);@@ -61,13 +71,13 @@ const ALLOWED = {
6171 input: ['type', 'checked', 'disabled'],6272 },6373 // highlight.js tokens: "hljs-title" plus sub-scopes like "function_".64- allowedClasses: { '*': ['mermaid', /^language-/], span: [/^hljs-/, /^[a-z]+_+$/] },74+ allowedClasses: { '*': [/^language-/], span: [/^hljs-/, /^[a-z]+_+$/] },6575 allowedSchemes: ['http', 'https', 'mailto'],6676 allowProtocolRelative: false,6777 };6878 6979 export function renderMarkdown(src: string, { link = (u) => u, image = (u) => u }: LinkResolver = {}): string {70- const env = {};80+ const env: Env = { token: randomUUID(), diagrams: [], href: (u) => (isRelative(u) ? link(u) : u) };7181 const tokens = md.parse(src, env);7282 const slug = slugger();7383 const walk = (list: Token[]): void => {@@ -90,7 +100,7 @@ export function renderMarkdown(src: string, { link = (u) => u, image = (u) => u
90100 });91101 const rendered = md.renderer.render(tokens, md.options, env);92102 // Raw HTML in the source (img, a, source) resolves the same way markdown does.93- return sanitize(rendered, {103+ const html = sanitize(rendered, {94104 ...ALLOWED,95105 transformTags: {96106 img: (tag, attribs) => ({ tagName: tag, attribs: { ...attribs, src: isRelative(attribs.src) ? image(attribs.src) : attribs.src, loading: 'lazy', decoding: 'async' } }),@@ -104,6 +114,7 @@ export function renderMarkdown(src: string, { link = (u) => u, image = (u) => u
104114 }),105115 },106116 });117+ return env.diagrams.length ? html.replace(new RegExp(`<div>${env.token}:(\\d+)</div>`, 'g'), (_, i: string) => env.diagrams[Number(i)]) : html;107118 }108119 109120 // Resolve a relative link against the directory of the file it appears in.@@ -0,0 +1,32 @@
1+2+# How the width tables in lib/diagrams.ts were made; the forge never runs3+# this. Diagrams are drawn at build time, where there is no browser to4+# measure text in, so a node's size comes from these: the advance width of5+# each character the sans font has, regular (400) and bold (650, --fw-strong,6+# which diagrams' bold text and titles are set in), in thousandths of an7+# em. Run it again when the font changes, in the venv of8+# site/assets/fonts/make-fonts.sh:9+#10+# PATH=/tmp/fonts-venv/bin:$PATH sh lib/text-widths.sh site/assets/fonts/open-sans.woff211+#12+# It prints the two tables: for U+0020-007E then U+00A0-017F, each width as13+# two base-36 digits, the way Pele writes its own.14+set -eu15+font=${1:?usage: text-widths.sh <variable sans .woff2>}16+python3 - "$font" <<'EOF'17+import sys18+from fontTools.ttLib import TTFont19+from fontTools.varLib import instancer20+21+codes = list(range(0x20, 0x7F)) + list(range(0xA0, 0x180))22+23+def b36(n):24+ digits = '0123456789abcdefghijklmnopqrstuvwxyz'25+ return digits[n // 36] + digits[n % 36]26+27+for name, wght in (('REGULAR', 400), ('BOLD', 650)):28+ f = instancer.instantiateVariableFont(TTFont(sys.argv[1]), {'wght': wght})29+ cmap, hmtx, upm = f.getBestCmap(), f['hmtx'], f['head'].unitsPerEm30+ # A character the font lacks comes from another font: 580, Pele's guess.31+ print(name, ''.join(b36(round(hmtx[cmap[c]][0] * 1000 / upm) if c in cmap else 580) for c in codes))32+EOF@@ -14,7 +14,7 @@
1414 "@typesafe-ai/sdk": "^0.7.0",1515 "highlight.js": "^11.12.0",1616 "markdown-it": "^14.1.0",17- "mermaid": "^12.2.0",17+ "pele": "1.6.0",1818 "sanitize-html": "^2.18.1"1919 },2020 "devDependencies": {@@ -217,62 +217,6 @@
217217 "node": ">=18"218218 }219219 },220- "node_modules/@antfu/install-pkg": {221- "version": "2.1.0",222- "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-2.1.0.tgz",223- "integrity": "sha512-sdg9NxU3zR4Mnawfbc/x6GB5Wf17WYud5qOuEuxXjaKpYpMkISSJEjItGebXJ2bQ4DIcly4NYH23mtkGJjvKUw==",224- "license": "MIT",225- "dependencies": {226- "package-manager-detector": "^1.8.0",227- "tinyexec": "^1.3.1"228- },229- "funding": {230- "url": "https://github.com/sponsors/antfu"231- }232- },233- "node_modules/@braintree/sanitize-url": {234- "version": "7.1.2",235- "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz",236- "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==",237- "license": "MIT"238- },239- "node_modules/@chevrotain/cst-dts-gen": {240- "version": "13.2.0",241- "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-13.2.0.tgz",242- "integrity": "sha512-bA9dvWlhAUcHkNwU+8w9yiUxJPlj/Lm3VqS7MD+NbIR/7LmpNLuK/AWNFdCo/T3SK5ND0/UHf3IJvl/Jn/i6yg==",243- "license": "Apache-2.0",244- "dependencies": {245- "@chevrotain/gast": "13.2.0",246- "@chevrotain/types": "13.2.0"247- }248- },249- "node_modules/@chevrotain/gast": {250- "version": "13.2.0",251- "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-13.2.0.tgz",252- "integrity": "sha512-bCARdAsG+eEbOveSXcOAC7pVxA04I1ionzeMOwIYf5NX7SaS7zgn4Dgug2BSqoL+iyKeMNSorQjsFSHJHsCNwg==",253- "license": "Apache-2.0",254- "dependencies": {255- "@chevrotain/types": "13.2.0"256- }257- },258- "node_modules/@chevrotain/regexp-to-ast": {259- "version": "13.2.0",260- "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-13.2.0.tgz",261- "integrity": "sha512-kxpUkLtiaK09ph+dib+Yvm+3sWSnuwlo25gqfKRgTPNmF6H7uW4gVkMjaRdtnN783S9sM6vUNWh9CsxYX4QwKw==",262- "license": "Apache-2.0"263- },264- "node_modules/@chevrotain/types": {265- "version": "13.2.0",266- "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-13.2.0.tgz",267- "integrity": "sha512-7qrYHRlKfWf2AvBfJi5eVOrKAkbxvMFsy/iIHTGIiApNcEecCaVIdR+mIQhyCBsTDJxBz4JWXHMo6krbhR9hVg==",268- "license": "Apache-2.0"269- },270- "node_modules/@chevrotain/utils": {271- "version": "13.2.0",272- "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-13.2.0.tgz",273- "integrity": "sha512-SzKYY68rseOrb9KILVzPsl6SjkQtdWw01SRGB5aCVI5kFcHltQsOYV3JB0Ffst72DgkvHCyDQ0NLwxtmCCf97Q==",274- "license": "Apache-2.0"275- },276220 "node_modules/@duckdb/node-api": {277221 "version": "1.5.6-r.1",278222 "resolved": "https://registry.npmjs.org/@duckdb/node-api/-/node-api-1.5.6-r.1.tgz",@@ -411,41 +355,12 @@
411355 "integrity": "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==",412356 "license": "MIT"413357 },414- "node_modules/@iconify/types": {415- "version": "2.0.0",416- "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",417- "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==",418- "license": "MIT"419- },420- "node_modules/@iconify/utils": {421- "version": "3.1.7",422- "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.7.tgz",423- "integrity": "sha512-JZHlwdID+dy+lTgbYC8NEC4zeugqeYsc6jewvzb4c58kHauJn+X7rNwQjxz5p2qSjqaEeQoLkCIQ9v/H4PK0/w==",424- "license": "MIT",425- "dependencies": {426- "@antfu/install-pkg": "^2.0.1",427- "@iconify/types": "^2.0.0",428- "import-meta-resolve": "^4.2.0"429- }430- },431358 "node_modules/@levischuck/tiny-cbor": {432359 "version": "0.2.11",433360 "resolved": "https://registry.npmjs.org/@levischuck/tiny-cbor/-/tiny-cbor-0.2.11.tgz",434361 "integrity": "sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==",435362 "license": "MIT"436363 },437- "node_modules/@mermaid-js/parser": {438- "version": "2.0.1",439- "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-2.0.1.tgz",440- "integrity": "sha512-j37dj/h6p6pots1L7pLPPNuABEEu9WP4OYg9pW1g+nYG331j0+3canE9OagtYdytYvdSAMG+2fKU5cxep6LgjA==",441- "license": "MIT",442- "dependencies": {443- "@chevrotain/types": "~13.2.0"444- },445- "engines": {446- "node": ">=22.12.0"447- }448- },449364 "node_modules/@peculiar/asn1-android": {450365 "version": "2.11.0",451366 "resolved": "https://registry.npmjs.org/@peculiar/asn1-android/-/asn1-android-2.11.0.tgz",@@ -749,265 +664,6 @@
749664 "node": ">=22"750665 }751666 },752- "node_modules/@types/d3": {753- "version": "7.4.3",754- "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz",755- "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==",756- "license": "MIT",757- "dependencies": {758- "@types/d3-array": "*",759- "@types/d3-axis": "*",760- "@types/d3-brush": "*",761- "@types/d3-chord": "*",762- "@types/d3-color": "*",763- "@types/d3-contour": "*",764- "@types/d3-delaunay": "*",765- "@types/d3-dispatch": "*",766- "@types/d3-drag": "*",767- "@types/d3-dsv": "*",768- "@types/d3-ease": "*",769- "@types/d3-fetch": "*",770- "@types/d3-force": "*",771- "@types/d3-format": "*",772- "@types/d3-geo": "*",773- "@types/d3-hierarchy": "*",774- "@types/d3-interpolate": "*",775- "@types/d3-path": "*",776- "@types/d3-polygon": "*",777- "@types/d3-quadtree": "*",778- "@types/d3-random": "*",779- "@types/d3-scale": "*",780- "@types/d3-scale-chromatic": "*",781- "@types/d3-selection": "*",782- "@types/d3-shape": "*",783- "@types/d3-time": "*",784- "@types/d3-time-format": "*",785- "@types/d3-timer": "*",786- "@types/d3-transition": "*",787- "@types/d3-zoom": "*"788- }789- },790- "node_modules/@types/d3-array": {791- "version": "3.2.2",792- "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz",793- "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==",794- "license": "MIT"795- },796- "node_modules/@types/d3-axis": {797- "version": "3.0.6",798- "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz",799- "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==",800- "license": "MIT",801- "dependencies": {802- "@types/d3-selection": "*"803- }804- },805- "node_modules/@types/d3-brush": {806- "version": "3.0.6",807- "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz",808- "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==",809- "license": "MIT",810- "dependencies": {811- "@types/d3-selection": "*"812- }813- },814- "node_modules/@types/d3-chord": {815- "version": "3.0.6",816- "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz",817- "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==",818- "license": "MIT"819- },820- "node_modules/@types/d3-color": {821- "version": "3.1.3",822- "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",823- "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",824- "license": "MIT"825- },826- "node_modules/@types/d3-contour": {827- "version": "3.0.6",828- "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz",829- "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==",830- "license": "MIT",831- "dependencies": {832- "@types/d3-array": "*",833- "@types/geojson": "*"834- }835- },836- "node_modules/@types/d3-delaunay": {837- "version": "6.0.4",838- "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",839- "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==",840- "license": "MIT"841- },842- "node_modules/@types/d3-dispatch": {843- "version": "3.0.7",844- "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz",845- "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==",846- "license": "MIT"847- },848- "node_modules/@types/d3-drag": {849- "version": "3.0.7",850- "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz",851- "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",852- "license": "MIT",853- "dependencies": {854- "@types/d3-selection": "*"855- }856- },857- "node_modules/@types/d3-dsv": {858- "version": "3.0.7",859- "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz",860- "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==",861- "license": "MIT"862- },863- "node_modules/@types/d3-ease": {864- "version": "3.0.2",865- "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",866- "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",867- "license": "MIT"868- },869- "node_modules/@types/d3-fetch": {870- "version": "3.0.7",871- "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz",872- "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==",873- "license": "MIT",874- "dependencies": {875- "@types/d3-dsv": "*"876- }877- },878- "node_modules/@types/d3-force": {879- "version": "3.0.10",880- "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz",881- "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==",882- "license": "MIT"883- },884- "node_modules/@types/d3-format": {885- "version": "3.0.4",886- "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz",887- "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==",888- "license": "MIT"889- },890- "node_modules/@types/d3-geo": {891- "version": "3.1.1",892- "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.1.tgz",893- "integrity": "sha512-65Emv9fQiQQqphLlRkuQ5ypPsOmWPhtBGCMv61JDPEPMvsx+gzhGf74yw1a78xFKPj6zw4AgQICJoQv0vK9M2w==",894- "license": "MIT",895- "dependencies": {896- "@types/geojson": "*"897- }898- },899- "node_modules/@types/d3-hierarchy": {900- "version": "3.1.7",901- "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz",902- "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==",903- "license": "MIT"904- },905- "node_modules/@types/d3-interpolate": {906- "version": "3.0.4",907- "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",908- "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",909- "license": "MIT",910- "dependencies": {911- "@types/d3-color": "*"912- }913- },914- "node_modules/@types/d3-path": {915- "version": "3.1.1",916- "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",917- "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",918- "license": "MIT"919- },920- "node_modules/@types/d3-polygon": {921- "version": "3.0.2",922- "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz",923- "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==",924- "license": "MIT"925- },926- "node_modules/@types/d3-quadtree": {927- "version": "3.0.6",928- "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz",929- "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==",930- "license": "MIT"931- },932- "node_modules/@types/d3-random": {933- "version": "3.0.4",934- "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.4.tgz",935- "integrity": "sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA==",936- "license": "MIT"937- },938- "node_modules/@types/d3-scale": {939- "version": "4.0.9",940- "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",941- "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",942- "license": "MIT",943- "dependencies": {944- "@types/d3-time": "*"945- }946- },947- "node_modules/@types/d3-scale-chromatic": {948- "version": "3.1.0",949- "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",950- "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==",951- "license": "MIT"952- },953- "node_modules/@types/d3-selection": {954- "version": "3.0.12",955- "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.12.tgz",956- "integrity": "sha512-Qe/KWYhEiIIxGs7HrAAjMfShxKldx19SJtr5zu53f3afPsdZNz7HHtdTLXo/kqeiWNXVycI24kSnfzBYkTzpgw==",957- "license": "MIT"958- },959- "node_modules/@types/d3-shape": {960- "version": "3.2.0",961- "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.2.0.tgz",962- "integrity": "sha512-kVd74ta9eof3eJOvbNd1vGKS/XERRyQbT26Og63hIsvDO84cjD5gEOhsXf26w3FSoNlPVz84DOFcKv/oou+fMw==",963- "license": "MIT",964- "dependencies": {965- "@types/d3-path": "*"966- }967- },968- "node_modules/@types/d3-time": {969- "version": "3.0.4",970- "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",971- "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",972- "license": "MIT"973- },974- "node_modules/@types/d3-time-format": {975- "version": "4.0.3",976- "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz",977- "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==",978- "license": "MIT"979- },980- "node_modules/@types/d3-timer": {981- "version": "3.0.2",982- "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",983- "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",984- "license": "MIT"985- },986- "node_modules/@types/d3-transition": {987- "version": "3.0.9",988- "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz",989- "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==",990- "license": "MIT",991- "dependencies": {992- "@types/d3-selection": "*"993- }994- },995- "node_modules/@types/d3-zoom": {996- "version": "3.0.9",997- "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.9.tgz",998- "integrity": "sha512-0sE1406XBYJGiqD3AusTl9ZqC//2mIXix51tbom25gDCA8ri4xnSZg28CaSE8Srl6FClqABUKfsn0qghgdepMA==",999- "license": "MIT",1000- "dependencies": {1001- "@types/d3-interpolate": "*",1002- "@types/d3-selection": "*"1003- }1004- },1005- "node_modules/@types/geojson": {1006- "version": "7946.0.16",1007- "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",1008- "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",1009- "license": "MIT"1010- },1011667 "node_modules/@types/linkify-it": {1012668 "version": "5.0.0",1013669 "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",@@ -1160,13 +816,6 @@
1160816 "node": ">=20.19.0"1161817 }1162818 },1163- "node_modules/@types/trusted-types": {1164- "version": "2.0.7",1165- "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",1166- "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",1167- "license": "MIT",1168- "optional": true1169- },1170819 "node_modules/@typesafe-ai/sdk": {1171820 "version": "0.7.0",1172821 "resolved": "https://registry.npmjs.org/@typesafe-ai/sdk/-/sdk-0.7.0.tgz",@@ -1510,696 +1159,153 @@
15101159 "license": "Apache-2.0",15111160 "optional": true,15121161 "os": [1513- "win32"1514- ],1515- "engines": {1516- "node": ">=16.20.0"1517- }1518- },1519- "node_modules/@upsetjs/venn.js": {1520- "version": "2.0.0",1521- "resolved": "https://registry.npmjs.org/@upsetjs/venn.js/-/venn.js-2.0.0.tgz",1522- "integrity": "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==",1523- "license": "MIT",1524- "optionalDependencies": {1525- "d3-selection": "^3.0.0",1526- "d3-transition": "^3.0.1"1527- }1528- },1529- "node_modules/a-sync-waterfall": {1530- "version": "1.0.1",1531- "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz",1532- "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==",1533- "license": "MIT"1534- },1535- "node_modules/acorn": {1536- "version": "8.19.0",1537- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz",1538- "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==",1539- "license": "MIT",1540- "bin": {1541- "acorn": "bin/acorn"1542- },1543- "engines": {1544- "node": ">=0.4.0"1545- }1546- },1547- "node_modules/acorn-walk": {1548- "version": "8.3.5",1549- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz",1550- "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==",1551- "license": "MIT",1552- "dependencies": {1553- "acorn": "^8.11.0"1554- },1555- "engines": {1556- "node": ">=0.4.0"1557- }1558- },1559- "node_modules/argparse": {1560- "version": "2.0.1",1561- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",1562- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",1563- "license": "Python-2.0"1564- },1565- "node_modules/asap": {1566- "version": "2.0.6",1567- "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",1568- "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",1569- "license": "MIT"1570- },1571- "node_modules/asn1js": {1572- "version": "3.1.0",1573- "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.1.0.tgz",1574- "integrity": "sha512-HWYi1THkOqguCiO+vwj09ZkQFD2kKEsejYHAYCtX0D80WVdoDnvRc9IukXX/jeb0T9NRWJnRtqwdbHltnFjwXA==",1575- "license": "BSD-3-Clause",1576- "dependencies": {1577- "pvtsutils": "^1.3.6",1578- "pvutils": "^1.2.0",1579- "tslib": "^2.8.1"1580- },1581- "engines": {1582- "node": ">=12.0.0"1583- }1584- },1585- "node_modules/balanced-match": {1586- "version": "1.0.2",1587- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",1588- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",1589- "license": "MIT"1590- },1591- "node_modules/bcp-47": {1592- "version": "2.1.1",1593- "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-2.1.1.tgz",1594- "integrity": "sha512-KLw+H/gd2p4zly1X7Yh/qziuyae5/w/QFnvTng9eZL5fvszL7Whl3MBoWF8yxL7ksUjBfOD+OxkytiqbBpG+Fw==",1595- "license": "MIT",1596- "dependencies": {1597- "is-alphabetical": "^2.0.0",1598- "is-alphanumerical": "^2.0.0",1599- "is-decimal": "^2.0.0"1600- },1601- "funding": {1602- "type": "github",1603- "url": "https://github.com/sponsors/wooorm"1604- }1605- },1606- "node_modules/bcp-47-match": {1607- "version": "2.0.3",1608- "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz",1609- "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==",1610- "license": "MIT",1611- "funding": {1612- "type": "github",1613- "url": "https://github.com/sponsors/wooorm"1614- }1615- },1616- "node_modules/bcp-47-normalize": {1617- "version": "2.3.0",1618- "resolved": "https://registry.npmjs.org/bcp-47-normalize/-/bcp-47-normalize-2.3.0.tgz",1619- "integrity": "sha512-8I/wfzqQvttUFz7HVJgIZ7+dj3vUaIyIxYXaTRP1YWoSDfzt6TUmxaKZeuXR62qBmYr+nvuWINFRl6pZ5DlN4Q==",1620- "license": "MIT",1621- "dependencies": {1622- "bcp-47": "^2.0.0",1623- "bcp-47-match": "^2.0.0"1624- },1625- "funding": {1626- "type": "github",1627- "url": "https://github.com/sponsors/wooorm"1628- }1629- },1630- "node_modules/brace-expansion": {1631- "version": "1.1.21",1632- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz",1633- "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==",1634- "license": "MIT",1635- "dependencies": {1636- "balanced-match": "^1.0.0",1637- "concat-map": "0.0.1"1638- }1639- },1640- "node_modules/chevrotain": {1641- "version": "13.2.0",1642- "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-13.2.0.tgz",1643- "integrity": "sha512-GTouk5YzgBxlTPCaDn5q0q2EJKkrYmnPF+HmF8v0Qo4r3iyPG7HJivkN1wthCXZcm3sIcZFpGTRovLPNdJNRCA==",1644- "license": "Apache-2.0",1645- "dependencies": {1646- "@chevrotain/cst-dts-gen": "13.2.0",1647- "@chevrotain/gast": "13.2.0",1648- "@chevrotain/regexp-to-ast": "13.2.0",1649- "@chevrotain/types": "13.2.0",1650- "@chevrotain/utils": "13.2.0"1651- },1652- "engines": {1653- "node": ">=22.0.0"1654- }1655- },1656- "node_modules/chokidar": {1657- "version": "4.0.3",1658- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",1659- "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",1660- "license": "MIT",1661- "dependencies": {1662- "readdirp": "^4.0.1"1663- },1664- "engines": {1665- "node": ">= 14.16.0"1666- },1667- "funding": {1668- "url": "https://paulmillr.com/funding/"1669- }1670- },1671- "node_modules/commander": {1672- "version": "10.0.1",1673- "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",1674- "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",1675- "license": "MIT",1676- "engines": {1677- "node": ">=14"1678- }1679- },1680- "node_modules/concat-map": {1681- "version": "0.0.1",1682- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",1683- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",1684- "license": "MIT"1685- },1686- "node_modules/cose-base": {1687- "version": "1.0.3",1688- "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz",1689- "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==",1690- "license": "MIT",1691- "dependencies": {1692- "layout-base": "^1.0.0"1693- }1694- },1695- "node_modules/cytoscape": {1696- "version": "3.34.4",1697- "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.34.4.tgz",1698- "integrity": "sha512-msY99tMY8iWTg+sIPGfSqDcPNOt4xcj+v/K/7qUFaCUxxlqnMkUYLBjRUzn9W5CU/R2hrmtJa5+UtovrEM2ctg==",1699- "license": "MIT",1700- "engines": {1701- "node": ">=0.10"1702- }1703- },1704- "node_modules/cytoscape-cose-bilkent": {1705- "version": "4.1.0",1706- "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz",1707- "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==",1708- "license": "MIT",1709- "dependencies": {1710- "cose-base": "^1.0.0"1711- },1712- "peerDependencies": {1713- "cytoscape": "^3.2.0"1714- }1715- },1716- "node_modules/cytoscape-fcose": {1717- "version": "2.2.0",1718- "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz",1719- "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==",1720- "license": "MIT",1721- "dependencies": {1722- "cose-base": "^2.2.0"1723- },1724- "peerDependencies": {1725- "cytoscape": "^3.2.0"1726- }1727- },1728- "node_modules/cytoscape-fcose/node_modules/cose-base": {1729- "version": "2.2.0",1730- "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz",1731- "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==",1732- "license": "MIT",1733- "dependencies": {1734- "layout-base": "^2.0.0"1735- }1736- },1737- "node_modules/cytoscape-fcose/node_modules/layout-base": {1738- "version": "2.0.1",1739- "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz",1740- "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==",1741- "license": "MIT"1742- },1743- "node_modules/d3": {1744- "version": "7.9.0",1745- "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz",1746- "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",1747- "license": "ISC",1748- "dependencies": {1749- "d3-array": "3",1750- "d3-axis": "3",1751- "d3-brush": "3",1752- "d3-chord": "3",1753- "d3-color": "3",1754- "d3-contour": "4",1755- "d3-delaunay": "6",1756- "d3-dispatch": "3",1757- "d3-drag": "3",1758- "d3-dsv": "3",1759- "d3-ease": "3",1760- "d3-fetch": "3",1761- "d3-force": "3",1762- "d3-format": "3",1763- "d3-geo": "3",1764- "d3-hierarchy": "3",1765- "d3-interpolate": "3",1766- "d3-path": "3",1767- "d3-polygon": "3",1768- "d3-quadtree": "3",1769- "d3-random": "3",1770- "d3-scale": "4",1771- "d3-scale-chromatic": "3",1772- "d3-selection": "3",1773- "d3-shape": "3",1774- "d3-time": "3",1775- "d3-time-format": "4",1776- "d3-timer": "3",1777- "d3-transition": "3",1778- "d3-zoom": "3"1779- },1780- "engines": {1781- "node": ">=12"1782- }1783- },1784- "node_modules/d3-array": {1785- "version": "3.2.4",1786- "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",1787- "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",1788- "license": "ISC",1789- "dependencies": {1790- "internmap": "1 - 2"1791- },1792- "engines": {1793- "node": ">=12"1794- }1795- },1796- "node_modules/d3-axis": {1797- "version": "3.0.0",1798- "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",1799- "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",1800- "license": "ISC",1801- "engines": {1802- "node": ">=12"1803- }1804- },1805- "node_modules/d3-brush": {1806- "version": "3.0.0",1807- "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",1808- "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",1809- "license": "ISC",1810- "dependencies": {1811- "d3-dispatch": "1 - 3",1812- "d3-drag": "2 - 3",1813- "d3-interpolate": "1 - 3",1814- "d3-selection": "3",1815- "d3-transition": "3"1816- },1817- "engines": {1818- "node": ">=12"1819- }1820- },1821- "node_modules/d3-chord": {1822- "version": "3.0.1",1823- "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",1824- "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",1825- "license": "ISC",1826- "dependencies": {1827- "d3-path": "1 - 3"1828- },1829- "engines": {1830- "node": ">=12"1831- }1832- },1833- "node_modules/d3-color": {1834- "version": "3.1.0",1835- "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",1836- "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",1837- "license": "ISC",1838- "engines": {1839- "node": ">=12"1840- }1841- },1842- "node_modules/d3-contour": {1843- "version": "4.0.2",1844- "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",1845- "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",1846- "license": "ISC",1847- "dependencies": {1848- "d3-array": "^3.2.0"1849- },1850- "engines": {1851- "node": ">=12"1852- }1853- },1854- "node_modules/d3-delaunay": {1855- "version": "6.0.4",1856- "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",1857- "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",1858- "license": "ISC",1859- "dependencies": {1860- "delaunator": "5"1861- },1862- "engines": {1863- "node": ">=12"1864- }1865- },1866- "node_modules/d3-dispatch": {1867- "version": "3.0.1",1868- "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",1869- "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",1870- "license": "ISC",1871- "engines": {1872- "node": ">=12"1873- }1874- },1875- "node_modules/d3-drag": {1876- "version": "3.0.0",1877- "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",1878- "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",1879- "license": "ISC",1880- "dependencies": {1881- "d3-dispatch": "1 - 3",1882- "d3-selection": "3"1883- },1884- "engines": {1885- "node": ">=12"1886- }1887- },1888- "node_modules/d3-dsv": {1889- "version": "3.0.1",1890- "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",1891- "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",1892- "license": "ISC",1893- "dependencies": {1894- "commander": "7",1895- "iconv-lite": "0.6",1896- "rw": "1"1897- },1898- "bin": {1899- "csv2json": "bin/dsv2json.js",1900- "csv2tsv": "bin/dsv2dsv.js",1901- "dsv2dsv": "bin/dsv2dsv.js",1902- "dsv2json": "bin/dsv2json.js",1903- "json2csv": "bin/json2dsv.js",1904- "json2dsv": "bin/json2dsv.js",1905- "json2tsv": "bin/json2dsv.js",1906- "tsv2csv": "bin/dsv2dsv.js",1907- "tsv2json": "bin/dsv2json.js"1908- },1909- "engines": {1910- "node": ">=12"1911- }1912- },1913- "node_modules/d3-dsv/node_modules/commander": {1914- "version": "7.2.0",1915- "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",1916- "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",1917- "license": "MIT",1918- "engines": {1919- "node": ">= 10"1920- }1921- },1922- "node_modules/d3-ease": {1923- "version": "3.0.1",1924- "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",1925- "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",1926- "license": "BSD-3-Clause",1927- "engines": {1928- "node": ">=12"1929- }1930- },1931- "node_modules/d3-fetch": {1932- "version": "3.0.1",1933- "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",1934- "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",1935- "license": "ISC",1936- "dependencies": {1937- "d3-dsv": "1 - 3"1938- },1939- "engines": {1940- "node": ">=12"1941- }1942- },1943- "node_modules/d3-force": {1944- "version": "3.0.0",1945- "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",1946- "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",1947- "license": "ISC",1948- "dependencies": {1949- "d3-dispatch": "1 - 3",1950- "d3-quadtree": "1 - 3",1951- "d3-timer": "1 - 3"1952- },1953- "engines": {1954- "node": ">=12"1955- }1956- },1957- "node_modules/d3-format": {1958- "version": "3.1.2",1959- "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz",1960- "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==",1961- "license": "ISC",1962- "engines": {1963- "node": ">=12"1964- }1965- },1966- "node_modules/d3-geo": {1967- "version": "3.1.1",1968- "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",1969- "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",1970- "license": "ISC",1971- "dependencies": {1972- "d3-array": "2.5.0 - 3"1973- },1974- "engines": {1975- "node": ">=12"1976- }1977- },1978- "node_modules/d3-hierarchy": {1979- "version": "3.1.2",1980- "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",1981- "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",1982- "license": "ISC",1983- "engines": {1984- "node": ">=12"1985- }1986- },1987- "node_modules/d3-interpolate": {1988- "version": "3.0.1",1989- "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",1990- "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",1991- "license": "ISC",1992- "dependencies": {1993- "d3-color": "1 - 3"1994- },1995- "engines": {1996- "node": ">=12"1997- }1998- },1999- "node_modules/d3-path": {2000- "version": "3.1.0",2001- "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",2002- "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",2003- "license": "ISC",2004- "engines": {2005- "node": ">=12"2006- }2007- },2008- "node_modules/d3-polygon": {2009- "version": "3.0.1",2010- "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",2011- "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",2012- "license": "ISC",2013- "engines": {2014- "node": ">=12"2015- }2016- },2017- "node_modules/d3-quadtree": {2018- "version": "3.0.1",2019- "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",2020- "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",2021- "license": "ISC",2022- "engines": {2023- "node": ">=12"2024- }2025- },2026- "node_modules/d3-random": {2027- "version": "3.0.1",2028- "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",2029- "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",2030- "license": "ISC",2031- "engines": {2032- "node": ">=12"2033- }2034- },2035- "node_modules/d3-sankey": {2036- "version": "0.12.3",2037- "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz",2038- "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==",2039- "license": "BSD-3-Clause",2040- "dependencies": {2041- "d3-array": "1 - 2",2042- "d3-shape": "^1.2.0"2043- }2044- },2045- "node_modules/d3-sankey/node_modules/d3-array": {2046- "version": "2.12.1",2047- "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",2048- "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",2049- "license": "BSD-3-Clause",2050- "dependencies": {2051- "internmap": "^1.0.0"2052- }2053- },2054- "node_modules/d3-sankey/node_modules/d3-path": {2055- "version": "1.0.9",2056- "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",2057- "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==",2058- "license": "BSD-3-Clause"2059- },2060- "node_modules/d3-sankey/node_modules/d3-shape": {2061- "version": "1.3.7",2062- "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz",2063- "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==",2064- "license": "BSD-3-Clause",2065- "dependencies": {2066- "d3-path": "1"1162+ "win32"1163+ ],1164+ "engines": {1165+ "node": ">=16.20.0"20671166 }20681167 },2069- "node_modules/d3-sankey/node_modules/internmap": {1168+ "node_modules/a-sync-waterfall": {20701169 "version": "1.0.1",2071- "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz",2072- "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==",2073- "license": "ISC"1170+ "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz",1171+ "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==",1172+ "license": "MIT"20741173 },2075- "node_modules/d3-scale": {2076- "version": "4.0.2",2077- "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",2078- "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",2079- "license": "ISC",2080- "dependencies": {2081- "d3-array": "2.10.0 - 3",2082- "d3-format": "1 - 3",2083- "d3-interpolate": "1.2.0 - 3",2084- "d3-time": "2.1.1 - 3",2085- "d3-time-format": "2 - 4"1174+ "node_modules/acorn": {1175+ "version": "8.19.0",1176+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz",1177+ "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==",1178+ "license": "MIT",1179+ "bin": {1180+ "acorn": "bin/acorn"20861181 },20871182 "engines": {2088- "node": ">=12"1183+ "node": ">=0.4.0"20891184 }20901185 },2091- "node_modules/d3-scale-chromatic": {2092- "version": "3.1.0",2093- "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",2094- "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",2095- "license": "ISC",1186+ "node_modules/acorn-walk": {1187+ "version": "8.3.5",1188+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz",1189+ "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==",1190+ "license": "MIT",20961191 "dependencies": {2097- "d3-color": "1 - 3",2098- "d3-interpolate": "1 - 3"1192+ "acorn": "^8.11.0"20991193 },21001194 "engines": {2101- "node": ">=12"1195+ "node": ">=0.4.0"21021196 }21031197 },2104- "node_modules/d3-selection": {2105- "version": "3.0.0",2106- "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",2107- "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",2108- "license": "ISC",2109- "engines": {2110- "node": ">=12"2111- }1198+ "node_modules/argparse": {1199+ "version": "2.0.1",1200+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",1201+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",1202+ "license": "Python-2.0"21121203 },2113- "node_modules/d3-shape": {2114- "version": "3.2.0",2115- "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",2116- "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",2117- "license": "ISC",2118- "dependencies": {2119- "d3-path": "^3.1.0"2120- },2121- "engines": {2122- "node": ">=12"2123- }1204+ "node_modules/asap": {1205+ "version": "2.0.6",1206+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",1207+ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",1208+ "license": "MIT"21241209 },2125- "node_modules/d3-time": {1210+ "node_modules/asn1js": {21261211 "version": "3.1.0",2127- "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",2128- "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",2129- "license": "ISC",1212+ "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.1.0.tgz",1213+ "integrity": "sha512-HWYi1THkOqguCiO+vwj09ZkQFD2kKEsejYHAYCtX0D80WVdoDnvRc9IukXX/jeb0T9NRWJnRtqwdbHltnFjwXA==",1214+ "license": "BSD-3-Clause",21301215 "dependencies": {2131- "d3-array": "2 - 3"1216+ "pvtsutils": "^1.3.6",1217+ "pvutils": "^1.2.0",1218+ "tslib": "^2.8.1"21321219 },21331220 "engines": {2134- "node": ">=12"1221+ "node": ">=12.0.0"21351222 }21361223 },2137- "node_modules/d3-time-format": {2138- "version": "4.1.0",2139- "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",2140- "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",2141- "license": "ISC",1224+ "node_modules/balanced-match": {1225+ "version": "1.0.2",1226+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",1227+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",1228+ "license": "MIT"1229+ },1230+ "node_modules/bcp-47": {1231+ "version": "2.1.1",1232+ "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-2.1.1.tgz",1233+ "integrity": "sha512-KLw+H/gd2p4zly1X7Yh/qziuyae5/w/QFnvTng9eZL5fvszL7Whl3MBoWF8yxL7ksUjBfOD+OxkytiqbBpG+Fw==",1234+ "license": "MIT",21421235 "dependencies": {2143- "d3-time": "1 - 3"1236+ "is-alphabetical": "^2.0.0",1237+ "is-alphanumerical": "^2.0.0",1238+ "is-decimal": "^2.0.0"21441239 },2145- "engines": {2146- "node": ">=12"1240+ "funding": {1241+ "type": "github",1242+ "url": "https://github.com/sponsors/wooorm"21471243 }21481244 },2149- "node_modules/d3-timer": {2150- "version": "3.0.1",2151- "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",2152- "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",2153- "license": "ISC",2154- "engines": {2155- "node": ">=12"1245+ "node_modules/bcp-47-match": {1246+ "version": "2.0.3",1247+ "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz",1248+ "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==",1249+ "license": "MIT",1250+ "funding": {1251+ "type": "github",1252+ "url": "https://github.com/sponsors/wooorm"21561253 }21571254 },2158- "node_modules/d3-transition": {2159- "version": "3.0.1",2160- "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",2161- "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",2162- "license": "ISC",1255+ "node_modules/bcp-47-normalize": {1256+ "version": "2.3.0",1257+ "resolved": "https://registry.npmjs.org/bcp-47-normalize/-/bcp-47-normalize-2.3.0.tgz",1258+ "integrity": "sha512-8I/wfzqQvttUFz7HVJgIZ7+dj3vUaIyIxYXaTRP1YWoSDfzt6TUmxaKZeuXR62qBmYr+nvuWINFRl6pZ5DlN4Q==",1259+ "license": "MIT",21631260 "dependencies": {2164- "d3-color": "1 - 3",2165- "d3-dispatch": "1 - 3",2166- "d3-ease": "1 - 3",2167- "d3-interpolate": "1 - 3",2168- "d3-timer": "1 - 3"2169- },2170- "engines": {2171- "node": ">=12"1261+ "bcp-47": "^2.0.0",1262+ "bcp-47-match": "^2.0.0"21721263 },2173- "peerDependencies": {2174- "d3-selection": "2 - 3"1264+ "funding": {1265+ "type": "github",1266+ "url": "https://github.com/sponsors/wooorm"21751267 }21761268 },2177- "node_modules/d3-zoom": {2178- "version": "3.0.0",2179- "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",2180- "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",2181- "license": "ISC",1269+ "node_modules/brace-expansion": {1270+ "version": "1.1.21",1271+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz",1272+ "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==",1273+ "license": "MIT",1274+ "dependencies": {1275+ "balanced-match": "^1.0.0",1276+ "concat-map": "0.0.1"1277+ }1278+ },1279+ "node_modules/chokidar": {1280+ "version": "4.0.3",1281+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",1282+ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",1283+ "license": "MIT",21821284 "dependencies": {2183- "d3-dispatch": "1 - 3",2184- "d3-drag": "2 - 3",2185- "d3-interpolate": "1 - 3",2186- "d3-selection": "2 - 3",2187- "d3-transition": "2 - 3"1285+ "readdirp": "^4.0.1"21881286 },21891287 "engines": {2190- "node": ">=12"1288+ "node": ">= 14.16.0"1289+ },1290+ "funding": {1291+ "url": "https://paulmillr.com/funding/"21911292 }21921293 },2193- "node_modules/dagre-d3-es": {2194- "version": "7.0.14",2195- "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz",2196- "integrity": "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==",1294+ "node_modules/commander": {1295+ "version": "10.0.1",1296+ "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",1297+ "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",21971298 "license": "MIT",2198- "dependencies": {2199- "d3": "^7.9.0",2200- "lodash-es": "^4.17.21"1299+ "engines": {1300+ "node": ">=14"22011301 }22021302 },1303+ "node_modules/concat-map": {1304+ "version": "0.0.1",1305+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",1306+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",1307+ "license": "MIT"1308+ },22031309 "node_modules/dayjs": {22041310 "version": "1.11.23",22051311 "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.23.tgz",@@ -2223,15 +1329,6 @@
22231329 }22241330 }22251331 },2226- "node_modules/delaunator": {2227- "version": "5.1.0",2228- "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz",2229- "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==",2230- "license": "ISC",2231- "dependencies": {2232- "robust-predicates": "^3.0.2"2233- }2234- },22351332 "node_modules/depd": {22361333 "version": "2.0.0",22371334 "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",@@ -2309,15 +1406,6 @@
23091406 "url": "https://github.com/fb55/domhandler?sponsor=1"23101407 }23111408 },2312- "node_modules/dompurify": {2313- "version": "3.4.17",2314- "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.17.tgz",2315- "integrity": "sha512-B/pSlYVHQ58kqISD/yDjuJ1yfzI34m1gF4lAod2bwSRhiN4K5Cl3H8Af7hqB2PmTL911tdEudnnXn5FnhRoz0w==",2316- "license": "(MPL-2.0 OR Apache-2.0)",2317- "optionalDependencies": {2318- "@types/trusted-types": "^2.0.7"2319- }2320- },23211409 "node_modules/domutils": {23221410 "version": "2.8.0",23231411 "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",@@ -2338,12 +1426,6 @@
23381426 "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",23391427 "license": "MIT"23401428 },2341- "node_modules/elkjs": {2342- "version": "0.9.3",2343- "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.3.tgz",2344- "integrity": "sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==",2345- "license": "EPL-2.0"2346- },23471429 "node_modules/encodeurl": {23481430 "version": "2.0.0",23491431 "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",@@ -2377,18 +1459,6 @@
23771459 "errno": "cli.js"23781460 }23791461 },2380- "node_modules/es-toolkit": {2381- "version": "1.52.0",2382- "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.52.0.tgz",2383- "integrity": "sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA==",2384- "license": "MIT",2385- "workspaces": [2386- "docs",2387- "benchmarks",2388- "tests/types",2389- "tests/browser-compat"2390- ]2391- },23921462 "node_modules/escape-html": {23931463 "version": "1.0.3",23941464 "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",@@ -2530,12 +1600,6 @@
25301600 "node": ">=11"25311601 }25321602 },2533- "node_modules/hachure-fill": {2534- "version": "0.5.2",2535- "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz",2536- "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==",2537- "license": "MIT"2538- },25391603 "node_modules/highlight.js": {25401604 "version": "11.12.0",25411605 "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.12.0.tgz",@@ -2605,43 +1669,12 @@
26051669 "url": "https://opencollective.com/express"26061670 }26071671 },2608- "node_modules/iconv-lite": {2609- "version": "0.6.3",2610- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",2611- "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",2612- "license": "MIT",2613- "dependencies": {2614- "safer-buffer": ">= 2.1.2 < 3.0.0"2615- },2616- "engines": {2617- "node": ">=0.10.0"2618- }2619- },2620- "node_modules/import-meta-resolve": {2621- "version": "4.2.0",2622- "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz",2623- "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==",2624- "license": "MIT",2625- "funding": {2626- "type": "github",2627- "url": "https://github.com/sponsors/wooorm"2628- }2629- },26301672 "node_modules/inherits": {26311673 "version": "2.0.4",26321674 "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",26331675 "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",26341676 "license": "ISC"26351677 },2636- "node_modules/internmap": {2637- "version": "2.0.3",2638- "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",2639- "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",2640- "license": "ISC",2641- "engines": {2642- "node": ">=12"2643- }2644- },26451678 "node_modules/is-alphabetical": {26461679 "version": "2.0.1",26471680 "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",@@ -2740,36 +1773,6 @@
27401773 "node": ">=8"27411774 }27421775 },2743- "node_modules/katex": {2744- "version": "0.18.10",2745- "resolved": "https://registry.npmjs.org/katex/-/katex-0.18.10.tgz",2746- "integrity": "sha512-/B6p9eY9DX7aHBfpkHdpirDTZ5QH9xTwL9y837PWuzuv41O5jFdqNQwVQEQsimY0/iVNkwBY4+4hMfhQ7d4Dbw==",2747- "funding": [2748- "https://opencollective.com/katex",2749- "https://github.com/sponsors/katex"2750- ],2751- "license": "MIT",2752- "dependencies": {2753- "commander": "^15.0.0"2754- },2755- "bin": {2756- "katex": "cli.js"2757- }2758- },2759- "node_modules/katex/node_modules/commander": {2760- "version": "15.0.0",2761- "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz",2762- "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==",2763- "license": "MIT",2764- "engines": {2765- "node": ">=22.12.0"2766- }2767- },2768- "node_modules/khroma": {2769- "version": "2.1.0",2770- "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz",2771- "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="2772- },27731776 "node_modules/kind-of": {27741777 "version": "6.0.3",27751778 "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",@@ -2797,12 +1800,6 @@
27971800 "dayjs": "^1.11.23"27981801 }27991802 },2800- "node_modules/layout-base": {2801- "version": "1.0.2",2802- "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz",2803- "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==",2804- "license": "MIT"2805- },28061803 "node_modules/linkify-it": {28071804 "version": "5.0.2",28081805 "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz",@@ -2848,12 +1845,6 @@
28481845 "integrity": "sha512-+dAZZ2mM+/m+vY9ezfoueVvrgnHIGi5FvgSymbIgJOFwiznWyA59mav95L+Mc6xPtL3s9gm5eNTlNtxJLbNM1g==",28491846 "license": "MIT"28501847 },2851- "node_modules/lodash-es": {2852- "version": "4.18.1",2853- "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz",2854- "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",2855- "license": "MIT"2856- },28571848 "node_modules/luxon": {28581849 "version": "3.7.2",28591850 "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz",@@ -2902,58 +1893,12 @@
29021893 "url": "https://github.com/fb55/entities?sponsor=1"29031894 }29041895 },2905- "node_modules/marked": {2906- "version": "16.4.2",2907- "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz",2908- "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==",2909- "license": "MIT",2910- "bin": {2911- "marked": "bin/marked.js"2912- },2913- "engines": {2914- "node": ">= 20"2915- }2916- },29171896 "node_modules/mdurl": {29181897 "version": "2.1.0",29191898 "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.1.0.tgz",29201899 "integrity": "sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg==",29211900 "license": "MIT"29221901 },2923- "node_modules/mermaid": {2924- "version": "12.2.0",2925- "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-12.2.0.tgz",2926- "integrity": "sha512-w7nr/SFrLO7BaVTs3dQTvYSV6nYSBj0Yy9DjBhsIJtAcHqRzXSq4Pg32ujHZ/AdWsAMNEvvhbm5dt9E2tOiHzw==",2927- "license": "MIT",2928- "dependencies": {2929- "@braintree/sanitize-url": "^7.1.2",2930- "@iconify/utils": "^3.0.2",2931- "@mermaid-js/parser": "^2.0.1",2932- "@types/d3": "^7.4.3",2933- "@upsetjs/venn.js": "^2.0.0",2934- "chevrotain": "~13.2.0",2935- "cytoscape": "^3.34.0",2936- "cytoscape-cose-bilkent": "^4.1.0",2937- "cytoscape-fcose": "^2.2.0",2938- "d3": "^7.9.0",2939- "d3-sankey": "^0.12.3",2940- "dagre-d3-es": "7.0.14",2941- "dayjs": "^1.11.21",2942- "dompurify": "^3.4.12",2943- "elkjs": "^0.9.3",2944- "es-toolkit": "^1.45.1",2945- "katex": "^0.18.0",2946- "khroma": "^2.1.0",2947- "marked": "^16.3.0",2948- "roughjs": "^4.6.6",2949- "stylis": "^4.3.6",2950- "ts-dedent": "^2.2.0",2951- "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0"2952- },2953- "engines": {2954- "node": ">=22.12.0"2955- }2956- },29571902 "node_modules/mime": {29581903 "version": "3.0.0",29591904 "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",@@ -3121,12 +2066,6 @@
31212066 "node": ">= 0.8"31222067 }31232068 },3124- "node_modules/package-manager-detector": {3125- "version": "1.9.0",3126- "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.9.0.tgz",3127- "integrity": "sha512-zAMfbta9nPA8AKkA4WdmNAT8R8s6Dk0/xwPsqNnRidMFcLo1YkLuPDTgs2f64XdzS8vbLNnoJ+uUIkgbz7oRHA==",3128- "license": "MIT"3129- },31302069 "node_modules/parse-srcset": {31312070 "version": "1.0.2",31322071 "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",@@ -3142,11 +2081,14 @@
31422081 "node": ">= 0.8"31432082 }31442083 },3145- "node_modules/path-data-parser": {3146- "version": "0.1.0",3147- "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz",3148- "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==",3149- "license": "MIT"2084+ "node_modules/pele": {2085+ "version": "1.6.0",2086+ "resolved": "https://registry.npmjs.org/pele/-/pele-1.6.0.tgz",2087+ "integrity": "sha512-VfAiVItsOGlGhvnBfphdT28VCG7mXMZxQNOwAGsCtxI2ztbUsT39MraEOYjsapLxVETkdUZscm0DdotYDQMLdw==",2088+ "license": "MIT",2089+ "engines": {2090+ "node": ">=20"2091+ }31502092 },31512093 "node_modules/picocolors": {31522094 "version": "1.1.1",@@ -3175,22 +2117,6 @@
31752117 "semver-compare": "^1.0.0"31762118 }31772119 },3178- "node_modules/points-on-curve": {3179- "version": "0.2.0",3180- "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz",3181- "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==",3182- "license": "MIT"3183- },3184- "node_modules/points-on-path": {3185- "version": "0.2.1",3186- "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz",3187- "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==",3188- "license": "MIT",3189- "dependencies": {3190- "path-data-parser": "0.1.0",3191- "points-on-curve": "0.2.0"3192- }3193- },31942120 "node_modules/postcss": {31952121 "version": "8.5.30",31962122 "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.30.tgz",@@ -3333,36 +2259,6 @@
33332259 "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",33342260 "license": "Apache-2.0"33352261 },3336- "node_modules/robust-predicates": {3337- "version": "3.0.3",3338- "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz",3339- "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==",3340- "license": "Unlicense"3341- },3342- "node_modules/roughjs": {3343- "version": "4.6.6",3344- "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz",3345- "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==",3346- "license": "MIT",3347- "dependencies": {3348- "hachure-fill": "^0.5.2",3349- "path-data-parser": "^0.1.0",3350- "points-on-curve": "^0.2.0",3351- "points-on-path": "^0.2.1"3352- }3353- },3354- "node_modules/rw": {3355- "version": "1.3.3",3356- "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",3357- "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",3358- "license": "BSD-3-Clause"3359- },3360- "node_modules/safer-buffer": {3361- "version": "2.1.2",3362- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",3363- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",3364- "license": "MIT"3365- },33662262 "node_modules/sanitize-html": {33672263 "version": "2.18.1",33682264 "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.18.1.tgz",@@ -3613,21 +2509,6 @@
36132509 "node": ">=0.10.0"36142510 }36152511 },3616- "node_modules/stylis": {3617- "version": "4.4.0",3618- "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz",3619- "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==",3620- "license": "MIT"3621- },3622- "node_modules/tinyexec": {3623- "version": "1.3.1",3624- "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz",3625- "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==",3626- "license": "MIT",3627- "engines": {3628- "node": ">=18"3629- }3630- },36312512 "node_modules/tinyglobby": {36322513 "version": "0.2.17",36332514 "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",@@ -3653,15 +2534,6 @@
36532534 "node": ">=0.6"36542535 }36552536 },3656- "node_modules/ts-dedent": {3657- "version": "2.3.0",3658- "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.3.0.tgz",3659- "integrity": "sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==",3660- "license": "MIT",3661- "engines": {3662- "node": ">=6.10"3663- }3664- },36652537 "node_modules/tslib": {36662538 "version": "2.8.1",36672539 "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",@@ -3749,19 +2621,6 @@
37492621 "integrity": "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==",37502622 "license": "MIT"37512623 },3752- "node_modules/uuid": {3753- "version": "14.0.3",3754- "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.3.tgz",3755- "integrity": "sha512-Sc5u1VVln93y1Xnz4R+CunOZc7R9LUcTmhj7Rc7Eg3HVcf7rVn2FOoa812fe0W8p3jD7xeoGSYbfqCGGH2B6xw==",3756- "funding": [3757- "https://github.com/sponsors/broofa",3758- "https://github.com/sponsors/ctavan"3759- ],3760- "license": "MIT",3761- "bin": {3762- "uuid": "dist-node/bin/uuid"3763- }3764- },37652624 "node_modules/ws": {37662625 "version": "8.22.0",37672626 "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.0.tgz",@@ -24,7 +24,7 @@
2424 <script src="{{ assets.live }}" defer></script>2525 <script src="{{ assets.keys }}" defer></script>2626 <script type="module" src="{{ assets.search }}"></script>27-<script src="{{ assets.ui }}" data-mermaid="{{ assets.mermaid }}" defer></script>27+<script src="{{ assets.ui }}" defer></script>2828 {% block scripts %}{% endblock %}2929 {% block head %}{{ m.meta(p.title if p and p.title else forge.config.name, r.description if r else '') }}{% endblock %}3030 </head>@@ -462,7 +462,19 @@ details.att pre, details.howto pre { white-space: pre-wrap; overflow-wrap: anywh
462462 /* In the raised file viewer, code sits on the page's colour, not in a second box. */463463 .viewer .markdown pre, .viewer .markdown code { background: var(--bg); border-color: var(--bg); }464464 .viewer .markdown pre code { background: none; }465-.markdown pre.mermaid { background: none; border: 0; text-align: center; }465+/* Diagrams, drawn at build time (lib/diagrams.ts): Pele's SVG takes its466+ colours and fonts from these, so it follows light and dark as it is. */467+.markdown .diagram { margin: var(--space-4) 0; text-align: center; }468+.markdown .diagram-source { margin: var(--space-4) 0; }469+.markdown .diagram-source figcaption { color: var(--muted); font-size: var(--text-sm); }470+.pele {471+ --pele-bg: var(--bg); --pele-fg: var(--fg); --pele-muted: var(--muted); --pele-line: var(--muted);472+ --pele-surface: var(--raised); --pele-surface-alt: var(--hover); --pele-border: var(--border-strong); --pele-accent: var(--accent);473+ --pele-critical: var(--fail); --pele-error: var(--fail); --pele-font: var(--sans); --pele-font-mono: var(--mono); --pele-radius: var(--radius);474+ --pele-title-weight: var(--fw-strong); --pele-heading-weight: var(--fw-strong); --pele-bold-weight: var(--fw-strong);475+ --pele-series-1: var(--viz-1); --pele-series-2: var(--pass); --pele-series-3: var(--warn); --pele-series-4: var(--fail);476+ --pele-series-5: var(--tok-title); --pele-series-6: var(--tok-const); --pele-series-7: var(--tok-builtin); --pele-series-8: var(--muted);477+}466478 .markdown blockquote { margin: var(--space-4) 0; padding: 0 var(--space-4); color: var(--muted); border-left: var(--bar) solid var(--border); }467479 .markdown table { display: block; max-width: 100%; overflow-x: auto; border-collapse: collapse; margin: var(--space-4) 0; font-size: var(--text-md); }468480 .markdown th, .markdown td { border-bottom: var(--hairline) solid var(--border); padding: var(--space-2) var(--space-3) var(--space-2) 0; text-align: left; vertical-align: top; }@@ -1,9 +1,6 @@
11 // Small page behaviors: copy buttons, the wrap-lines checkbox, the find-file2-// button for touch screens, relative times, and diagrams (mermaid, loaded3-// only when a page has one).2+// button for touch screens, and relative times.43 (() => {5- // Its hashed name, from this script's tag (base.njk).6- const mermaidSrc = document.currentScript?.dataset.mermaid;74 const copy = async (text: string, button: HTMLElement): Promise<void> => {85 await navigator.clipboard.writeText(text);96 const was = button.textContent;@@ -139,16 +136,4 @@
139136 140137 // Clicking into a clone URL selects it, for copying by hand.141138 for (const input of document.querySelectorAll<HTMLInputElement>('.copyrow input')) input.addEventListener('focus', () => input.select());142-143- if (mermaidSrc && document.querySelector('pre.mermaid')) {144- const s = document.createElement('script');145- s.src = mermaidSrc;146- s.onload = () => {147- const mermaid = (window as unknown as { mermaid: { initialize(o: object): void; run(): Promise<void> } }).mermaid;148- const dark = matchMedia('(prefers-color-scheme: dark)').matches;149- mermaid.initialize({ startOnLoad: false, theme: dark ? 'dark' : 'default', securityLevel: 'strict' });150- void mermaid.run();151- };152- document.head.append(s);153- }154139 })();@@ -0,0 +1,35 @@
1+# A diagram that doesn't parse doesn't stop the build or the page: it shows2+# as its source, with a short note of why, and the rest of the markdown3+# renders around it.4+. "$(dirname "$0")/../lib.sh"5+user dami6+repo dami/greet7+clone dami dami/greet "$T/w/d"8+cat >"$T/w/d/README.md" <<'EOF'9+# greet10+11+```mermaid12+flowchart LR13+ start[start] -->14+```15+16+Between the diagrams.17+18+```mermaid19+not a diagram <b>at all</b>20+```21+22+After the diagrams.23+EOF24+at "$T/w/d" commit "first" greet.sh25+push_as dami "$T/w/d" origin main26+build27+p=$FORGE_WWW/dami/greet/index.html28+has "$p" '<figure class="diagram-source"><pre><code class="language-mermaid">flowchart LR'29+has "$p" ' start\[start\] -->'30+has "$p" '<figcaption>Not drawn: Parse error on line [0-9]+\.</figcaption></figure>'31+has "$p" '<code class="language-mermaid">not a diagram <b>at all</b>'32+has "$p" '<figcaption>Not drawn: No diagram type detected for the given text\.</figcaption>'33+has "$p" 'Between the diagrams\.'34+has "$p" 'After the diagrams\.'35+lacks "$p" 'class="pele'@@ -0,0 +1,58 @@
1+# Markdown is anyone's who can push a repo, and a diagram's text goes into2+# the page as SVG: none of it can run a script, load anything, restyle the3+# page or link anywhere but where links may go. Pele escapes and filters4+# it, and its SVG passes a sanitizer that keeps only what Pele draws with;5+# SVG written straight into the markdown is still dropped.6+. "$(dirname "$0")/../lib.sh"7+user dami8+repo dami/greet9+clone dami dami/greet "$T/w/d"10+cat >"$T/w/d/README.md" <<'EOF'11+# greet12+13+```mermaid14+---15+title: "<script>alert('title')</script>"16+---17+%%{init: {"themeCSS": "body { display: none }", "fontFamily": "x;background:url(https://evil.example/f)"}}%%18+flowchart LR19+ accTitle: </title><script>alert('acc')</script>20+ A["<script>alert(1)</script>"] --> B["<img src=x onerror=alert(2)>"]21+ B --> C["</text></svg><style>body{display:none}</style>"]22+ C --> D["#lt;a href=#quot;javascript:alert(3)#quot;#gt;x#lt;/a#gt;"]23+ D --> E@{ img: "https://evil.example/pixel.png", label: "img" }24+ click A "javascript:alert(4)"25+ click B href "data:text/html,<script>alert(5)</script>"26+ click C "//evil.example/x"27+ click D "docs/guide.md" "tip"28+ style A fill:red,stroke:#000,font-family:x29+ classDef box fill:#f9f30+ class B box31+```32+33+<svg onload="alert(6)"><rect width="9999" height="9999"/><a href="javascript:alert(7)"><text>raw</text></a></svg>34+EOF35+at "$T/w/d" commit "first" docs/guide.md36+push_as dami "$T/w/d" origin main37+build38+p=$FORGE_WWW/dami/greet/index.html39+fig=$(grep -o '<figure class="diagram">.*</figure>' "$p")40+[ -n "$fig" ] || fail "no diagram on $p"41+tags() { printf '%s' "$fig" | grep -oE '<[^>]*>'; }42+# Only Pele's shapes, text and links: no script, style, image or foreign43+# object, and no attribute but these.44+eq "$(tags | grep -oE '^<[a-z]+' | LC_ALL=C sort -u | tr '\n' ' ')" '<a <figure <g <path <rect <svg <text <title <tspan '45+eq "$(tags | grep -oE ' [a-zA-Z:-]+=' | tr -d ' =' | LC_ALL=C sort -u | tr '\n' ' ')" 'aria-labelledby aria-roledescription class d fill font-weight height href id rel role stroke stroke-linecap style text-anchor transform viewBox width x xml:space xmlns y '46+if tags | grep -qi 'url('; then fail "a url() in the diagram"; fi47+# One link: the relative one, resolved to the forge's page. javascript:,48+# data: and network-path links are dropped.49+eq "$(tags | grep -oE '<a [^>]*>' | sort -u)" '<a href="/dami/greet/blob/main/docs/guide.md/" rel="noopener">'50+# Inline styles keep only drawing properties; classDef names don't reach51+# the page's classes.52+eq "$(tags | grep -oE ' class="[^"]*"' | sed 's/ class="//; s/"$//' | tr ' ' '\n' | grep -vE '^(pele(-[a-zA-Z-]+)?|clickable|diagram)$' | sort -u)" ''53+# The text is text: what looked like markup is escaped or gone.54+printf '%s' "$fig" | grep -q '<title id="diagram-[0-9a-f]\{8\}-title"></title><script>alert(.acc.)</script></title>' || fail "accTitle not escaped"55+# Init directives don't reach the SVG (their CSS stays out of its tags; the56+# label that says it is text), and raw SVG in the markdown is dropped.57+if tags | grep -q 'display'; then fail "the diagram's CSS reached a tag"; fi58+lacks "$p" 'evil\.example|onload|onerror|<rect width="9999"'@@ -0,0 +1,37 @@
1+# A ```mermaid fence in markdown is drawn at build time: the page has the2+# diagram as inline SVG with its labels as text, and no script draws it.3+. "$(dirname "$0")/../lib.sh"4+user dami5+repo dami/greet6+clone dami dami/greet "$T/w/d"7+cat >"$T/w/d/README.md" <<'EOF'8+# greet9+10+```mermaid11+flowchart LR12+ push[git push] --> pre{pre-receive} -->|ok| post[post-receive] --> build[forge build] --> pages[pages]13+```14+15+Pushes become pages.16+17+```mermaid18+flowchart LR19+ a --> b20+```21+EOF22+at "$T/w/d" commit "first" greet.sh23+push_as dami "$T/w/d" origin main24+build25+p=$FORGE_WWW/dami/greet/index.html26+has "$p" '<figure class="diagram"><svg xmlns="http://www\.w3\.org/2000/svg" class="pele pele-flowchart" viewBox="0 0 [0-9.]+ [0-9.]+"'27+has "$p" '<text class="pele-label"[^>]*>git push</text>'28+has "$p" '>post-receive</text>'29+# Coloured by the stylesheet's --pele-* variables, not colours of its own.30+has "$p" 'style="max-width:100%;height:auto;--_bg:var\(--pele-bg,'31+has "$FORGE_WWW/assets/$(cd "$FORGE_WWW/assets" && ls style.*.css)" '--pele-fg: var\(--fg\)'32+# A link preview's text skips the labels.33+has "$p" '<meta property="og:description" content="greet Pushes become pages\.">'34+# The scripts are the ones every page has; none is for diagrams.35+eq "$(grep -o '<script[^>]*>' "$p" | sed -E 's/\.[A-Za-z0-9_-]{10}\.js//')" "$(grep -o '<script[^>]*>' "$FORGE_WWW/dami/greet/log/main/index.html" | sed -E 's/\.[A-Za-z0-9_-]{10}\.js//')"36+lacks "$p" 'mermaid'37+eq "$(ls "$FORGE_WWW/assets" | grep -ci -e mermaid -e pele)" 02/3 Draw a wide diagram again for phones
A diagram is as wide as its layout, and scaled down to fit the column: a flowchart of eight steps left to right came out 1256px wide, which a phone shows at 350px, its labels about 4px tall. In a browser Pele's mount() would draw it again for the width it has; pages here have no script for that. So one wider than a phone (340px) is drawn a second time with Pele's maxWidth at that width, which wraps labels and turns a left-to-right flowchart top to bottom, and the figure holds both. A container query shows the second below 640px of column and the first above it. Each has its own ids, and the hidden one is display: none, so screen readers read one. Only diagrams that come out narrower get the second drawing.
| M | README.md | 14 | ++++++++------ |
| M | lib/ | 17 | ++++++++++++++++- |
| M | site/ | 8 | +++++++- |
| M | test/ | 3 | +++ |
@@ -710,12 +710,14 @@ diagram script. `style.css` colours them through Pele's `--pele-*`
710710 variables, which follow light and dark mode without drawing them again.711711 There is no browser at build time to measure text in, so labels are sized712712 from a table of the sans font's character widths in `lib/diagrams.ts`713-(`lib/text-widths.sh` makes it from the font). A diagram that doesn't parse714-shows its source and the reason instead. Markdown is anyone's who can push715-a repo: Pele escapes a diagram's text and filters its links and styles,716-then the SVG goes through a sanitizer of its own that keeps only the717-shapes, text and links Pele draws with (no images, no `url()`), while raw718-SVG in the markdown itself is still dropped.713+(`lib/text-widths.sh` makes it from the font). One wider than a phone is714+drawn a second time, laid out for a phone's width (often top to bottom),715+and the stylesheet shows that one in a narrow column. A diagram that716+doesn't parse shows its source and the reason instead. Markdown is anyone's717+who can push a repo: Pele escapes a diagram's text and filters its links718+and styles, then the SVG goes through a sanitizer of its own that keeps719+only the shapes, text and links Pele draws with (no images, no `url()`),720+while raw SVG in the markdown itself is still dropped.719721 720722 ## The lake and insights721723 @@ -76,6 +76,12 @@ const SVG: sanitize.IOptions = {
7676 7777 const escape = (s: string): string => s.replace(/[&<>"]/g, (c) => `&${{ '&': 'amp', '<': 'lt', '>': 'gt', '"': 'quot' }[c]};`);7878 79+// The width a phone gives a diagram, about. One drawn wider than this gets80+// a second drawing laid out for it, often turned top to bottom, which the81+// stylesheet shows instead in a narrow column (Pele's mount() would draw it82+// again for the width; a page here has no script for that).83+const PHONE = 340;84+7985 // One diagram's HTML. `href` rewrites a link in it, as the markdown's own8086 // links are (relative ones resolve to forge pages).8187 export function diagram(src: string, href: (url: string) => string = (u) => u): string {@@ -85,7 +91,16 @@ export function diagram(src: string, href: (url: string) => string = (u) => u):
8591 const options = { measurer, images: false, linkSchemes: ['http', 'https', 'mailto'] };8692 let svg;8793 try {88- svg = render(src, { ...options, idPrefix }).svg;94+ const wide = render(src, { ...options, idPrefix });95+ svg = wide.svg;96+ if (wide.width > PHONE) {97+ try {98+ const narrow = render(src, { ...options, idPrefix: idPrefix + '-narrow', maxWidth: PHONE });99+ if (narrow.width < wide.width) svg += narrow.svg;100+ } catch {101+ // The wide one will do.102+ }103+ }89104 } catch (e) {90105 // Pele's message names the line, and can quote it: shown as text.91106 let why = 'Pele failed on it';@@ -464,7 +464,13 @@ details.att pre, details.howto pre { white-space: pre-wrap; overflow-wrap: anywh
464464 .viewer .markdown pre code { background: none; }465465 /* Diagrams, drawn at build time (lib/diagrams.ts): Pele's SVG takes its466466 colours and fonts from these, so it follows light and dark as it is. */467-.markdown .diagram { margin: var(--space-4) 0; text-align: center; }467+.markdown .diagram { margin: var(--space-4) 0; text-align: center; container-type: inline-size; }468+/* A wide diagram's second drawing, laid out for a phone, replaces it there. */469+.markdown .diagram > svg + svg { display: none; }470+@container (max-width: 640px) {471+ .markdown .diagram > svg:has(+ svg) { display: none; }472+ .markdown .diagram > svg + svg { display: inline; }473+}468474 .markdown .diagram-source { margin: var(--space-4) 0; }469475 .markdown .diagram-source figcaption { color: var(--muted); font-size: var(--text-sm); }470476 .pele {@@ -1,5 +1,6 @@
11 # A ```mermaid fence in markdown is drawn at build time: the page has the22 # diagram as inline SVG with its labels as text, and no script draws it.3+# One wider than a phone has a second drawing, laid out for one, after it.34 . "$(dirname "$0")/../lib.sh"45 user dami56 repo dami/greet@@ -29,6 +30,8 @@ has "$p" '>post-receive</text>'
2930 # Coloured by the stylesheet's --pele-* variables, not colours of its own.3031 has "$p" 'style="max-width:100%;height:auto;--_bg:var\(--pele-bg,'3132 has "$FORGE_WWW/assets/$(cd "$FORGE_WWW/assets" && ls style.*.css)" '--pele-fg: var\(--fg\)'33+# The wide one drawn twice, the small one once.34+eq "$(grep '<figure class="diagram">' "$p" | awk -F '<svg xmlns' '{ print NF - 1 }' | tr '\n' ' ')" '2 1 '3235 # A link preview's text skips the labels.3336 has "$p" '<meta property="og:description" content="greet Pushes become pages\.">'3437 # The scripts are the ones every page has; none is for diagrams.3/3 Name the line a diagram's syntax error is on
Pele 1.6.0 locates a syntax error in the text its parsers see, which is
not quite the diagram's: front matter and %% comment lines are taken out
first and a newline is added at the end, as Mermaid does. So an error
after front matter or a comment was reported lines early, and one at the
end of a diagram a line past its last (a fence's text ends in a newline,
so two past):
render('flowchart LR\n A-->') // PeleError: line 3, snippet ""
The fallback's note now names the source's line. Pele's snippet quotes
the line it means, which is the source's line as written, at or after
the one Pele counted, so diagrams.ts looks for it there; an error at the
end is on the last line with anything on it. If neither works out (an
entity in the line changes its text), Pele's number stays.
This is a workaround, in one function (errorLine), to drop when Pele
reports lines in the source; scenario diagram-error-line holds the
expected lines either way. Not yet reported upstream.| M | lib/ | 24 | ++++++++++++++++++++++- |
| M | test/ | 2 | +- |
| A | test/ | 32 | ++++++++++++++++++++++++++++++ |
@@ -74,6 +74,25 @@ const SVG: sanitize.IOptions = {
7474 parser: { lowerCaseAttributeNames: false },7575 };7676 77+// Which line of the source a syntax error is on. Pele 1.6.0 counts lines in78+// the text it parses, which is the source less its front matter and %%79+// comment lines, plus a newline, as Mermaid does: an error after either is80+// reported lines early, and one at the end a line past the last. The line81+// its snippet quotes is the source's, so find it, at or after the line82+// Pele counted; at the end, it is the last line with anything on it. Drop83+// this when Pele reports lines in the source (upstream: see the commit that84+// added it).85+function errorLine(src: string, e: PeleError): number {86+ const lines = src.split(/\r\n?|\n/);87+ const quoted = e.snippet.split('\n')[0];88+ if (quoted.trim() === '') {89+ const last = lines.findLastIndex((l) => l.trim() !== '') + 1;90+ return last > 0 && e.line > last ? last : e.line;91+ }92+ const at = lines.findIndex((l, i) => i >= e.line - 1 && l === quoted);93+ return at === -1 ? e.line : at + 1;94+}95+7796 const escape = (s: string): string => s.replace(/[&<>"]/g, (c) => `&${{ '&': 'amp', '<': 'lt', '>': 'gt', '"': 'quot' }[c]};`);7897 7998 // The width a phone gives a diagram, about. One drawn wider than this gets@@ -104,7 +123,10 @@ export function diagram(src: string, href: (url: string) => string = (u) => u):
104123 } catch (e) {105124 // Pele's message names the line, and can quote it: shown as text.106125 let why = 'Pele failed on it';107- if (e instanceof PeleError) why = e.message.split('\n')[0].replace(/[.:]$/, '').slice(0, 160);126+ if (e instanceof PeleError) {127+ why = e.message.split('\n')[0].replace(/[.:]$/, '').slice(0, 160);128+ if (e.code === 'syntax' && e.line) why = why.replace(/ on line \d+/, ` on line ${errorLine(src, e)}`);129+ }108130 return `<figure class="diagram-source"><pre><code class="language-mermaid">${escape(src)}</code></pre><figcaption>Not drawn: ${escape(why)}.</figcaption></figure>\n`;109131 }110132 const clean = sanitize(svg, {@@ -27,7 +27,7 @@ build
2727 p=$FORGE_WWW/dami/greet/index.html2828 has "$p" '<figure class="diagram-source"><pre><code class="language-mermaid">flowchart LR'2929 has "$p" ' start\[start\] -->'30-has "$p" '<figcaption>Not drawn: Parse error on line [0-9]+\.</figcaption></figure>'30+has "$p" '<figcaption>Not drawn: Parse error on line 2\.</figcaption></figure>'3131 has "$p" '<code class="language-mermaid">not a diagram <b>at all</b>'3232 has "$p" '<figcaption>Not drawn: No diagram type detected for the given text\.</figcaption>'3333 has "$p" 'Between the diagrams\.'@@ -0,0 +1,32 @@
1+# A diagram's syntax error names the line of the diagram it is on, counting2+# its front matter and comment lines, and an error at the end names the3+# last line. Pele 1.6.0 counts lines in the text it parses, without those,4+# plus a newline; lib/diagrams.ts finds the line it means.5+. "$(dirname "$0")/../lib.sh"6+user dami7+repo dami/greet8+clone dami dami/greet "$T/w/d"9+cat >"$T/w/d/README.md" <<'EOF'10+# greet11+12+```mermaid13+---14+title: Push15+---16+%% the hooks, in order17+flowchart LR18+ push --> pre19+ pre --> -->20+```21+22+```mermaid23+flowchart LR24+ push --> pre -->25+```26+EOF27+at "$T/w/d" commit "first" greet.sh28+push_as dami "$T/w/d" origin main29+build30+p=$FORGE_WWW/dami/greet/index.html31+eq "$(grep -o '<figcaption>Not drawn: [^<]*' "$p" | sed 's/<figcaption>//')" 'Not drawn: Parse error on line 7.32+Not drawn: Parse error on line 2.'