From 072e1dcc4235c9c77ce1e4beabe698c792c63f2d Mon Sep 17 00:00:00 2001 From: NGPixel Date: Fri, 31 Jul 2026 16:03:37 -0400 Subject: [PATCH] refactor: move non-markdown features into MDC blocks --- CLAUDE.md | 4 +- backend/locales/en.json | 13 +- backend/models/sites.ts | 10 - blocks/block-asciinema/component.js | 256 +++ blocks/block-diagram/component.js | 281 ++++ blocks/block-infobox/component.js | 169 +- blocks/block-mathjax/component.js | 304 ++++ blocks/block-plantuml/component.js | 303 ++++ blocks/package-lock.json | 1491 ++++++++++++++++- blocks/package.json | 7 + blocks/rollup.config.mjs | 4 + frontend/package-lock.json | 33 - frontend/package.json | 2 - frontend/src/assets/icons.generated.js | 2 +- frontend/src/components/EditorMarkdown.vue | 51 +- .../EditorMarkdownConfigOverlay.vue | 105 +- frontend/src/components/PageHeader.vue | 17 +- frontend/src/css/_page-contents.scss | 92 +- frontend/src/main.js | 12 - frontend/src/renderers/markdown.js | 83 +- .../src/renderers/modules/github-alerts.js | 92 + frontend/src/renderers/modules/katex.js | 175 -- frontend/src/renderers/modules/kroki.mjs | 160 -- frontend/src/renderers/modules/plantuml.js | 202 --- 24 files changed, 2961 insertions(+), 907 deletions(-) create mode 100644 blocks/block-asciinema/component.js create mode 100644 blocks/block-diagram/component.js create mode 100644 blocks/block-mathjax/component.js create mode 100644 blocks/block-plantuml/component.js create mode 100644 frontend/src/renderers/modules/github-alerts.js delete mode 100644 frontend/src/renderers/modules/katex.js delete mode 100644 frontend/src/renderers/modules/kroki.mjs delete mode 100644 frontend/src/renderers/modules/plantuml.js diff --git a/CLAUDE.md b/CLAUDE.md index 1fa0d327f..a62d5940c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -117,7 +117,9 @@ Self-contained Lit components. Each lives in `blocks/block-/component.js` `rollup.config.mjs` picks up any directory matching `block-*` automatically, so a new block needs no config change. Output goes to `blocks/compiled/`, which the backend serves statically under `/_blocks/`. Blocks are loaded dynamically at runtime, which is why `_blocks/**` is excluded from -Vite's `dynamicImportVarsOptions`. +Vite's `dynamicImportVarsOptions`. A block pulling in a heavy library is fine — nothing is fetched +until its tag turns up in a page — and a library that still ships CommonJS works too, since the +rollup config runs `@rollup/plugin-commonjs` after `resolve()`. Blocks style themselves with `:host` / `:host-context(body.body--dark)` for dark mode and read the theme colors via CSS custom properties (`var(--q-primary)` — the `--q-` prefix is historical; the diff --git a/backend/locales/en.json b/backend/locales/en.json index 2c2793a92..f9e28fa2d 100644 --- a/backend/locales/en.json +++ b/backend/locales/en.json @@ -180,22 +180,12 @@ "admin.editors.markdown.allowHTML": "Allow HTML", "admin.editors.markdown.allowHTMLHint": "Allow HTML tags in content.", "admin.editors.markdown.general": "General", - "admin.editors.markdown.kroki": "Kroki", - "admin.editors.markdown.krokiHint": "Enable Kroki Diagrams Parser", - "admin.editors.markdown.krokiServerUrl": "Kroki Server URL", - "admin.editors.markdown.krokiServerUrlHint": "URL to the Kroki server used for image generation.", - "admin.editors.markdown.latexEngine": "LaTeX Engine", - "admin.editors.markdown.latexEngineHint": "Which engine to use to process TeX/LaTeX expressions.", "admin.editors.markdown.lineBreaks": "Auto Line Breaks", "admin.editors.markdown.lineBreaksHint": "Automatically add linebreaks within paragraphs.", "admin.editors.markdown.linkify": "Auto-linking", "admin.editors.markdown.linkifyHint": "Automatically convert URLs into clickable links.", "admin.editors.markdown.multimdTable": "MultiMarkdown Table", "admin.editors.markdown.multimdTableHint": "Enable support for MultiMarkdown Table features.", - "admin.editors.markdown.plantuml": "PlantUML", - "admin.editors.markdown.plantumlHint": "Enable PlantUML Parser", - "admin.editors.markdown.plantumlServerUrl": "PlantUML Server URL", - "admin.editors.markdown.plantumlServerUrlHint": "URL to the PlantUML server used for image generation.", "admin.editors.markdown.quotes": "Quotes Style", "admin.editors.markdown.quotesHint": "When typographer is enabled. Double + single quotes replacement pairs. e.g. «»„“ for Russian, „“‚‘ for German, etc.", "admin.editors.markdown.saveSuccess": "Markdown editor configuration saved successfully.", @@ -1637,7 +1627,8 @@ "editor.emoji.smileysEmotion": "Smileys & Emotion", "editor.emoji.symbols": "Symbols", "editor.emoji.travelPlaces": "Travel & Places", - "editor.markup.admonitionDanger": "Danger / Important Admonition", + "editor.markup.admonitionDanger": "Danger / Caution Admonition", + "editor.markup.admonitionImportant": "Important Admonition", "editor.markup.admonitionInfo": "Info / Note Admonition", "editor.markup.admonitionSuccess": "Tip / Success Admonition", "editor.markup.admonitionWarning": "Warning Admonition", diff --git a/backend/models/sites.ts b/backend/models/sites.ts index 0e12d6329..7bcff096a 100644 --- a/backend/models/sites.ts +++ b/backend/models/sites.ts @@ -148,14 +148,9 @@ class Sites { isActive: true, config: { allowHTML: true, - kroki: false, - krokiServerUrl: 'https://kroki.io', - latexEngine: 'katex', lineBreaks: true, linkify: true, multimdTable: true, - plantuml: false, - plantumlServerUrl: 'https://www.plantuml.com/plantuml/', quotes: 'english', tabWidth: 2, typographer: false, @@ -326,14 +321,9 @@ class Sites { isActive: true, config: { allowHTML: true, - kroki: false, - krokiServerUrl: 'https://kroki.io', - latexEngine: 'katex', lineBreaks: true, linkify: true, multimdTable: true, - plantuml: false, - plantumlServerUrl: 'https://www.plantuml.com/plantuml/', quotes: 'english', tabWidth: 2, typographer: false, diff --git a/blocks/block-asciinema/component.js b/blocks/block-asciinema/component.js new file mode 100644 index 000000000..0ad9d226a --- /dev/null +++ b/blocks/block-asciinema/component.js @@ -0,0 +1,256 @@ +import { LitElement, html, css, unsafeCSS } from 'lit' +import { create } from 'asciinema-player' +// -> The player's stylesheet, as a string. It is what draws the terminal, and a in the page +// cannot reach into this shadow root — see the `cssAsString` plugin in rollup.config.mjs. +import playerCss from 'asciinema-player/dist/bundle/asciinema-player.css' + +/** + * An attribute that means "off" when it says so. + * + * MDC writes every prop with a value — `autoPlay="false"` is what the block picker produces for a + * toggle that was switched on and off again — and Lit's own Boolean converter reads any string at all + * as true, that one included. + */ +const boolean = { + converter: { + fromAttribute: (value) => value !== null && value !== 'false', + toAttribute: (value) => (value ? 'true' : null) + } +} + +/** + * Block Asciinema + */ +export class BlockAsciinemaElement extends LitElement { + /** + * Metadata for the admin area and the editor's block picker. Collected at build time into + * `compiled/blocks.manifest.json`, which the server reads to register the block. Values must be + * plain literals. See `props` in `block-index` for what the picker does with that list. + */ + static definition = { + block: 'asciinema', + name: 'Terminal Recording', + description: 'Plays an asciinema recording — a .cast file — in a terminal player.', + icon: 'run-command', + props: [ + { + name: 'src', + type: 'string', + label: 'Recording URL', + hint: 'Path or URL of the .cast file to play.', + required: true + }, + { + name: 'theme', + type: 'select', + label: 'Theme', + options: [ + 'asciinema', + 'dracula', + 'gruvbox-dark', + 'monokai', + 'nord', + 'seti', + 'solarized-dark', + 'solarized-light', + 'tango' + ], + hint: 'Terminal colours. All but solarized-light are dark.', + default: 'asciinema' + }, + { + name: 'autoPlay', + type: 'boolean', + label: 'Play On Load', + hint: 'Start as soon as the page is opened, rather than waiting to be asked.', + // -> Stated, so that a toggle switched on and then off again writes nothing into the page + default: false + }, + { + name: 'loop', + type: 'boolean', + label: 'Loop', + hint: 'Start again on reaching the end.', + default: false + }, + { + name: 'speed', + type: 'number', + label: 'Speed', + hint: 'Playback rate. 2 plays twice as fast as it was recorded.', + default: 1 + }, + { + name: 'idleTimeLimit', + type: 'number', + label: 'Idle Time Limit', + hint: 'Cap the pauses in the recording at this many seconds. Empty keeps them as recorded.' + } + ] + } + + static get styles() { + return [ + unsafeCSS(playerCss), + css` + :host { + display: block; + } + + /* -> The gap below the block. On this element rather than :host: see block-index. */ + .player, + .error { + margin-bottom: 16px; + } + + .player { + border-radius: 5px; + /* -> The terminal paints its own background into the corners otherwise */ + overflow: hidden; + } + + .error { + color: var(--q-negative, #c10015); + border: 1px dashed color-mix(in srgb, currentColor 50%, transparent); + border-radius: 5px; + padding: 1rem; + } + ` + ] + } + + static get properties() { + return { + /** + * Path or URL of the .cast file + * @type {string} + */ + src: { type: String }, + + /** + * Name of one of the player's terminal themes + * @type {string} + */ + theme: { type: String }, + + /** + * Whether to start without being asked + * @type {boolean} + */ + autoPlay: boolean, + + /** + * Whether to start again at the end + * @type {boolean} + */ + loop: boolean, + + /** + * Playback rate, 1 being the speed it was recorded at + * @type {number} + */ + speed: { type: Number }, + + /** + * Longest pause to play back, in seconds + * @type {number} + */ + idleTimeLimit: { type: Number }, + + // Internal Properties + _error: { state: true } + } + } + + constructor() { + super() + this.src = '' + this.theme = 'asciinema' + this.autoPlay = false + this.loop = false + this.speed = 1 + this.idleTimeLimit = null + this._error = '' + this._player = null + } + + /** + * What to give the player, out of what the author gave the block. + * + * Only the settings that were actually asked for: an option left out is the player's own default, + * which is the one that gets maintained. A speed of zero or a negative one would stop the recording + * dead, and a nonsense number would take the player with it, so that one is bounded. + */ + _options() { + const speed = Number(this.speed) + const idle = Number(this.idleTimeLimit) + return { + theme: this.theme || 'asciinema', + autoPlay: this.autoPlay, + loop: this.loop, + speed: Number.isFinite(speed) && speed > 0 ? Math.min(speed, 10) : 1, + ...(Number.isFinite(idle) && idle > 0 ? { idleTimeLimit: idle } : {}), + // -> A recording is as wide as the terminal it was made in, which is rarely this column's width + fit: 'width' + } + } + + /** + * Fetch the recording, and say so in the block if it cannot be had. + * + * The player is given this rather than the address itself, for the sake of what happens when the + * address is wrong. Handed a URL it fetches the file on its own, and a fetch that fails leaves an + * empty terminal sitting there with the reason in the console — where an author who mistyped a path + * will not see it. Fetching it here is the only way to get hold of that failure, which is the + * common one: a typo, a file that has moved, or a host that sends no CORS headers. + * + * The response is handed over whole, which is a source the player takes as it comes; there is + * nothing to be gained by reading it here, and it lets the player stream a long recording. + */ + async _fetch(src) { + try { + const response = await fetch(src) + if (!response.ok) { + throw new Error(`${response.status} ${response.statusText}`.trim()) + } + return response + } catch (err) { + this._error = `This recording could not be loaded from ${src} — ${err.message}` + throw err + } + } + + firstUpdated() { + const src = this.src?.trim() + if (!src) { + this._error = 'This player needs the address of a .cast recording.' + return + } + /* + A function rather than the recording itself, so that nothing is fetched until it is played — + which is the player's own behaviour, and the right one: a page carrying a recording should not + pull the whole thing down before anybody has asked to watch it. + */ + this._player = create( + { data: () => this._fetch(src) }, + this.renderRoot.querySelector('.player'), + this._options() + ) + } + + disconnectedCallback() { + super.disconnectedCallback() + // -> The player keeps listeners on window and a resize observer, which outlive the element + this._player?.dispose() + this._player = null + } + + render() { + if (this._error) { + return html`
${this._error}
` + } + return html`
` + } +} + +window.customElements.define('block-asciinema', BlockAsciinemaElement) diff --git a/blocks/block-diagram/component.js b/blocks/block-diagram/component.js new file mode 100644 index 000000000..303f63ba8 --- /dev/null +++ b/blocks/block-diagram/component.js @@ -0,0 +1,281 @@ +import { LitElement, html, css } from 'lit' +import { unsafeSVG } from 'lit/directives/unsafe-svg.js' +import mermaid from 'mermaid' + +/** + * A number for the next drawing, so every one of them gets an id of its own. + * + * Mermaid names the SVG it produces and writes that name into the CSS it embeds in it, so two + * diagrams sharing an id would style each other. A counter rather than a random name: the ids are + * scoped to one page load, and a run of them is easier to recognise in the inspector. + */ +let drawingCount = 0 + +/** + * The drawing in progress, so that only ever one of them is. + * + * Mermaid is configured globally — `initialize` sets the library up, not a call to it — so two + * diagrams on a page asking for different themes would each set theirs and then be drawn in whichever + * one was set last. Queued, a diagram has the library to itself from the moment it configures it to + * the moment it is handed back an SVG. + */ +let queue = Promise.resolve() + +/** + * Configure mermaid and draw one diagram with it, once whatever is ahead of it is done. + */ +function drawInTurn(config, id, source) { + const drawing = queue.then(() => { + mermaid.initialize(config) + return mermaid.render(id, source) + }) + // -> Whether it worked or not, since a diagram that could not be drawn must not hold up the rest + queue = drawing.then( + () => {}, + () => {} + ) + return drawing +} + +/** + * Block Diagram + */ +export class BlockDiagramElement extends LitElement { + /** + * Metadata for the admin area and the editor's block picker. Collected at build time into + * `compiled/blocks.manifest.json`, which the server reads to register the block. Values must be + * plain literals. See `props` in `block-index` for what the picker does with that list. + */ + static definition = { + block: 'diagram', + name: 'Diagram', + description: 'Draws a Mermaid diagram — flowchart, sequence, class, state, ER, gantt and more.', + icon: 'workflow', + /* + A fenced block, and not only for the syntax highlighting: markdown would otherwise have its way + with the source before this ever sees it. `-->` survives, but the typographer turns `--` into a + dash, an indented line reads as a code block of its own, and `%%` comments and `#` labels are + claimed as structure. Inside a fence the text arrives exactly as it was typed. + */ + template: `\`\`\`mermaid +flowchart LR + A[Start] --> B{Ready?} + B -->|Yes| C[Ship it] + B -->|No| A +\`\`\``, + props: [ + { + name: 'caption', + type: 'string', + label: 'Caption', + hint: 'Shown under the diagram.' + }, + { + name: 'theme', + type: 'select', + label: 'Theme', + options: ['auto', 'default', 'dark', 'neutral', 'forest'], + hint: 'auto follows the light or dark theme the reader is using.', + default: 'auto' + }, + { + name: 'align', + type: 'select', + label: 'Alignment', + options: ['left', 'center'], + default: 'left' + } + ] + } + + static get styles() { + return css` + :host { + display: block; + } + + /* -> The gap below the block. On this element rather than :host: see block-index. */ + .diagram, + .error { + margin-bottom: 16px; + } + + .diagram { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; + } + .diagram.is-center { + align-items: center; + } + + /* + Mermaid sizes the drawing itself — it writes a max-width on the SVG at the width the diagram + came out to, so a small one is left at its own size and a large one shrinks to the column. Only + the height is settled here, so that shrinking keeps the shapes in proportion. + */ + svg { + max-width: 100%; + height: auto; + } + + .caption { + color: #424242; + font-size: 0.8em; + } + :host-context(body.body--dark) .caption { + color: rgba(255, 255, 255, 0.7); + } + + .error { + color: var(--q-negative, #c10015); + border: 1px dashed color-mix(in srgb, currentColor 50%, transparent); + border-radius: 5px; + padding: 1rem; + white-space: pre-wrap; + } + ` + } + + static get properties() { + return { + /** + * Text shown under the diagram + * @type {string} + */ + caption: { type: String }, + + /** + * Mermaid theme, or `auto` to follow the reader's + * @type {string} + */ + theme: { type: String }, + + /** + * Where the drawing sits in the column, `left` or `center` + * @type {string} + */ + align: { type: String }, + + // Internal Properties + _svg: { state: true }, + _error: { state: true } + } + } + + constructor() { + super() + this.caption = '' + this.theme = 'auto' + this.align = 'left' + this._svg = '' + this._error = '' + this._themeWatcher = null + /** The drawing being waited on, so a stale one cannot land after a newer one. */ + this._drawing = 0 + /** The source, and whether it came out of a fence. Both read from the body once, on first render. */ + this._source = '' + this._fenced = false + } + + /** + * The theme to draw in. + * + * `auto` reads the class the app puts on the body, which is the same thing every block's CSS keys + * its dark mode off — a diagram cannot do it in CSS, because mermaid picks its colours while it + * draws and writes them into the SVG. + */ + _theme() { + if (this.theme && this.theme !== 'auto') { + return this.theme + } + return document.body.classList.contains('body--dark') ? 'dark' : 'default' + } + + /** + * Draw the source, or say why it could not be drawn. + */ + async _draw() { + const drawing = ++this._drawing + const config = { + startOnLoad: false, + // -> A page is authored by whoever may edit it, so the text in a diagram is treated as text: + // HTML in a label is escaped and `click` directives do nothing + securityLevel: 'strict', + // -> Mermaid's own answer to a broken diagram is to append a drawing of a bomb to the body, + // outside this element and past the page's styling. The message below is this block's job. + suppressErrorRendering: true, + theme: this._theme(), + // -> The page's own font, so a diagram reads as part of the text around it. Mermaid measures + // its labels in the same font, so the boxes come out the right size for it. + fontFamily: 'inherit' + } + try { + const { svg } = await drawInTurn(config, `block-diagram-${++drawingCount}`, this._source) + // -> A theme toggle can start a second drawing while this one is still going + if (drawing !== this._drawing) { + return + } + this._svg = svg + this._error = '' + } catch (err) { + if (drawing !== this._drawing) { + return + } + this._svg = '' + /* + Mermaid says what it could not read and where, which is the useful half. The other half is + the fence, because a diagram that renders in every other tool and not here is nearly always a + source markdown got to first — see `template`. + */ + this._error = `This diagram could not be drawn: ${err.message ?? err}` + if (!this._fenced) { + this._error += + '\n\nThe source has to go inside a fenced code block, or markdown rewrites it before this block sees it.' + } + } + } + + firstUpdated() { + /* + The source is the block's body, taken from the fence markdown left behind. `textContent` is what + undoes the escaping that put `-->` in the markup, and gives back what the author typed. + */ + const fence = this.querySelector('pre') + this._fenced = Boolean(fence) + this._source = ((fence ?? this).textContent ?? '').trim() + if (!this._source) { + this._error = + 'This diagram is empty. Its source goes in the body of the block, inside a fenced code block.' + return + } + this._draw() + + // -> Only `auto` has anything to follow; a diagram asked for a theme by name keeps it either way + if (this.theme === 'auto') { + this._themeWatcher = new MutationObserver(() => this._draw()) + this._themeWatcher.observe(document.body, { attributeFilter: ['class'] }) + } + } + + disconnectedCallback() { + super.disconnectedCallback() + this._themeWatcher?.disconnect() + this._themeWatcher = null + } + + render() { + if (this._error) { + return html`
${this._error}
` + } + return html` +
+ ${unsafeSVG(this._svg)} + ${this.caption ? html`
${this.caption}
` : null} +
+ ` + } +} + +window.customElements.define('block-diagram', BlockDiagramElement) diff --git a/blocks/block-infobox/component.js b/blocks/block-infobox/component.js index c30896172..25ed026ad 100644 --- a/blocks/block-infobox/component.js +++ b/blocks/block-infobox/component.js @@ -22,6 +22,58 @@ const NO_SVG = html` ` +/** + * What an author writes to mean "this value goes somewhere": a page, an email address, a number. + * + * The scheme has to be spelled out. A value that merely looks like a hostname is left alone, since + * plenty of ordinary facts read that way — a file name, a version, a decimal — and there is no test + * that tells `notes.txt` from `montreal.ca` without guessing. + */ +const SCHEME = /^(?:https?:\/\/|mailto:|tel:)/i + +/** + * The link a value stands for, if it is one. + * + * The whole value has to be the address; a sentence with a URL in it is prose, and picking the link + * out of it is markdown's job, not this block's. + * + * @returns {{ href: string, label: string, isExternal: boolean } | null} + */ +function linkOf(text) { + if (!SCHEME.test(text) || /\s/.test(text)) { + return null + } + let url + try { + url = new URL(text) + } catch { + return null + } + /* + Shown without its scheme. An infobox is a column of short facts read at a glance, and `https://` + is the same four inches of boilerplate on every row of it — the address is the part that says + where the row goes. The label comes off the text as typed rather than out of the parsed URL, which + would put back a trailing slash the author did not write. + */ + const label = text.replace(SCHEME, '') + if (!label) { + return null + } + return { + href: url.href, + label, + /* + The mark means "this leaves the wiki", so it is for a web address on another host — the question + the page's renderer asks of a link, and for the same reason it asks it of the host and not of + the text. See `isExternalHref` in `renderers/markdown.js`, which also leaves an email address + and a telephone number unmarked: neither goes to a page at all, and both say what they are. + */ + isExternal: + (url.protocol === 'http:' || url.protocol === 'https:') && + url.origin !== globalThis.location?.origin + } +} + /** * One value, as it is shown. * @@ -36,7 +88,17 @@ function valueOf(value) { // -> Joined by hand rather than with `join`, so that a boolean among them is still drawn return value.map((entry, index) => html`${index > 0 ? ', ' : ''}${valueOf(entry)}`) } - return String(value) + const text = String(value) + const link = linkOf(text) + if (!link) { + return text + } + // -> No whitespace inside the anchor — hence the tags broken after their closing bracket, which is + // how the formatter keeps it out: a space beside the words is taken in by the underline on hover + // and pushes the external mark off the end of them + return html`${link.label}` } /** @@ -66,10 +128,15 @@ export class BlockInfoboxElement extends LitElement { name: 'Infobox', description: 'A summary box beside the text, filled in from a list of facts.', icon: 'data-sheet', - template: `City: Montreal + template: `\`\`\`yaml +City: Montreal Country: Canada -Metro: true -"Key with space": foo-bar`, +Public Transport: + Metro: true + Bus: true + Monorail: false +Website: https://montreal.ca +\`\`\``, props: [ { name: 'name', @@ -193,16 +260,91 @@ Metro: true overflow-wrap: anywhere; } - /* -> A nested mapping: its own heading across both columns, then its rows under it */ + /* + -> A nested mapping: its own heading across both columns, then its rows under it + + Shaded top-down rather than flat, so the heading reads as the lid of the group under it: the + pale edge catches the eye where the group starts and the colour settles into the one the box's + own name is drawn on. The two stops are declared per theme, since "lighter" in dark mode is a + lighter dark grey and not a step towards white. + */ .group { grid-column: 1 / -1; padding: 7px 12px; border-top: 1px solid var(--infobox-rule); - background-color: var(--infobox-head); + background-image: linear-gradient(to bottom, var(--infobox-head-top), var(--infobox-head)); font-weight: 600; text-align: center; } + /* + The rule that closes a group. + Thicker than the ones between rows, and in the border colour rather than the rule colour, so + that a row belonging to the group and a row that follows it are told apart at a glance — the + heading marks where the group starts, this marks where it stops. + */ + dl > :is(dt, dd).is-group-end { + border-bottom: 3px solid var(--infobox-border); + } + + /* -> At the foot of the box there is nothing to separate from, and the card's own border is there */ + dl > :is(dt, dd):is(:last-child, :nth-last-child(2)) { + border-bottom: 0; + } + + /* + Whatever comes next drops its own line: the thick one above it is the separation, and the two + together would read as a single rule of an odd weight. + + Two selectors because a row is two children of the grid — the label and the value — so the + line over it is drawn twice, once per column. Leaving the second one on broke the rule in + half: nothing above the label, a hairline above the value. A group heading spans both columns + and is only ever the one element, which is why the second selector asks for a dd. + */ + dl > dd.is-group-end + *, + dl > dd.is-group-end + * + dd { + border-top: 0; + } + + /* + A value that is a web address, drawn the way the page draws its links: the same colour token, + the same medium weight, the same underline on hover. The rules are repeated here because a + stylesheet in the page cannot reach into a shadow root — the custom properties it declares do + reach in, which is what keeps the box in step with a re-themed site. + */ + a { + color: var(--content-link, var(--q-primary, #1976d2)); + font-weight: 500; + text-decoration: none; + } + a:hover, + a:focus-visible { + text-decoration: underline; + text-decoration-thickness: 1px; + text-underline-offset: 2px; + } + + /* + A link that leaves the wiki says so — the same mark, from the same masked SVG, as a link in the + text beside it. Masked rather than drawn, so it takes the link's own colour in either theme, + and sized in em so it keeps its proportion to the words. See the LINKS section of + css/_page-contents.scss. + */ + a.is-external-link::after { + content: ''; + display: inline-block; + width: 0.8em; + height: 0.8em; + margin-left: 0.25em; + background-color: currentColor; + /* -> Subordinate to the words: a marker, not a second link */ + opacity: 0.7; + mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M14 3v2h3.59l-9.83 9.83l1.41 1.41L19 6.41V10h2V3m-2 16H5V5h7V3H5c-1.11 0-2 .9-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7h-2z'/%3E%3C/svg%3E"); + mask-repeat: no-repeat; + mask-size: contain; + vertical-align: baseline; + } + .yes { color: var(--q-positive, #02c39a); vertical-align: -3px; @@ -222,12 +364,14 @@ Metro: true --infobox-border: #d5d5d5; --infobox-bg: #f8f9fa; --infobox-head: #eaecf0; + --infobox-head-top: #f7f8fa; --infobox-rule: #e3e5e8; } :host-context(body.body--dark) { --infobox-border: rgba(255, 255, 255, 0.15); --infobox-bg: #161b22; --infobox-head: #1e232a; + --infobox-head-top: #2b323c; --infobox-rule: rgba(255, 255, 255, 0.1); } ` @@ -341,12 +485,15 @@ Metro: true const isGroup = rows.length > 1 || rows[0].label !== undefined return html` ${isGroup ? html`
${label}
` : null} - ${rows.map( - (row) => html` -
${isGroup ? row.label : label}
-
${valueOf(row.value)}
+ ${rows.map((row, index) => { + // -> The pair that closes a group carries the rule that separates it from + // whatever is listed after it + const groupEnd = isGroup && index === rows.length - 1 ? 'is-group-end' : '' + return html` +
${isGroup ? row.label : label}
+
${valueOf(row.value)}
` - )} + })} ` })} diff --git a/blocks/block-mathjax/component.js b/blocks/block-mathjax/component.js new file mode 100644 index 000000000..211b9e18a --- /dev/null +++ b/blocks/block-mathjax/component.js @@ -0,0 +1,304 @@ +import { LitElement, html, css } from 'lit' +import { unsafeSVG } from 'lit/directives/unsafe-svg.js' +import { mathjax } from '@mathjax/src/js/mathjax.js' +import { TeX } from '@mathjax/src/js/input/tex.js' +import { SVG } from '@mathjax/src/js/output/svg.js' +import { liteAdaptor } from '@mathjax/src/js/adaptors/liteAdaptor.js' +import { RegisterHTMLHandler } from '@mathjax/src/js/handlers/html.js' +import { MathJaxNewcmFont } from '@mathjax/mathjax-newcm-font/js/svg.js' +import { MathJaxMhchemFontExtension } from '@mathjax/mathjax-mhchem-font-extension/js/svg.js' +/* + Every TeX package the block understands, imported for its side effect: a configuration registers + itself under its name, and `PACKAGES` below is what then switches it on. + + This is the set MathJax's own "all packages" bundle carries, less three of them. `html` is left out + because it exists to put HTML into the page from inside TeX — a link, a class, a style attribute — + which is not what a formula is for. `noerrors` and `noundefined` are left out because both answer a + mistake by drawing something: the unreadable source in place of the formula, or a black box where a + macro should have been. Without them the error reaches this file, which has a panel to say so in. + + Nothing here loads anything at run time. `require` and `autoload` are absent for that reason: both + fetch a package the moment TeX asks for one, which cannot work in a bundle — the block is a single + file served from /_blocks, with no MathJax install behind it to fetch from. +*/ +import '@mathjax/src/js/input/tex/base/BaseConfiguration.js' +import '@mathjax/src/js/input/tex/action/ActionConfiguration.js' +import '@mathjax/src/js/input/tex/ams/AmsConfiguration.js' +import '@mathjax/src/js/input/tex/amscd/AmsCdConfiguration.js' +import '@mathjax/src/js/input/tex/bbox/BboxConfiguration.js' +import '@mathjax/src/js/input/tex/boldsymbol/BoldsymbolConfiguration.js' +import '@mathjax/src/js/input/tex/braket/BraketConfiguration.js' +import '@mathjax/src/js/input/tex/bussproofs/BussproofsConfiguration.js' +import '@mathjax/src/js/input/tex/cancel/CancelConfiguration.js' +import '@mathjax/src/js/input/tex/cases/CasesConfiguration.js' +import '@mathjax/src/js/input/tex/centernot/CenternotConfiguration.js' +import '@mathjax/src/js/input/tex/color/ColorConfiguration.js' +import '@mathjax/src/js/input/tex/colortbl/ColortblConfiguration.js' +import '@mathjax/src/js/input/tex/empheq/EmpheqConfiguration.js' +import '@mathjax/src/js/input/tex/enclose/EncloseConfiguration.js' +import '@mathjax/src/js/input/tex/extpfeil/ExtpfeilConfiguration.js' +import '@mathjax/src/js/input/tex/gensymb/GensymbConfiguration.js' +import '@mathjax/src/js/input/tex/mathtools/MathtoolsConfiguration.js' +import '@mathjax/src/js/input/tex/mhchem/MhchemConfiguration.js' +import '@mathjax/src/js/input/tex/newcommand/NewcommandConfiguration.js' +import '@mathjax/src/js/input/tex/physics/PhysicsConfiguration.js' +import '@mathjax/src/js/input/tex/textcomp/TextcompConfiguration.js' +import '@mathjax/src/js/input/tex/textmacros/TextMacrosConfiguration.js' +import '@mathjax/src/js/input/tex/unicode/UnicodeConfiguration.js' +import '@mathjax/src/js/input/tex/upgreek/UpgreekConfiguration.js' +import '@mathjax/src/js/input/tex/verb/VerbConfiguration.js' + +const PACKAGES = [ + 'base', + 'action', + 'ams', + 'amscd', + 'bbox', + 'boldsymbol', + 'braket', + 'bussproofs', + 'cancel', + 'cases', + 'centernot', + 'color', + 'colortbl', + 'empheq', + 'enclose', + 'extpfeil', + 'gensymb', + 'mathtools', + 'mhchem', + 'newcommand', + 'physics', + 'textcomp', + 'textmacros', + 'unicode', + 'upgreek', + 'verb' +] + +/** + * MathJax, set up once for the page. + * + * Off the document entirely: the SVG output measures nothing in the DOM — it has the metrics of every + * glyph in the font it draws with — so a formula can be typeset against a document MathJax makes up + * for itself and handed back as markup. That is what makes it usable from inside a shadow root, which + * MathJax has no notion of and where its own stylesheet in the page would not reach. + */ +const adaptor = liteAdaptor() +RegisterHTMLHandler(adaptor) + +const output = new SVG({ + fontData: MathJaxNewcmFont, + /* + Each formula carries its own glyph definitions. The alternative, one cache for the page, is a + single hidden `svg` in the document that every formula points into — and a reference from inside a + shadow root does not resolve to it, so every letter would come out blank. + */ + fontCache: 'local' +}) + +/* + mhchem's bonds, arrows and brackets are glyphs of their own, in a font variant the text font has no + reason to carry. Added here rather than fetched: MathJax's own build loads this on demand the first + time a `\ce` turns up, which is the one thing a bundled block cannot do. +*/ +output.font.addExtension(MathJaxMhchemFontExtension) + +const document_ = mathjax.document('', { + InputJax: new TeX({ + packages: PACKAGES, + // -> Handing the error on rather than drawing it: see the panel in `render` + formatError: (jax, err) => { + throw err + } + }), + OutputJax: output +}) + +/** + * Block MathJax + */ +export class BlockMathjaxElement extends LitElement { + /** + * Metadata for the admin area and the editor's block picker. Collected at build time into + * `compiled/blocks.manifest.json`, which the server reads to register the block. Values must be + * plain literals. See `props` in `block-index` for what the picker does with that list. + */ + static definition = { + block: 'mathjax', + name: 'MathJax', + description: + 'Typesets a TeX formula, including chemical equations written with mhchem — \\ce{} and \\pu{}.', + icon: 'sigma', + /* + Fenced, and not as a nicety: TeX is made of the characters markdown reads as its own. A lone + backslash goes missing, `_` and `^` open emphasis, `\\` at the end of a line is a break, and the + typographer rewrites quotes and dashes inside the source. Inside a fence it arrives as typed. + */ + template: `\`\`\`latex +x = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a} +\`\`\``, + props: [ + { + name: 'caption', + type: 'string', + label: 'Caption', + hint: 'Shown under the formula.' + }, + { + name: 'align', + type: 'select', + label: 'Alignment', + options: ['center', 'left'], + default: 'center' + } + ] + } + + static get styles() { + return css` + :host { + display: block; + } + + /* -> The gap below the block. On this element rather than :host: see block-index. */ + .formula, + .error { + margin-bottom: 16px; + } + + .formula { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + } + .formula.is-left { + align-items: flex-start; + } + + /* + A formula wider than the column scrolls rather than shrinks, the way a display equation in the + text does — see .katex-display in css/_page-contents.scss. Shrinking is the wrong answer for + something read symbol by symbol: a long derivation would end up a grey smear. + */ + .drawing { + max-width: 100%; + overflow-x: auto; + overflow-y: hidden; + /* -> Room for the scrollbar to appear without it sitting on the descenders */ + padding: 0.2em 0; + } + + /* + The drawing takes the colour of the text around it: MathJax paints its glyphs in currentColor, + so dark mode needs nothing here — unlike a block that picks its own colours. + */ + svg { + display: block; + } + + .caption { + color: #424242; + font-size: 0.8em; + text-align: center; + } + :host-context(body.body--dark) .caption { + color: rgba(255, 255, 255, 0.7); + } + + .error { + color: var(--q-negative, #c10015); + border: 1px dashed color-mix(in srgb, currentColor 50%, transparent); + border-radius: 5px; + padding: 1rem; + white-space: pre-wrap; + } + ` + } + + static get properties() { + return { + /** + * Text shown under the formula + * @type {string} + */ + caption: { type: String }, + + /** + * Where the formula sits in the column, `center` or `left` + * @type {string} + */ + align: { type: String }, + + // Internal Properties + _svg: { state: true }, + _error: { state: true } + } + } + + constructor() { + super() + this.caption = '' + this.align = 'center' + this._svg = '' + this._error = '' + } + + /** + * Typeset the source, or say why it could not be. + */ + _typeset(source, fenced) { + try { + const container = document_.convert(source, { display: true }) + const drawing = adaptor.firstChild(container) + /* + The formula named for a reader who cannot see it. MathJax's own answer to this is a MathML + copy of the expression alongside the drawing, which needs its stylesheet in the page to stay + hidden and its speech engine to read well — neither of which a block in a shadow root has. The + source is what is left, and it is what the author wrote: imperfectly read aloud, but the + drawing already carries role="img", and an image with no name at all is worse. + */ + adaptor.setAttribute(drawing, 'aria-label', source) + this._svg = adaptor.outerHTML(drawing) + this._error = '' + } catch (err) { + this._svg = '' + this._error = `This formula could not be typeset: ${err.message ?? err}` + if (!fenced) { + this._error += + '\n\nThe source has to go inside a fenced code block, or markdown rewrites it before this block sees it.' + } + } + } + + firstUpdated() { + /* + The source is the block's body, taken from the fence markdown left behind. `textContent` is what + undoes the escaping that put `&` and `<` in the markup, and gives back what was typed. + */ + const fence = this.querySelector('pre') + const source = ((fence ?? this).textContent ?? '').trim() + if (!source) { + this._error = + 'This formula is empty. Its TeX source goes in the body of the block, inside a fenced code block.' + return + } + this._typeset(source, Boolean(fence)) + } + + render() { + if (this._error) { + return html`
${this._error}
` + } + return html` +
+
${unsafeSVG(this._svg)}
+ ${this.caption ? html`
${this.caption}
` : null} +
+ ` + } +} + +window.customElements.define('block-mathjax', BlockMathjaxElement) diff --git a/blocks/block-plantuml/component.js b/blocks/block-plantuml/component.js new file mode 100644 index 000000000..c1382b4d2 --- /dev/null +++ b/blocks/block-plantuml/component.js @@ -0,0 +1,303 @@ +import { LitElement, html, css } from 'lit' +import { deflateRaw } from 'pako' + +/** The default server, which is the one PlantUML runs for everybody. */ +const DEFAULT_SERVER = 'https://www.plantuml.com/plantuml' + +/** + * PlantUML's own alphabet for the text it carries in a URL. + * + * Base64 by shape but not by order — digits first, then the letters, and `-_` for the last two — so + * the standard encoders cannot be used and this is done by hand below. + */ +const ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_' + +/** + * A diagram source as it goes into a PlantUML URL: deflated, then written in that alphabet. + * + * Raw deflate with no zlib header, which is what the server's decoder expects. Three bytes at a time + * become four characters; a group short of three is padded with zeros, and the server disregards what + * the padding decodes to. + * + * The result is about 1.4 characters per character of source, so a very large diagram can outgrow what + * a server will accept in a URL. That is a limit of this transport, and the way past it is the + * server's POST endpoint, which is not implemented here. + */ +function encodeForUrl(source) { + const bytes = deflateRaw(new TextEncoder().encode(source), { level: 9 }) + let encoded = '' + for (let i = 0; i < bytes.length; i += 3) { + const b1 = bytes[i] + const b2 = bytes[i + 1] ?? 0 + const b3 = bytes[i + 2] ?? 0 + encoded += ALPHABET[b1 >> 2] + encoded += ALPHABET[((b1 & 0x3) << 4) | (b2 >> 4)] + encoded += ALPHABET[((b2 & 0xf) << 2) | (b3 >> 6)] + encoded += ALPHABET[b3 & 0x3f] + } + return encoded +} + +/** + * Block PlantUML + */ +export class BlockPlantumlElement extends LitElement { + /** + * Metadata for the admin area and the editor's block picker. Collected at build time into + * `compiled/blocks.manifest.json`, which the server reads to register the block. Values must be + * plain literals. See `props` in `block-index` for what the picker does with that list. + */ + static definition = { + block: 'plantuml', + name: 'PlantUML', + description: + 'Draws a PlantUML diagram — sequence, class, state, activity, mindmap, gantt and the rest.', + icon: 'polyline', + /* + Fenced, and named `plantuml` so the source is what it says it is. The fence is also what keeps + markdown off it: `->` survives, but `--` becomes a dash, a line opening with `*` or `#` is read + as a list or a heading, and an indented line becomes a code block of its own. + + Passed to the server exactly as written, `@startuml` included — which is why a `@startmindmap` + or a `@startgantt` works here too. Wrapping it in `@startuml` on the author's behalf would rule + every one of those out. + */ + template: `\`\`\`plantuml +@startuml +Alice -> Bob : hello +Bob --> Alice : hi +@enduml +\`\`\``, + props: [ + { + name: 'server', + type: 'string', + label: 'Server', + hint: 'PlantUML server to draw with. The public one when left empty.', + // -> Written out rather than taken from DEFAULT_SERVER above: the manifest is read out of this + // file's syntax tree at build time, where a name is just a name + default: 'https://www.plantuml.com/plantuml' + }, + { + name: 'format', + type: 'select', + label: 'Format', + options: ['svg', 'png'], + hint: 'svg stays sharp at any size; png is there for a server with svg switched off.', + default: 'svg' + }, + { + name: 'caption', + type: 'string', + label: 'Caption', + hint: 'Shown under the diagram.' + }, + { + name: 'align', + type: 'select', + label: 'Alignment', + options: ['left', 'center'], + default: 'left' + } + ] + } + + static get styles() { + return css` + :host { + display: block; + } + + /* -> The gap below the block. On this element rather than :host: see block-index. */ + .diagram, + .error { + margin-bottom: 16px; + } + + .diagram { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; + } + .diagram.is-center { + align-items: center; + } + + /* + The drawing sits on white in both themes, padded, the way a QR code does. PlantUML draws in + black on nothing at all, so on a dark page a diagram left to the page's background is black on + black — and its own colours, where a diagram has them, are picked to sit on paper. + */ + .sheet { + max-width: 100%; + padding: 12px; + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 5px; + background-color: #fff; + /* -> A diagram wider than the column scrolls rather than shrinking to illegibility */ + overflow-x: auto; + } + :host-context(body.body--dark) .sheet { + border-color: rgba(255, 255, 255, 0.15); + } + + img { + display: block; + /* -> Its own size, up to the width of the column */ + max-width: 100%; + height: auto; + } + + .caption { + color: #424242; + font-size: 0.8em; + } + :host-context(body.body--dark) .caption { + color: rgba(255, 255, 255, 0.7); + } + + .error { + color: var(--q-negative, #c10015); + border: 1px dashed color-mix(in srgb, currentColor 50%, transparent); + border-radius: 5px; + padding: 1rem; + white-space: pre-wrap; + } + ` + } + + static get properties() { + return { + /** + * PlantUML server to draw with + * @type {string} + */ + server: { type: String }, + + /** + * Image format to ask the server for, `svg` or `png` + * @type {string} + */ + format: { type: String }, + + /** + * Text shown under the diagram + * @type {string} + */ + caption: { type: String }, + + /** + * Where the diagram sits in the column, `left` or `center` + * @type {string} + */ + align: { type: String }, + + // Internal Properties + _src: { state: true }, + _error: { state: true } + } + } + + constructor() { + super() + this.server = DEFAULT_SERVER + this.format = 'svg' + this.caption = '' + this.align = 'left' + this._src = '' + this._error = '' + } + + /** + * Where the drawing comes from. + * + * An `img` rather than markup fetched and inlined, because that is the one way of asking that needs + * nothing of the server beyond the picture: no CORS headers, which a PlantUML behind somebody's own + * proxy may well not send. It also means the browser caches the drawing like any other image. + */ + _url(source) { + const server = (this.server?.trim() || DEFAULT_SERVER).replace(/\/+$/, '') + const format = this.format === 'png' ? 'png' : 'svg' + return `${server}/${format}/${encodeForUrl(source)}` + } + + /** + * Say what went wrong, having been told only that the image did not load. + * + * Not the case of a diagram PlantUML cannot read: it answers those with a picture saying so, and a + * browser draws it whatever status came with it — so a mistake in the source shows up as the + * server's own message where the diagram would have been, which is the best place for it. + * + * What is left is a server that did not answer, or answered with something that is not an image: a + * wrong address, a host that cannot be reached from where the reader is, a login page. The request + * is made a second time to tell those apart, and to read `X-PlantUML-Diagram-Error` if it is there. + * Best effort — a server that sends no CORS headers refuses this second request, and the message + * below stands as it is. Nothing about drawing a diagram depends on any of it. + */ + async _explain(url) { + // -> Resolved against the page, since a server may perfectly well be a path on this wiki + const absolute = new URL(url, window.location.href) + this._error = `The diagram could not be drawn by ${absolute.origin}.` + try { + const response = await fetch(absolute) + const reason = response.headers.get('x-plantuml-diagram-error') + if (reason) { + this._error = `PlantUML could not read this diagram: ${reason}` + } else if (!response.ok) { + this._error = `The server answered ${response.status} ${response.statusText} for this diagram.` + } + } catch { + // -> Unreachable, blocked, or simply not a PlantUML server; the message above says as much + this._error += ' Check the server address, and that the page may reach it.' + } + } + + firstUpdated() { + /* + The source is the block's body, taken from the fence markdown left behind. `textContent` is what + undoes the escaping that put `-->` in the markup, and gives back what the author typed. + */ + const fence = this.querySelector('pre') + const source = ((fence ?? this).textContent ?? '').trim() + if (!source) { + this._error = + 'This diagram is empty. Its source goes in the body of the block, inside a ```plantuml fence.' + if (this.querySelector('img')) { + // -> The renderer's own PlantUML option claims that fence and draws it before this block is + // ever built, leaving an image where the source should be + this._error += + '\n\nThe PlantUML option in the markdown editor settings is on, and it has already turned this fence into a diagram of its own. Switch it off to draw through this block.' + } + return + } + this._src = this._url(source) + } + + render() { + if (this._error) { + return html`
${this._error}
` + } + /* + Nothing at all until the URL exists, which is the first thing `firstUpdated` does — and it runs + after this. An `img` rendered without one carries `src=""`, which a browser resolves to the page + itself, fetches, fails to read as an image, and reports as a failed diagram. + */ + if (!this._src) { + return null + } + return html` +
+
+ ${this.caption || 'PlantUML diagram'} +
+ ${this.caption ? html`
${this.caption}
` : null} +
+ ` + } +} + +window.customElements.define('block-plantuml', BlockPlantumlElement) diff --git a/blocks/package-lock.json b/blocks/package-lock.json index 3b475eb12..dd89b71c2 100644 --- a/blocks/package-lock.json +++ b/blocks/package-lock.json @@ -9,12 +9,19 @@ "version": "1.0.0", "license": "AGPL-3.0", "dependencies": { + "@mathjax/mathjax-mhchem-font-extension": "4.1.3", + "@mathjax/mathjax-newcm-font": "4.1.3", + "@mathjax/src": "4.1.3", + "asciinema-player": "3.17.0", "js-yaml": "4.1.0", "leaflet": "1.9.4", "lit": "3.2.1", + "mermaid": "11.16.0", + "pako": "3.0.1", "uqr": "0.1.3" }, "devDependencies": { + "@rollup/plugin-commonjs": "29.0.3", "@rollup/plugin-node-resolve": "15.3.0", "@rollup/plugin-terser": "0.4.4", "glob": "11.0.0", @@ -22,6 +29,40 @@ "rollup-plugin-summary": "2.0.1" } }, + "node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", + "license": "MIT", + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@braintree/sanitize-url": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", + "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==", + "license": "MIT" + }, + "node_modules/@chevrotain/types": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.1.2.tgz", + "integrity": "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==", + "license": "Apache-2.0" + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -33,6 +74,23 @@ "node": ">=0.1.90" } }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "license": "MIT" + }, + "node_modules/@iconify/utils": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.4.tgz", + "integrity": "sha512-b1S7B1k9ohZ+iNTi2ATxbRYG9fTrJmUT0rc46bvVnNxqNRGW7dyo/vRREwyniI5IRN2RSJHDcm+s3BjWrSAjHw==", + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.1.0", + "@iconify/types": "^2.0.0", + "import-meta-resolve": "^4.2.0" + } + }, "node_modules/@isaacs/cliui": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", @@ -108,6 +166,66 @@ "@lit-labs/ssr-dom-shim": "^1.5.0" } }, + "node_modules/@mathjax/mathjax-mhchem-font-extension": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@mathjax/mathjax-mhchem-font-extension/-/mathjax-mhchem-font-extension-4.1.3.tgz", + "integrity": "sha512-WFx0IooitEJq1TXc9V941o8eaZdICSPn8FpsIBDdhkMhb0if61jCvk6vtQN84dlLnNEorK011NfpirMkHYygSQ==", + "license": "Apache-2.0" + }, + "node_modules/@mathjax/mathjax-newcm-font": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@mathjax/mathjax-newcm-font/-/mathjax-newcm-font-4.1.3.tgz", + "integrity": "sha512-gzAB3dFHilHX1l5x2xUqRL+1jDQt3Fyza1DkEMVXWC4E8SvsGdlgEza47HYi2WhVcgfkvf4zgUGzuhbq3Pjlew==", + "license": "Apache-2.0" + }, + "node_modules/@mathjax/src": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@mathjax/src/-/src-4.1.3.tgz", + "integrity": "sha512-rIrWquuBSoJuoMBdC/1qD+AUHTorlccPicoVy6P2xbUgnuDBpCcpbHtOAsB8L3hdCHtNBg92lF8e3Fz+pkcQbw==", + "license": "Apache-2.0", + "dependencies": { + "@mathjax/mathjax-newcm-font": "4.1.3", + "mhchemparser": "^4.2.1", + "mj-context-menu": "^1.0.0", + "speech-rule-engine": "5.0.0-rc.4" + } + }, + "node_modules/@mermaid-js/parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.2.0.tgz", + "integrity": "sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==", + "license": "MIT", + "dependencies": { + "@chevrotain/types": "~11.1.2" + } + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-29.0.3.tgz", + "integrity": "sha512-ZaOxZceP7SOUW7Lqw5IRVweSQYWaeIPnXIGLiB690EBA3FGJTO40EEr2L5yZplJWsgTCogILRSpcAe7+U0Otdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "fdir": "^6.2.0", + "is-reference": "1.2.1", + "magic-string": "^0.30.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=16.0.0 || 14 >= 14.17" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "node_modules/@rollup/plugin-node-resolve": { "version": "15.3.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz", @@ -403,6 +521,289 @@ "win32" ] }, + "node_modules/@solid-primitives/refs": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@solid-primitives/refs/-/refs-1.1.4.tgz", + "integrity": "sha512-bLjwIs6ZPu8NQnuw04sU3Zc8qKSpbc0umUU/O4SHf6oWOdO4+dHY8vb1T7C4b/Tg103+9WGr6sEE0+NFlbaB/A==", + "license": "MIT", + "dependencies": { + "@solid-primitives/utils": "^6.4.1" + }, + "peerDependencies": { + "solid-js": "^1.6.12" + } + }, + "node_modules/@solid-primitives/transition-group": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@solid-primitives/transition-group/-/transition-group-1.1.2.tgz", + "integrity": "sha512-gnHS0OmcdjeoHN9n7Khu8KNrOlRc8a2weETDt2YT6o1zeW/XtUC6Db3Q9pkMU/9cCKdEmN4b0a/41MKAHRhzWA==", + "license": "MIT", + "peerDependencies": { + "solid-js": "^1.6.12" + } + }, + "node_modules/@solid-primitives/utils": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@solid-primitives/utils/-/utils-6.4.1.tgz", + "integrity": "sha512-ISSB5QX1qP2ynrheIpYwc4oKR5Ny4siNuUyf1qZniy+Il+p/PtDB0QK1Dnle8noiHpwRD3gpPdubOC3qI/Zamg==", + "license": "MIT", + "peerDependencies": { + "solid-js": "^1.6.12" + } + }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "license": "MIT" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "license": "MIT", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "license": "MIT" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "license": "MIT" + }, + "node_modules/@types/d3-random": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.4.tgz", + "integrity": "sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "license": "MIT" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "license": "MIT" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -410,6 +811,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, "node_modules/@types/resolve": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", @@ -423,6 +830,25 @@ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "license": "MIT" }, + "node_modules/@upsetjs/venn.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@upsetjs/venn.js/-/venn.js-2.0.0.tgz", + "integrity": "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==", + "license": "MIT", + "optionalDependencies": { + "d3-selection": "^3.0.0", + "d3-transition": "^3.0.1" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.9.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.10.tgz", + "integrity": "sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==", + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", @@ -452,11 +878,21 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "license": "Python-2.0" }, + "node_modules/asciinema-player": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/asciinema-player/-/asciinema-player-3.17.0.tgz", + "integrity": "sha512-JbjNJmA2TLIeYNaOEja+kVSzXadKoqpIzVVmfBGNj2DmdtE/vExBCnkE8NYEcpaQcDvUYg8Ltt0urT80frACmw==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.21.0", + "solid-js": "^1.3.0", + "solid-transition-group": "^0.2.3" + } + }, "node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, "license": "MIT", "engines": { "node": "18 || 20 || >=22" @@ -466,73 +902,608 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/brotli-size": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/brotli-size/-/brotli-size-4.0.0.tgz", + "integrity": "sha512-uA9fOtlTRC0iqKfzff1W34DXUA3GyVqbUaeo3Rw3d4gd1eavKVCETXrn3NzO74W+UVkG3UHu8WxUi+XvKI/huA==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer": "0.1.1" + }, + "engines": { + "node": ">= 10.16.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "license": "MIT", + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^4.0.2" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/cytoscape": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.34.0.tgz", + "integrity": "sha512-62rNSrioXw93uliKFBwjukeQyeWwH2PqDrTac31r2P6464u3AUvTk0xS4LVvT251g7IgkFunrI48ZEZGjywSOg==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "license": "MIT", + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "license": "MIT", + "dependencies": { + "cose-base": "^2.2.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "license": "MIT", + "dependencies": { + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", + "license": "MIT" + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "license": "BSD-3-Clause", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "license": "ISC" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" }, "engines": { - "node": "18 || 20 || >=22" + "node": ">=12" } }, - "node_modules/brotli-size": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/brotli-size/-/brotli-size-4.0.0.tgz", - "integrity": "sha512-uA9fOtlTRC0iqKfzff1W34DXUA3GyVqbUaeo3Rw3d4gd1eavKVCETXrn3NzO74W+UVkG3UHu8WxUi+XvKI/huA==", - "dev": true, - "license": "MIT", + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", "dependencies": { - "duplexer": "0.1.1" + "d3-time": "1 - 3" }, "engines": { - "node": ">= 10.16.0" + "node": ">=12" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } }, - "node_modules/cli-table3": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", - "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", - "dev": true, - "license": "MIT", + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", "dependencies": { - "string-width": "^4.2.0" + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" }, "engines": { - "node": "10.* || >= 12.*" + "node": ">=12" }, - "optionalDependencies": { - "@colors/colors": "1.5.0" + "peerDependencies": { + "d3-selection": "2 - 3" } }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" }, "engines": { - "node": ">= 8" + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz", + "integrity": "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==", + "license": "MIT", + "dependencies": { + "d3": "^7.9.0", + "lodash-es": "^4.17.21" } }, + "node_modules/dayjs": { + "version": "1.11.21", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz", + "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==", + "license": "MIT" + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -543,6 +1514,24 @@ "node": ">=0.10.0" } }, + "node_modules/delaunator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz", + "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/dompurify": { + "version": "3.4.12", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.12.tgz", + "integrity": "sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/duplexer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", @@ -556,6 +1545,17 @@ "dev": true, "license": "MIT" }, + "node_modules/es-toolkit": { + "version": "1.50.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.50.0.tgz", + "integrity": "sha512-OyZKhUVvEep9ITEiwHn8GKnMRQIVqoSIX7WnRbkWgJkllCujilqP2rD0u979tkl8wqyc8ICwlc1UBVv/Sl1G6w==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks", + "tests/types" + ] + }, "node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", @@ -563,6 +1563,24 @@ "dev": true, "license": "MIT" }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/filesize": { "version": "10.1.6", "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.1.6.tgz", @@ -663,6 +1681,12 @@ "dev": true, "license": "MIT" }, + "node_modules/hachure-fill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", + "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", + "license": "MIT" + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -676,6 +1700,37 @@ "node": ">= 0.4" } }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -709,6 +1764,16 @@ "dev": true, "license": "MIT" }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -744,6 +1809,42 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/katex": { + "version": "0.16.47", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz", + "integrity": "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", + "license": "MIT" + }, "node_modules/leaflet": { "version": "1.9.4", "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", @@ -781,21 +1882,82 @@ "@types/trusted-types": "^2.0.2" } }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", + "license": "MIT" + }, "node_modules/lru-cache": { "version": "11.2.6", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", - "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/marked": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", + "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/mermaid": { + "version": "11.16.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.16.0.tgz", + "integrity": "sha512-Zvm3kbstgdpvIJPPItlL7fppIZ3kibvc1oZIGxdvk9t6UFz6flv+Jw7FtRGKwfcI8OckmH04LqG6LlS6X4B1pA==", + "license": "MIT", + "dependencies": { + "@braintree/sanitize-url": "^7.1.2", + "@iconify/utils": "^3.0.2", + "@mermaid-js/parser": "^1.2.0", + "@types/d3": "^7.4.3", + "@upsetjs/venn.js": "^2.0.0", + "cytoscape": "^3.33.3", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", + "d3": "^7.9.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.14", + "dayjs": "^1.11.20", + "dompurify": "^3.3.3", + "es-toolkit": "^1.45.1", + "katex": "^0.16.45", + "khroma": "^2.1.0", + "marked": "^16.3.0", + "roughjs": "^4.6.6", + "stylis": "^4.3.6", + "ts-dedent": "^2.2.0", + "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0" + } + }, + "node_modules/mhchemparser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/mhchemparser/-/mhchemparser-4.2.1.tgz", + "integrity": "sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==", + "license": "Apache-2.0" + }, "node_modules/minimatch": { "version": "10.2.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", - "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.2" @@ -811,19 +1973,54 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } }, + "node_modules/mj-context-menu": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mj-context-menu/-/mj-context-menu-1.0.0.tgz", + "integrity": "sha512-OSgBFQRCVhrZwRa9lhqnKcJU92dd/YXgBjup0uyeuj9bOaVsf4myOyrjU4PfhYkdDk6AqVUTov7v5uFMvIbduA==", + "license": "Apache-2.0", + "dependencies": { + "rimraf": "^6.0.1" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, "license": "BlueOak-1.0.0" }, + "node_modules/package-manager-detector": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.8.0.tgz", + "integrity": "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==", + "license": "MIT" + }, + "node_modules/pako": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pako/-/pako-3.0.1.tgz", + "integrity": "sha512-GupotUUI0mlhugKjUs4bjOwLt3nrehy9Ys2dxC0GtgVef5cnKggkDMmf2bq2poCCuVXopWPmqsc9VDT2iJUy+w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "(MIT AND Zlib)" + }, + "node_modules/path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", + "license": "MIT" + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -845,7 +2042,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", - "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^11.0.0", @@ -871,6 +2067,22 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", + "license": "MIT" + }, + "node_modules/points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "license": "MIT", + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -902,6 +2114,48 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/rimraf": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz", + "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==", + "license": "Unlicense" + }, "node_modules/rollup": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", @@ -965,6 +2219,24 @@ "dev": true, "license": "MIT" }, + "node_modules/roughjs": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", + "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", + "license": "MIT", + "dependencies": { + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -986,6 +2258,12 @@ ], "license": "MIT" }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", @@ -996,6 +2274,27 @@ "randombytes": "^2.1.0" } }, + "node_modules/seroval": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.5.6.tgz", + "integrity": "sha512-rVQVWjjSvlINzaQPZH5JFqsqEsIWdTxY3iJZCnTL/5gQbXIRooVZKI60tVCkOVfzcRPejboxO2t0P89dg5mQaA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/seroval-plugins": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.5.6.tgz", + "integrity": "sha512-HXuLAX2pu/UByPpaeo/TaMfvMIi+1QqIoPJYCcAtU8QkVNwgR6MPlGuCQTErV1JwraaMbYaWVIBX7mppzGLATQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "seroval": "^1.0" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -1042,6 +2341,34 @@ "node": ">=20.0.0" } }, + "node_modules/solid-js": { + "version": "1.9.14", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.14.tgz", + "integrity": "sha512-sAEXC0Kk0S1EDg+8ysEWJDbYhA3RRoEjwuySUGlKIemeo0I5YZfOyumNjNs9Sv3y2nmhD+0rW66ag2HsMuQiGQ==", + "license": "MIT", + "dependencies": { + "csstype": "^3.1.0", + "seroval": "~1.5.4", + "seroval-plugins": "~1.5.4" + } + }, + "node_modules/solid-transition-group": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/solid-transition-group/-/solid-transition-group-0.2.3.tgz", + "integrity": "sha512-iB72c9N5Kz9ykRqIXl0lQohOau4t0dhel9kjwFvx81UZJbVwaChMuBuyhiZmK24b8aKEK0w3uFM96ZxzcyZGdg==", + "license": "MIT", + "dependencies": { + "@solid-primitives/refs": "^1.0.5", + "@solid-primitives/transition-group": "^1.0.2" + }, + "engines": { + "node": ">=18.0.0", + "pnpm": ">=8.6.0" + }, + "peerDependencies": { + "solid-js": "^1.6.12" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -1063,6 +2390,29 @@ "source-map": "^0.6.0" } }, + "node_modules/speech-rule-engine": { + "version": "5.0.0-rc.4", + "resolved": "https://registry.npmjs.org/speech-rule-engine/-/speech-rule-engine-5.0.0-rc.4.tgz", + "integrity": "sha512-3dFcH2QtQNhtvUUc09TxoaEK4WG03B9Z57krFG9jocrKykKGu35BhIkWr0vgEAxZZomEWkeluff69y/EbYzP4Q==", + "license": "Apache-2.0", + "dependencies": { + "@xmldom/xmldom": "^0.9.10", + "commander": "^14.0.3", + "wicked-good-xpath": "^1.3.0" + }, + "bin": { + "sre": "bin/sre" + } + }, + "node_modules/speech-rule-engine/node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -1091,6 +2441,12 @@ "node": ">=8" } }, + "node_modules/stylis": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz", + "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==", + "license": "MIT" + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -1123,12 +2479,43 @@ "node": ">=10" } }, + "node_modules/tinyexec": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/ts-dedent": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.3.0.tgz", + "integrity": "sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==", + "license": "MIT", + "engines": { + "node": ">=6.10" + } + }, "node_modules/uqr": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/uqr/-/uqr-0.1.3.tgz", "integrity": "sha512-0rjE8iEJe4YmT9TOhwsZtqCMRLc5DXZUI2UEYUUg63ikBkqqE5EYWaI0etFe/5KUcmcYwLih2RND1kq+hrUJXA==", "license": "MIT" }, + "node_modules/uuid": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz", + "integrity": "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -1144,6 +2531,12 @@ "engines": { "node": ">= 8" } + }, + "node_modules/wicked-good-xpath": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/wicked-good-xpath/-/wicked-good-xpath-1.3.0.tgz", + "integrity": "sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==", + "license": "MIT" } } } diff --git a/blocks/package.json b/blocks/package.json index e79acebbb..157c0c46d 100644 --- a/blocks/package.json +++ b/blocks/package.json @@ -11,12 +11,19 @@ "author": "Nicolas Giard", "license": "AGPL-3.0", "dependencies": { + "@mathjax/mathjax-mhchem-font-extension": "4.1.3", + "@mathjax/mathjax-newcm-font": "4.1.3", + "@mathjax/src": "4.1.3", + "asciinema-player": "3.17.0", "js-yaml": "4.1.0", "leaflet": "1.9.4", "lit": "3.2.1", + "mermaid": "11.16.0", + "pako": "3.0.1", "uqr": "0.1.3" }, "devDependencies": { + "@rollup/plugin-commonjs": "29.0.3", "@rollup/plugin-node-resolve": "15.3.0", "@rollup/plugin-terser": "0.4.4", "glob": "11.0.0", diff --git a/blocks/rollup.config.mjs b/blocks/rollup.config.mjs index 49d2352b3..03e8d7546 100644 --- a/blocks/rollup.config.mjs +++ b/blocks/rollup.config.mjs @@ -1,6 +1,7 @@ import summary from 'rollup-plugin-summary' import terser from '@rollup/plugin-terser' import resolve from '@rollup/plugin-node-resolve' +import commonjs from '@rollup/plugin-commonjs' import * as glob from 'glob' @@ -120,6 +121,9 @@ export default { blocksManifest(), cssAsString(), resolve(), + // -> A block's own code is ESM, but a library it pulls in need not be: mermaid reaches for dayjs, + // which ships as UMD, and rollup has no notion of `module.exports` without this + commonjs(), terser({ ecma: 2019, module: true diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1edf9513f..feb5d1b7a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -24,7 +24,6 @@ "iconify-icon": "3.0.2", "js-cookie": "3.0.8", "jwt-decode": "4.0.0", - "katex": "0.17.0", "ky": "2.0.2", "lodash-es": "4.18.1", "lowlight": "3.3.0", @@ -45,7 +44,6 @@ "markdown-it-task-lists": "2.1.1", "mitt": "3.0.1", "monaco-editor": "0.55.1", - "pako": "2.1.0", "pinia": "3.0.4", "prosemirror-commands": "1.7.1", "prosemirror-history": "1.5.0", @@ -5711,31 +5709,6 @@ "node": ">=18" } }, - "node_modules/katex": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.17.0.tgz", - "integrity": "sha512-Vdw0ATsQ9V+LuegM/BTwQqV/6cTl5lbGcIrU+BCgLxyf6bo38ybOr372tuSIxir3CN720flu1meYR6XzNMwQnw==", - "funding": [ - "https://opencollective.com/katex", - "https://github.com/sponsors/katex" - ], - "license": "MIT", - "dependencies": { - "commander": "^8.3.0" - }, - "bin": { - "katex": "cli.js" - } - }, - "node_modules/katex/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -6703,12 +6676,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pako": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", - "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", - "license": "(MIT AND Zlib)" - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 8dad1389f..fa484fa0a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -33,7 +33,6 @@ "iconify-icon": "3.0.2", "js-cookie": "3.0.8", "jwt-decode": "4.0.0", - "katex": "0.17.0", "ky": "2.0.2", "lodash-es": "4.18.1", "lowlight": "3.3.0", @@ -54,7 +53,6 @@ "markdown-it-task-lists": "2.1.1", "mitt": "3.0.1", "monaco-editor": "0.55.1", - "pako": "2.1.0", "pinia": "3.0.4", "prosemirror-commands": "1.7.1", "prosemirror-history": "1.5.0", diff --git a/frontend/src/assets/icons.generated.js b/frontend/src/assets/icons.generated.js index 644b7c651..7133b7d24 100644 --- a/frontend/src/assets/icons.generated.js +++ b/frontend/src/assets/icons.generated.js @@ -151,7 +151,6 @@ export const BUNDLED_ICONS = { "mdi:basketball": {"body":"","width":24,"height":24}, "mdi:book-plus": {"body":"","width":24,"height":24}, "mdi:car": {"body":"","width":24,"height":24}, - "mdi:chart-multiline": {"body":"","width":24,"height":24}, "mdi:check": {"body":"","width":24,"height":24}, "mdi:check-circle": {"body":"","width":24,"height":24}, "mdi:checkbox-blank-outline": {"body":"","width":24,"height":24}, @@ -227,6 +226,7 @@ export const BUNDLED_ICONS = { "mdi:marker": {"body":"","width":24,"height":24}, "mdi:marker-cancel": {"body":"","width":24,"height":24}, "mdi:menu-down": {"body":"","width":24,"height":24}, + "mdi:message-alert": {"body":"","width":24,"height":24}, "mdi:minus": {"body":"","width":24,"height":24}, "mdi:near-me": {"body":"","width":24,"height":24}, "mdi:palette": {"body":"","width":24,"height":24}, diff --git a/frontend/src/components/EditorMarkdown.vue b/frontend/src/components/EditorMarkdown.vue index aa4bff1e8..f748c4429 100644 --- a/frontend/src/components/EditorMarkdown.vue +++ b/frontend/src/components/EditorMarkdown.vue @@ -39,11 +39,6 @@ t('editor.markup.insertBlock') }} - - {{ - t('editor.markup.insertDiagram') - }} - {{ t('editor.markup.insertFootnote') @@ -138,23 +133,41 @@ + @click="insertBeforeEachLine({ content: `> `, before: `> [!NOTE]` })"> - + + {{ t('editor.markup.admonitionInfo') }} + @click="insertBeforeEachLine({ content: `> `, before: `> [!TIP]` })"> {{ t('editor.markup.admonitionSuccess') }} + + @click="insertBeforeEachLine({ content: `> `, before: `> [!IMPORTANT]` })"> + + + + {{ t('editor.markup.admonitionImportant') }} + + @@ -162,7 +175,7 @@ + @click="insertBeforeEachLine({ content: `> `, before: `> [!CAUTION]` })"> @@ -628,8 +641,12 @@ function insertAfter({ content, newLine, focus = true }) { /** * Insert content before current line + * + * `before` is a line of its own, put above the first of them — the `> [!NOTE]` that opens an + * admonition. It rides along in that line's own edit rather than as an insertion of its own, so no + * two edits in the batch start at the same position. */ -function insertBeforeEachLine({ content, after, focus = true }) { +function insertBeforeEachLine({ content, before, focus = true }) { const edits = [] for (const selection of editor.getSelections()) { const lineCount = selection.endLineNumber - selection.startLineNumber + 1 @@ -640,18 +657,10 @@ function insertBeforeEachLine({ content, after, focus = true }) { if (lineContent.startsWith(content)) { lineContent = lineContent.substring(content.length) } + const opening = before && line === lines[0] ? `${before}\n` : '' edits.push({ range: new Range(line, 1, line, lineLength + 1), - text: `${content}${lineContent}`, - forceMoveMarkers: true - }) - } - if (after) { - const lastLine = lines.at(-1) - const lineLength = editor.getModel().getLineContent(lastLine).length - edits.push({ - range: new Range(lastLine, lineLength + 1, lastLine, lineLength + 1), - text: `\n${after}`, + text: `${opening}${content}${lineContent}`, forceMoveMarkers: true }) } diff --git a/frontend/src/components/EditorMarkdownConfigOverlay.vue b/frontend/src/components/EditorMarkdownConfigOverlay.vue index aad33d381..325b9559b 100644 --- a/frontend/src/components/EditorMarkdownConfigOverlay.vue +++ b/frontend/src/components/EditorMarkdownConfigOverlay.vue @@ -117,23 +117,6 @@ - - - - {{t(`admin.editors.markdown.latexEngine`)}} - {{t(`admin.editors.markdown.latexEngineHint`)}} - - - - - - @@ -204,82 +187,6 @@ - - -
{{t('admin.editors.markdown.plantuml')}}
-
- - - - {{t(`admin.editors.markdown.plantuml`)}} - {{t(`admin.editors.markdown.plantumlHint`)}} - - - - - - -
- - -
{{t('admin.editors.markdown.kroki')}}
-
- - - - {{t(`admin.editors.markdown.kroki`)}} - {{t(`admin.editors.markdown.krokiHint`)}} - - - - - - -
@@ -326,12 +233,7 @@ function defaultConfig() { quotes: 'english', underline: true, tabWidth: 2, - latexEngine: 'katex', - multimdTable: true, - plantuml: false, - plantumlServerUrl: 'https://www.plantuml.com/plantuml/', - kroki: false, - krokiServerUrl: 'https://kroki.io' + multimdTable: true } } @@ -340,11 +242,6 @@ const state = reactive({ loading: 0 }) -const latexEngines = [ - { value: 'katex', label: 'KaTeX' }, - { value: 'mathjax', label: 'Mathjax' } -] - const quoteStyles = [ { value: 'chinese', label: 'Chinese' }, { value: 'english', label: 'English' }, diff --git a/frontend/src/components/PageHeader.vue b/frontend/src/components/PageHeader.vue index 4643ecc4f..587714d6f 100644 --- a/frontend/src/components/PageHeader.vue +++ b/frontend/src/components/PageHeader.vue @@ -28,7 +28,12 @@ keystroke as the store echoes it back, and a rewritten text node puts the caret at the start of it. `syncEditable` writes it instead, and only when the two have actually diverged. --> -
+ +