diff --git a/docs/content/docs/features/blocks/diagrams.mdx b/docs/content/docs/features/blocks/diagrams.mdx index 605759d0ce..175ecf6564 100644 --- a/docs/content/docs/features/blocks/diagrams.mdx +++ b/docs/content/docs/features/blocks/diagrams.mdx @@ -5,7 +5,7 @@ description: Mermaid diagram blocks for BlockNote — rendered as diagrams in th # Diagrams -The `@blocknote/diagram-block` package adds a **diagram block**: authored as [Mermaid](https://mermaid.js.org/) source in a source popup, rendered as the diagram it describes — flowcharts, sequence diagrams, Gantt charts, and everything else Mermaid supports. +The `@blocknote/diagram-block` package adds a **diagram block**: authored as [Mermaid](https://mermaid.js.org/) source in a source popup and rendered by [Pele](https://pele.run/). Diagram renderers load on demand, supporting flowcharts, sequence diagrams, Gantt charts, and other [supported diagram types](https://pele.run/compatibility). This block is only available in React (`@blocknote/react`). @@ -64,6 +64,31 @@ The example below wires them all up. +## Renderer compatibility + +Stored diagrams and Markdown fences still use Mermaid source, so existing documents need no migration. Pele has its own layout and visual style. Mermaid themes and `themeVariables` do not apply; use [Pele's CSS variables](https://pele.run/theming) to customize previews. Arbitrary HTML labels and external Mermaid plugins such as ZenUML are not supported. + +The renderer switch removes the exported Mermaid-specific helpers `initializeMermaid`, `defaultMermaidOptions`, `trimDiagramSVG`, `setSVGFontFamily`, and `withSVGFontFamily`. If you used these helpers directly, replace rendering with `renderDiagram`, or use `renderDiagramToSVG` / `renderDiagramToImage` for export images. Pass the font family to the renderer instead of rewriting the resulting SVG. Global initialization and DOM-based trimming are no longer needed. + +## Theming + +Diagram previews automatically inherit your editor's theme, including live light/dark changes. No diagram-specific configuration is needed: + +- **Mantine:** uses BlockNote's colors and corner radius, including values from a custom `theme` object. +- **Ariakit / React:** uses the shared `--bn-*` theme variables. +- **Shadcn:** uses the host's semantic color variables (`--background`, `--foreground`, `--muted`, `--border`, `--primary`, and `--destructive`), `--radius`, and `--chart-1` through `--chart-5`. These must contain complete CSS colors, as in Shadcn's current themes. Missing variables fall back to BlockNote's palette. Dark mode follows the host's token changes, typically through its `.dark` class. + +Chart series use BlockNote's highlight colors, or Shadcn's chart tokens when available. To override individual diagram colors without changing the rest of the editor, set [Pele variables](https://pele.run/theming) on your editor container or a diagram: + +```css +.bn-root { + --pele-accent: #6366f1; + --pele-radius: 8px; +} +``` + +Explicit colors authored in Mermaid `style` / `classDef` rules take precedence over theme defaults. Editor theme variables affect previews only; exported images use the standalone renderer's defaults. Use a custom `renderDiagram` export callback to supply a different export palette. + ## Exporting Diagrams export to every format BlockNote supports. [Markdown](/docs/features/export/markdown) works out of the box — diagrams export as ` ```mermaid ` fenced code blocks, their common Markdown notation. @@ -95,9 +120,8 @@ The factory takes one option: ```typescript createDiagramBlockMapping(options?: { /** - * Renders the Mermaid source to an image. Defaults to the built-in - * Mermaid renderer, which only works in the browser - see "Exporting - * server-side" below. + * Renders the Mermaid source to an image. Defaults to Pele, producing + * PNG for DOCX, ODT, and email, or vector SVG for Typst / PDF. */ renderDiagram?: RenderDiagram; }); @@ -109,7 +133,9 @@ The email subpath's factory additionally takes an `imageDelivery` option: some e ### Exporting server-side -Rendering Mermaid source to an image requires a browser, so the built-in renderer only works for client-side exports. When exporting server-side, pass a `renderDiagram` function to `createDiagramBlockMapping` — without one, a server-side export throws: +Typst / PDF exports work on the server out of the box: Pele produces vector SVG without a browser. Labels use the exporter's font family; make those fonts available to the PDF compiler. Exports resolve Pele's default colors so standalone SVG consumers do not need CSS variables; preview-only CSS overrides are not included. + +DOCX, ODT, and email use PNG images. Their built-in rasterizer requires browser canvas. For server-side exports to those formats, pass a `renderDiagram` function to `createDiagramBlockMapping`; without one, the export throws: ```typescript import { createDiagramBlockMapping } from "@blocknote/diagram-block/docx-exporter"; @@ -125,4 +151,4 @@ const renderDiagram: RenderDiagram = async (source) => { createDiagramBlockMapping({ renderDiagram }); ``` -Common choices for the server-side renderer are [`@mermaid-js/mermaid-cli`](https://github.com/mermaid-js/mermaid-cli) (renders in a headless browser) or a [Kroki](https://kroki.io) server. Invalid Mermaid source is an expected failure — return it as `{ error }` rather than throwing, and the export renders the error placeholder for that block instead of failing. +For example, render SVG using Pele's `renderAsync` from `pele/lazy` and rasterize it with a server-side SVG-to-PNG tool. Invalid Mermaid source is an expected failure: return it as `{ error }` rather than throwing, and the export renders the error placeholder for that block instead of failing. diff --git a/docs/package.json b/docs/package.json index ae922fdeeb..bf108e103f 100644 --- a/docs/package.json +++ b/docs/package.json @@ -92,7 +92,6 @@ "lib0": "1.0.0-rc.36", "lucide-react": "^0.562.0", "mathjax-full": "^3.2.2", - "mermaid": "^11.0.0", "motion": "^12.28.1", "next": "^16.3.0", "next-themes": "^0.4.6", diff --git a/packages/diagram-block/package.json b/packages/diagram-block/package.json index db30935c00..63f35c97e6 100644 --- a/packages/diagram-block/package.json +++ b/packages/diagram-block/package.json @@ -76,37 +76,37 @@ "dependencies": { "@blocknote/core": "workspace:^", "@blocknote/react": "workspace:^", - "mermaid": "^11.0.0" + "pele": "^1.5.2" }, "devDependencies": { "@blocknote/shared": "workspace:^", - "react-icons": "^5.5.0", "@blocknote/xl-docx-exporter": "workspace:^", "@blocknote/xl-email-exporter": "workspace:^", "@blocknote/xl-odt-exporter": "workspace:^", + "@blocknote/xl-typst-compiler": "workspace:^", + "@blocknote/xl-typst-exporter": "workspace:^", "@react-email/components": "^1.0.12", "@types/react": "^19.2.3", - "@zip.js/zip.js": "^2.8.8", "@types/react-dom": "^19.2.3", + "@zip.js/zip.js": "^2.8.8", "docx": "^9.9.0", "react": "^19.2.5", "react-dom": "^19.2.5", + "react-icons": "^5.5.0", "rimraf": "^5.0.10", "rollup-plugin-webpack-stats": "^0.2.6", "typescript": "^7.0.2", - "vite-plus": "catalog:", - "@blocknote/xl-typst-exporter": "workspace:^", - "@blocknote/xl-typst-compiler": "workspace:^" + "vite-plus": "catalog:" }, "peerDependencies": { "@blocknote/xl-docx-exporter": "workspace:^", "@blocknote/xl-email-exporter": "workspace:^", "@blocknote/xl-odt-exporter": "workspace:^", + "@blocknote/xl-typst-exporter": "workspace:^", "@react-email/components": "^1.0.12", "docx": "^9.8.0", "react": "^18.0 || ^19.0 || >= 19.0.0-rc", - "react-dom": "^18.0 || ^19.0 || >= 19.0.0-rc", - "@blocknote/xl-typst-exporter": "workspace:^" + "react-dom": "^18.0 || ^19.0 || >= 19.0.0-rc" }, "peerDependenciesMeta": { "@blocknote/xl-docx-exporter": { diff --git a/packages/diagram-block/src/block/createReactDiagramBlockSpec.test.tsx b/packages/diagram-block/src/block/createReactDiagramBlockSpec.test.tsx index 9880c30d6d..54e931c10f 100644 --- a/packages/diagram-block/src/block/createReactDiagramBlockSpec.test.tsx +++ b/packages/diagram-block/src/block/createReactDiagramBlockSpec.test.tsx @@ -17,22 +17,13 @@ import { createReactDiagramBlockSpec } from "./createReactDiagramBlockSpec.js"; * @vitest-environment jsdom */ -// Mermaid needs a real browser to render, so the tests mock it - the specs -// under test cover the block's editing behavior, not Mermaid itself. Sources -// containing "INVALID" fail to parse, for testing the error states. -vi.mock("mermaid", () => ({ - default: { - initialize: vi.fn(), - parse: vi.fn(async (source: string) => { - if (source.includes("INVALID")) { - throw new Error("mock parse error"); - } - return true; - }), - render: vi.fn(async () => ({ - svg: '', - })), - }, +// Cover popup behavior separately from the real Pele renderer's unit tests. +vi.mock("../helpers/renderDiagram.js", () => ({ + renderDiagram: vi.fn(async (source: string) => + source.includes("INVALID") + ? { error: "mock parse error" } + : { svg: '' }, + ), })); // The diagram block isn't a default block, so register it in a custom schema. @@ -64,7 +55,7 @@ describe("Diagram block source popup", () => { flushSync(() => { root.render(); }); - // Let the React node view render (and the mocked Mermaid "render") before + // Let the React node view render (and the mocked diagram renderer) before // assertions read its DOM. await flush(); @@ -117,7 +108,7 @@ describe("Diagram block source popup", () => { it("renders the (mocked) diagram preview", () => { expect( previewRoot().querySelector( - '.bn-preview-container [data-mermaid-mock="true"]', + '.bn-preview-container [data-pele-mock="true"]', ), ).not.toBeNull(); }); @@ -192,7 +183,7 @@ describe("Diagram block source popup", () => { /** The mocked rendered diagram, if currently shown as the preview. */ function renderedDiagram(): Element | null { return previewRoot().querySelector( - '.bn-preview-container [data-mermaid-mock="true"]', + '.bn-preview-container [data-pele-mock="true"]', ); } diff --git a/packages/diagram-block/src/block/createReactDiagramBlockSpec.tsx b/packages/diagram-block/src/block/createReactDiagramBlockSpec.tsx index 6a85ec9ebf..11f190b596 100644 --- a/packages/diagram-block/src/block/createReactDiagramBlockSpec.tsx +++ b/packages/diagram-block/src/block/createReactDiagramBlockSpec.tsx @@ -11,9 +11,7 @@ export const createDiagramBlockConfig = createBlockConfig( () => ({ type: "diagram" as const, - // The block is semantically a diagram; Mermaid is its (only, for now) - // rendering engine. An `engine` prop with a "mermaid" default can be - // added later without breaking stored documents. + // Store Mermaid source, independently of the renderer (Pele). propSchema: {}, content: "plain" as const, }) as const, diff --git a/packages/diagram-block/src/block/diagramTheme.browser.test.ts b/packages/diagram-block/src/block/diagramTheme.browser.test.ts new file mode 100644 index 0000000000..5ae2c40429 --- /dev/null +++ b/packages/diagram-block/src/block/diagramTheme.browser.test.ts @@ -0,0 +1,104 @@ +import { afterEach, describe, expect, test } from "vite-plus/test"; + +import "../../../react/src/editor/styles.css"; +import "../../../shadcn/src/style.css"; +import { renderDiagram } from "../helpers/renderDiagram.js"; + +// Real SVG/computed CSS is needed: node tests cannot observe inherited theme +// variables or a live color-scheme change. No editor machinery is involved. +const roots: HTMLElement[] = []; + +afterEach(() => { + for (const root of roots.splice(0)) { + root.remove(); + } +}); + +async function diagram( + className = "", + source = "graph TD\n A[Start] --> B[End]", +) { + const result = await renderDiagram(source); + if (result.error !== undefined) { + throw new Error(result.error); + } + const root = document.createElement("div"); + root.className = `bn-root ${className}`; + root.dataset.colorScheme = "light"; + root.innerHTML = result.svg; + roots.push(root); + document.body.append(root); + const svg = root.querySelector("svg")!; + const label = svg.querySelector("text")!; + const node = svg.querySelector(".pele-node rect")!; + return { root, svg, label, node }; +} + +describe("diagram themes", () => { + test("follows live light/dark changes without replacing the SVG", async () => { + const { root, svg, label, node } = await diagram("bn-mantine"); + expect(getComputedStyle(label).fill).toBe("rgb(63, 63, 63)"); + expect(getComputedStyle(node).fill).toBe("rgb(239, 239, 239)"); + + root.dataset.colorScheme = "dark"; + expect(getComputedStyle(label).fill).toBe("rgb(207, 207, 207)"); + expect(getComputedStyle(node).fill).toBe("rgb(22, 22, 22)"); + expect(root.querySelector("svg")).toBe(svg); + }); + + test("uses custom theme variables and keeps editors isolated", async () => { + const first = await diagram("bn-mantine"); + const second = await diagram("bn-ak"); + // Mantine's custom theme objects set these variables on the themed root; + // CSS themes can set the same variables without depending on Mantine. + first.root.style.setProperty("--bn-colors-editor-text", "#123456"); + first.root.style.setProperty("--bn-colors-hovered-background", "#abcdef"); + first.root.style.setProperty("--bn-colors-border", "#345678"); + first.root.style.setProperty("--bn-border-radius", "12px"); + expect(getComputedStyle(first.label).fill).toBe("rgb(18, 52, 86)"); + expect(getComputedStyle(first.node).fill).toBe("rgb(171, 205, 239)"); + expect(getComputedStyle(first.node).stroke).toBe("rgb(52, 86, 120)"); + expect( + getComputedStyle(first.svg).getPropertyValue("--pele-radius").trim(), + ).toBe("12px"); + expect(getComputedStyle(second.label).fill).toBe("rgb(63, 63, 63)"); + }); + + test("Shadcn uses its semantic tokens and updates with the host theme", async () => { + const { root, label, node } = await diagram("bn-shadcn"); + root.style.setProperty("--foreground", "#112233"); + root.style.setProperty("--muted", "#ddeeff"); + root.style.setProperty("--border", "#445566"); + expect(getComputedStyle(label).fill).toBe("rgb(17, 34, 51)"); + expect(getComputedStyle(node).fill).toBe("rgb(221, 238, 255)"); + expect(getComputedStyle(node).stroke).toBe("rgb(68, 85, 102)"); + + root.style.setProperty("--foreground", "#ddeeff"); + root.style.setProperty("--muted", "#112233"); + expect(getComputedStyle(label).fill).toBe("rgb(221, 238, 255)"); + expect(getComputedStyle(node).fill).toBe("rgb(17, 34, 51)"); + }); + + test("chart colors follow BlockNote highlights and Shadcn chart tokens", async () => { + const source = 'pie\n "One" : 3\n "Two" : 2'; + const first = await diagram("bn-mantine", source); + const second = await diagram("bn-shadcn", source); + first.root.style.setProperty("--bn-colors-highlights-blue-text", "#123456"); + second.root.style.setProperty("--chart-1", "#654321"); + expect(getComputedStyle(first.svg.querySelector(".pele-slice")!).fill).toBe( + "rgb(18, 52, 86)", + ); + expect( + getComputedStyle(second.svg.querySelector(".pele-slice")!).fill, + ).toBe("rgb(101, 67, 33)"); + }); + + test("explicit Pele overrides still win and unconfigured Shadcn falls back", async () => { + const { root, label, node } = await diagram("bn-shadcn"); + expect(getComputedStyle(label).fill).toBe("rgb(63, 63, 63)"); + root.style.setProperty("--pele-fg", "#654321"); + root.style.setProperty("--pele-surface", "#abcdef"); + expect(getComputedStyle(label).fill).toBe("rgb(101, 67, 33)"); + expect(getComputedStyle(node).fill).toBe("rgb(171, 205, 239)"); + }); +}); diff --git a/packages/diagram-block/src/block/helpers/render/DiagramBlockPreviewWithPopup.browser.test.tsx b/packages/diagram-block/src/block/helpers/render/DiagramBlockPreviewWithPopup.browser.test.tsx new file mode 100644 index 0000000000..358de7f073 --- /dev/null +++ b/packages/diagram-block/src/block/helpers/render/DiagramBlockPreviewWithPopup.browser.test.tsx @@ -0,0 +1,92 @@ +import { Component, ReactNode } from "react"; +import { createRoot, Root } from "react-dom/client"; +import { afterEach, beforeEach, expect, test, vi } from "vite-plus/test"; + +import { renderDiagram } from "../../../helpers/renderDiagram.js"; +import { useDiagramSVG } from "./DiagramBlockPreviewWithPopup.js"; + +vi.mock("../../../helpers/renderDiagram.js", () => ({ + renderDiagram: vi.fn(), +})); + +type BoundaryState = { status: "healthy" } | { status: "failed" }; + +class Boundary extends Component< + { children: ReactNode; onError: (error: unknown) => void }, + BoundaryState +> { + state: BoundaryState = { status: "healthy" }; + + static getDerivedStateFromError(): BoundaryState { + return { status: "failed" }; + } + + componentDidCatch(error: Error) { + this.props.onError(error); + } + + render() { + return this.state.status === "failed" ? ( + Unexpected rendering failure + ) : ( + this.props.children + ); + } +} + +function Preview({ source }: { source: string }) { + const { svg, error } = useDiagramSVG(source); + return {error ?? svg}; +} + +let host: HTMLDivElement; +let root: Root; + +beforeEach(() => { + host = document.createElement("div"); + document.body.append(host); + // These tests deliberately exercise the boundary, not console reporting. + root = createRoot(host, { onCaughtError() {} }); +}); + +afterEach(() => { + root.unmount(); + host.remove(); + vi.resetAllMocks(); +}); + +test("unexpected renderer rejections reach the boundary without leaking messages", async () => { + const failure = new Error("private infrastructure details"); + vi.mocked(renderDiagram).mockRejectedValue(failure); + const onError = vi.fn(); + + root.render( + + + , + ); + + await vi.waitFor(() => { + expect(onError).toHaveBeenCalledWith(failure); + expect(host.textContent).toBe("Unexpected rendering failure"); + }); + expect(host.textContent).not.toContain(failure.message); +}); + +test("expected source errors remain inline instead of reaching the boundary", async () => { + vi.mocked(renderDiagram).mockResolvedValue({ + error: "Invalid diagram source", + }); + const onError = vi.fn(); + + root.render( + + + , + ); + + await vi.waitFor(() => { + expect(host.textContent).toBe("Invalid diagram source"); + }); + expect(onError).not.toHaveBeenCalled(); +}); diff --git a/packages/diagram-block/src/block/helpers/render/DiagramBlockPreviewWithPopup.tsx b/packages/diagram-block/src/block/helpers/render/DiagramBlockPreviewWithPopup.tsx index 68331463ae..9833361905 100644 --- a/packages/diagram-block/src/block/helpers/render/DiagramBlockPreviewWithPopup.tsx +++ b/packages/diagram-block/src/block/helpers/render/DiagramBlockPreviewWithPopup.tsx @@ -3,29 +3,24 @@ import { ReactCustomBlockRenderProps, SourceBlockWithPreview, } from "@blocknote/react"; -import mermaid from "mermaid"; import { useEffect, useState } from "react"; import { SiMermaid } from "react-icons/si"; import { plainContentToString } from "@blocknote/core"; -import { initializeMermaid } from "../../../helpers/initializeMermaid.js"; -import { withSVGFontFamily } from "../../../helpers/svgFontFamily.js"; -import { trimDiagramSVG } from "../../../helpers/trimDiagramSVG.js"; +import { renderDiagram } from "../../../helpers/renderDiagram.js"; import { getDiagramDictionary } from "../../../i18n/dictionary.js"; import { DiagramBlockConfig } from "../../createReactDiagramBlockSpec.js"; -// Each render call needs its own element ID. -let mermaidElementId = 0; - /** * Renders the Mermaid source to an SVG string. The current diagram (or the * last valid one, when the source has an error) stays up until the new one * has fully rendered, and swapping inline SVG commits in a single frame - so * the preview never flashes. */ -export const useMermaidSVG = (source: string, fontFamilyElement?: Element) => { +export function useDiagramSVG(source: string, fontFamilyElement?: Element) { const [svg, setSVG] = useState(""); const [error, setError] = useState(undefined); + const [failure, setFailure] = useState<{ cause: unknown }>(); useEffect(() => { if (!source.trim()) { @@ -38,8 +33,6 @@ export const useMermaidSVG = (source: string, fontFamilyElement?: Element) => { return; } - initializeMermaid(); - // Read the font at render time, not at mount - the computed font is // themeable via CSS, so a memoized read would go stale on theme changes. const fontFamily = fontFamilyElement @@ -49,37 +42,37 @@ export const useMermaidSVG = (source: string, fontFamilyElement?: Element) => { // Rendering is asynchronous, so bail out if the source changed (or the // block was removed) before it finished. let stale = false; - void (async () => { - // The rendered SVG carries the given ID into the document, and Mermaid - // removes any existing element with that ID when rendering. So each - // render gets a fresh ID - reusing one makes Mermaid yank the displayed - // diagram out of the page mid-render. - const id = `mermaid-preview-${mermaidElementId++}`; - try { - await mermaid.parse(source); - const { svg } = await mermaid.render(id, source); - if (!stale) { - // Diagrams display in the editor's own font (see - // `setSVGFontFamily`) - the same treatment the export renderers - // apply, so the preview shows what exports produce. - const trimmed = trimDiagramSVG(svg); - setSVG(fontFamily ? withSVGFontFamily(trimmed, fontFamily) : trimmed); + void renderDiagram(source, { fontFamily }) + .then((result) => { + if (stale) { + return; + } + if (result.error !== undefined) { + setError(result.error); + } else { + setSVG(result.svg); setError(undefined); } - } catch (err) { + }) + .catch((cause: unknown) => { if (!stale) { - setError(err instanceof Error ? err.message : String(err)); + // Unexpected failures belong to the host's React error boundary, not + // the source-error UI. Never expose arbitrary exception messages there. + setFailure({ cause }); } - } - })(); + }); return () => { stale = true; }; }, [source, fontFamilyElement]); + if (failure) { + throw failure.cause; + } + return { svg, error }; -}; +} export const DiagramBlockPreviewWithPopup = ( props: ReactCustomBlockRenderProps, ) => { @@ -87,7 +80,7 @@ export const DiagramBlockPreviewWithPopup = ( // Diagrams render in the editor's computed font, so they match the document // they live in - the block renders inside the editor, so the element is // mounted by the time the hook's effect reads its style. - const { svg, error } = useMermaidSVG(source, props.editor.domElement); + const { svg, error } = useDiagramSVG(source, props.editor.domElement); const dict = getDiagramDictionary(props.editor).block; return ( @@ -101,7 +94,7 @@ export const DiagramBlockPreviewWithPopup = ( preview={ svg ? (
{ }); it("should throw a descriptive error without a renderer outside the browser", async () => { - // The built-in Mermaid renderer can't work here, and silently degrading + // The built-in PNG rasterizer can't work here, and silently degrading // is worse than failing loudly - the error names the `renderDiagram` // option to pass. const exporter = createExporter(diagramBlockMapping); diff --git a/packages/diagram-block/src/docx-exporter/index.ts b/packages/diagram-block/src/docx-exporter/index.ts index 6775ad09a4..b42741ab77 100644 --- a/packages/diagram-block/src/docx-exporter/index.ts +++ b/packages/diagram-block/src/docx-exporter/index.ts @@ -47,10 +47,9 @@ function errorParagraph( /** * Creates a DOCX block mapping for `@blocknote/diagram-block` that embeds - * diagrams as images. Rendering runs in the browser by default (Mermaid - * can't render outside of it); when exporting elsewhere (e.g. server-side), - * pass a `renderDiagram` function backed by e.g. `@mermaid-js/mermaid-cli` - * or a Kroki server. Invalid sources render an error placeholder (mirroring + * diagrams as PNG images using Pele and browser canvas. When exporting + * server-side, pass a `renderDiagram` function with a server-side rasterizer. + * Invalid sources render an error placeholder (mirroring * the editor): * * ```ts @@ -82,7 +81,7 @@ export function createDiagramBlockMapping(options?: { (typeof document !== "undefined" ? renderDiagramToImage : undefined); if (!renderDiagram) { throw new Error( - "Rendering diagrams to images requires a browser. When exporting elsewhere, pass a `renderDiagram` function to `createDiagramBlockMapping` (e.g. backed by @mermaid-js/mermaid-cli or a Kroki server).", + "Default PNG rasterization requires a browser. When exporting elsewhere, pass a `renderDiagram` function with a server-side rasterizer to `createDiagramBlockMapping`.", ); } diff --git a/packages/diagram-block/src/email-exporter/index.tsx b/packages/diagram-block/src/email-exporter/index.tsx index 4d99388b40..5073ab5fd1 100644 --- a/packages/diagram-block/src/email-exporter/index.tsx +++ b/packages/diagram-block/src/email-exporter/index.tsx @@ -30,10 +30,10 @@ const MAX_WIDTH_PIXELS = 600; /** * Creates an email block mapping for `@blocknote/diagram-block` that embeds * diagrams as images, with the Mermaid source as the alt text. Rendering - * runs in the browser by default (Mermaid can't render outside of it); when + * uses Pele with browser PNG rasterization by default; when * exporting elsewhere (e.g. server-side email rendering), pass a - * `renderDiagram` function backed by e.g. `@mermaid-js/mermaid-cli` or a - * Kroki server. Images are embedded as data URLs by default; pass an + * `renderDiagram` function with a server-side rasterizer. + * Images are embedded as data URLs by default; pass an * `imageDelivery` (e.g. `createCIDImageDelivery` from * `@blocknote/xl-email-exporter`) to deliver them as inline `cid:` * attachments instead, which more email clients display. Invalid sources @@ -69,7 +69,7 @@ export function createDiagramBlockMapping(options?: { (typeof document !== "undefined" ? renderDiagramToImage : undefined); if (!renderDiagram) { throw new Error( - "Rendering diagrams to images requires a browser. When exporting elsewhere, pass a `renderDiagram` function to `createDiagramBlockMapping` (e.g. backed by @mermaid-js/mermaid-cli or a Kroki server).", + "Default PNG rasterization requires a browser. When exporting elsewhere, pass a `renderDiagram` function with a server-side rasterizer to `createDiagramBlockMapping`.", ); } diff --git a/packages/diagram-block/src/exporterTestUtil.ts b/packages/diagram-block/src/exporterTestUtil.ts index 9ce4f87b89..7b2dc5e200 100644 --- a/packages/diagram-block/src/exporterTestUtil.ts +++ b/packages/diagram-block/src/exporterTestUtil.ts @@ -15,8 +15,8 @@ export const diagramDocument = [ }, ] as any; -// A stub renderer, standing in for e.g. mermaid-cli on a server - the real -// (browser-only) Mermaid rendering is covered by its `.browser.test`. +// A stub rasterizer, standing in for a custom server PNG renderer. Real Pele +// SVG rendering is covered by node tests; browser tests cover PNG rasterization. export const renderDiagram: RenderDiagram = async () => ({ image: { mimeType: "image/png", @@ -31,7 +31,7 @@ export const renderInvalidDiagram: RenderDiagram = async () => ({ error: "No diagram type detected", }); -// An SVG-producing stub, standing in for the browser `renderDiagramToSVG` +// An SVG-producing stub, standing in for the default `renderDiagramToSVG` // default of the Typst exporter mapping. export const renderDiagramSVG: RenderDiagram = async () => ({ image: { diff --git a/packages/diagram-block/src/helpers/index.ts b/packages/diagram-block/src/helpers/index.ts index ab7e3cfbe5..5be114a635 100644 --- a/packages/diagram-block/src/helpers/index.ts +++ b/packages/diagram-block/src/helpers/index.ts @@ -1,5 +1,3 @@ -export * from "./initializeMermaid.js"; +export * from "./renderDiagram.js"; export * from "./renderDiagramToImage.js"; export * from "./renderDiagramToSVG.js"; -export * from "./svgFontFamily.js"; -export * from "./trimDiagramSVG.js"; diff --git a/packages/diagram-block/src/helpers/initializeMermaid.ts b/packages/diagram-block/src/helpers/initializeMermaid.ts deleted file mode 100644 index a4096098f9..0000000000 --- a/packages/diagram-block/src/helpers/initializeMermaid.ts +++ /dev/null @@ -1,46 +0,0 @@ -import mermaid, { type MermaidConfig } from "mermaid"; - -/** - * The rendering options every BlockNote diagram render uses - applied once - * through `initializeMermaid`, so the editor preview and the export - * renderers all draw the same way. - * - * Labels render as SVG text instead of Mermaid's default HTML labels: HTML - * labels live in `` elements that only browsers display - - * every other SVG consumer (Typst's renderer, resvg, Office viewers) - * silently drops them, and exports would diverge from the preview. SVG text - * keeps label wrapping, `
` and markdown-string formatting; the one - * thing it can't render is raw inline HTML in label text, which then shows - * as literal tags - consistently, in the preview as well as in exports. - * - * (The cast: `MermaidConfig` doesn't declare `htmlLabels` for every diagram - * type that honors it at runtime.) - */ -export const defaultMermaidOptions = { - htmlLabels: false, - flowchart: { htmlLabels: false }, - class: { htmlLabels: false }, - state: { htmlLabels: false }, - er: { htmlLabels: false }, -} as MermaidConfig; - -// The diagrams are rendered manually whenever a block's source changes. -let initialized = false; - -// Mermaid config is page-global: a consumer app that also uses Mermaid and -// calls `initialize` itself will overwrite these options (last write wins) -// - and with them, the SVG-label rendering the exporters rely on. -export const initializeMermaid = () => { - if (!initialized) { - initialized = true; - mermaid.initialize({ - ...defaultMermaidOptions, - startOnLoad: false, - // On render errors, makes Mermaid throw right away - instead of - // rendering its own error graphic AND leaving its temporary render - // element behind in the document (it only cleans the element up with - // this option set). The block renders its own error UI. - suppressErrorRendering: true, - }); - } -}; diff --git a/packages/diagram-block/src/helpers/renderDiagram.ts b/packages/diagram-block/src/helpers/renderDiagram.ts new file mode 100644 index 0000000000..c57395180d --- /dev/null +++ b/packages/diagram-block/src/helpers/renderDiagram.ts @@ -0,0 +1,29 @@ +import { PeleError, renderAsync } from "pele/lazy"; + +export const DEFAULT_DIAGRAM_FONT_FAMILY = '"Inter", sans-serif'; + +let diagramId = 0; + +/** Shared preview/export adapter. Only invalid user input becomes an error value. */ +export async function renderDiagram( + source: string, + options: { fontFamily?: string; responsive?: boolean } = {}, +): Promise< + | ({ error?: undefined } & Awaited>) + | { error: string } +> { + try { + return await renderAsync(source, { + ...options, + fontFamily: options.fontFamily ?? DEFAULT_DIAGRAM_FONT_FAMILY, + idPrefix: `bn-diagram-${diagramId++}`, + // Preserve the source direction, consistently in previews and exports. + autoDirection: false, + }); + } catch (error) { + if (error instanceof PeleError) { + return { error: error.message }; + } + throw error; + } +} diff --git a/packages/diagram-block/src/helpers/renderDiagramToImage.browser.test.ts b/packages/diagram-block/src/helpers/renderDiagramToImage.browser.test.ts index 8727efb465..7d38b32260 100644 --- a/packages/diagram-block/src/helpers/renderDiagramToImage.browser.test.ts +++ b/packages/diagram-block/src/helpers/renderDiagramToImage.browser.test.ts @@ -4,7 +4,7 @@ import { describe, expect, test } from "vite-plus/test"; import { renderDiagramToImage } from "./renderDiagramToImage.js"; -// Browser unit tests for the browser-only Mermaid renderer - the +// Browser unit tests for the browser-only PNG renderer - the // `RenderDiagram` implementation that the (node) unit suites replace with // stubs. Runs in the tests package's browser suite. describe("renderDiagramToImage", () => { @@ -25,10 +25,8 @@ describe("renderDiagramToImage", () => { await decodeAndSample(exportImageToDataURL(result.image)); expect(inkedPixels).toBeGreaterThan(0); // The diagram must fill the image, not sit letterboxed in a fraction - // of it - Mermaid crops the view box to the content bar ~8px padding, - // so a healthy render inks ~0.85+ of the canvas. (WebKit regression - // check: it lets Mermaid's inline max-width style shrink the - // rasterization to ~half if the renderer doesn't strip it.) + // of it. Fixed intrinsic SVG dimensions must not be capped by + // responsive sizing styles, including when WebKit rasterizes them. expect(inkedFractionX).toBeGreaterThan(0.75); expect(inkedFractionY).toBeGreaterThan(0.75); }, @@ -38,7 +36,7 @@ describe("renderDiagramToImage", () => { "returns invalid Mermaid source as a typed error", { timeout: 15000 }, async () => { - // The real Mermaid parse boundary: invalid source is expected (it's + // The real Pele parse boundary: invalid source is expected (it's // user input), so it comes back as a typed error rather than a throw. const result = await renderDiagramToImage("not a valid diagram !!"); diff --git a/packages/diagram-block/src/helpers/renderDiagramToImage.ts b/packages/diagram-block/src/helpers/renderDiagramToImage.ts index e7bddaac0e..5dec3527a2 100644 --- a/packages/diagram-block/src/helpers/renderDiagramToImage.ts +++ b/packages/diagram-block/src/helpers/renderDiagramToImage.ts @@ -1,17 +1,14 @@ import type { ExportImage } from "@blocknote/core"; import { renderToSizedSVG } from "./renderToSizedSVG.js"; -import { DEFAULT_DIAGRAM_FONT_FAMILY } from "./svgFontFamily.js"; /** * Renders Mermaid source to an {@link ExportImage} (with the diagram's * natural dimensions in pixels). Invalid sources are expected (they're user * input), so they're returned as a typed error - with a message safe to show * to readers - rather than thrown; unexpected failures (environment, - * renderer infrastructure) throw. The default implementations - * ({@link renderDiagramToImage}, {@link renderDiagramToSVG}) need a browser - * - Mermaid can't render outside of it; exporters running elsewhere plug in - * their own (e.g. backed by `@mermaid-js/mermaid-cli` or a Kroki server). + * renderer infrastructure) throw. PNG rasterization needs a browser; + * {@link renderDiagramToSVG} also works on the server using Pele. */ export type RenderDiagram = ( source: string, @@ -28,28 +25,26 @@ const DEFAULT_RASTER_SCALE = 2; * as images when exporting documents to PDF/DOCX/ODT. Labels rasterize in * `fontFamily` (resolved by the browser, so the family should be loaded in * the page) - by default BlockNote's UI font, so exported diagrams match - * the surrounding document rather than standing out in Mermaid's own font. - * Invalid sources are returned as a typed error. Browser-only - Mermaid - * can't render outside of it. + * the surrounding document. Invalid sources are returned as a typed error. + * Browser-only because rasterization uses Image and canvas. */ export async function renderDiagramToImage( source: string, scale: number = DEFAULT_RASTER_SCALE, - fontFamily: string = DEFAULT_DIAGRAM_FONT_FAMILY, + fontFamily?: string, ): ReturnType { // The explicit dimensions must be on the SVG itself, not the `Image` // element below: the element's width/height only affect layout, while // canvas rasterization uses the SVG's intrinsic size - without one, Safari // falls back to a default size (and older Firefox refuses to draw the - // image to a canvas at all). WebKit also honors a leftover `max-width` - // style during canvas rasterization - rendering the diagram letterboxed at - // a fraction of the canvas - so the stripped style matters here too. The - // scale goes into the SVG's dimensions (rather than only the canvas), so + // image to a canvas at all). Pele's fixed-size output also avoids responsive + // max-width styles that can letterbox the image in WebKit. The scale goes + // into the SVG's dimensions (rather than only the canvas), so // browsers rasterize the vector at the full canvas resolution instead of // upscaling a 1x raster. const result = await renderToSizedSVG(source, { scale, fontFamily }); if (result.error !== undefined) { - return { error: result.error }; + return result; } const { svg, width, height } = result; diff --git a/packages/diagram-block/src/helpers/renderDiagramToSVG.browser.test.ts b/packages/diagram-block/src/helpers/renderDiagramToSVG.test.ts similarity index 84% rename from packages/diagram-block/src/helpers/renderDiagramToSVG.browser.test.ts rename to packages/diagram-block/src/helpers/renderDiagramToSVG.test.ts index 791c9dee0e..7c33c02fa7 100644 --- a/packages/diagram-block/src/helpers/renderDiagramToSVG.browser.test.ts +++ b/packages/diagram-block/src/helpers/renderDiagramToSVG.test.ts @@ -2,8 +2,7 @@ import { describe, expect, test } from "vite-plus/test"; import { renderDiagramToSVG } from "./renderDiagramToSVG.js"; -// Browser unit tests for the browser-only SVG renderer used by the Typst -// exporter mapping. Runs in the tests package's browser suite. +// The SVG export adapter works without a browser DOM. describe("renderDiagramToSVG", () => { test( "renders Mermaid source to sized, foreignObject-free SVG", @@ -29,7 +28,8 @@ describe("renderDiagramToSVG", () => { // Explicit intrinsic dimensions, and no `max-width` style capping them. expect(svg).toMatch(/]*\bwidth="\d+"/); expect(svg).toMatch(/]*\bheight="\d+"/); - expect(svg).not.toMatch(/]*style="/); + expect(svg).not.toContain("max-width"); + expect(svg).not.toContain("var(--"); }, ); @@ -48,9 +48,6 @@ describe("renderDiagramToSVG", () => { } const svg = new TextDecoder().decode(result.image.data); expect(svg).toContain("Inter 18pt"); - // The rewrite must catch every declaration - a leftover default stack - // would render some labels in a different font. - expect(svg).not.toContain("trebuchet"); }, ); diff --git a/packages/diagram-block/src/helpers/renderDiagramToSVG.ts b/packages/diagram-block/src/helpers/renderDiagramToSVG.ts index fe595af6c5..e873a659c2 100644 --- a/packages/diagram-block/src/helpers/renderDiagramToSVG.ts +++ b/packages/diagram-block/src/helpers/renderDiagramToSVG.ts @@ -5,15 +5,13 @@ import { renderToSizedSVG } from "./renderToSizedSVG.js"; * Renders the Mermaid source to an SVG {@link ExportImage} (with the * diagram's natural dimensions in pixels) - for exporters whose target can * embed vector SVG (e.g. Typst), keeping diagrams crisp at any zoom. The - * output displays anywhere: like all BlockNote diagram rendering, labels - * are SVG text (see `defaultMermaidOptions`), not browser-only `` - * HTML. Invalid sources are returned as a typed error. Browser-only - - * Mermaid can't render outside of it. + * output displays anywhere: Pele renders labels as SVG text, not browser-only + * `` HTML. Invalid sources are returned as a typed error. + * Works in browsers and on the server. * * Pass `fontFamily` (a CSS font-family list) to declare labels in the - * document's own font rather than Mermaid's default - applied by rewriting - * the rendered SVG's font declarations, so it takes effect wherever the SVG - * is consumed. For faithful output the family should be resolvable by the + * document's own font, used for both label measurement and SVG output. + * For faithful output the family should be resolvable by the * SVG consumer - e.g. the same family the exporter's fonts are declared * under. */ @@ -25,7 +23,7 @@ export async function renderDiagramToSVG( fontFamily: options?.fontFamily, }); if (result.error !== undefined) { - return { error: result.error }; + return result; } return { diff --git a/packages/diagram-block/src/helpers/renderToSizedSVG.test.ts b/packages/diagram-block/src/helpers/renderToSizedSVG.test.ts new file mode 100644 index 0000000000..ccd57adaf8 --- /dev/null +++ b/packages/diagram-block/src/helpers/renderToSizedSVG.test.ts @@ -0,0 +1,74 @@ +import { describe, expect, it } from "vite-plus/test"; + +import { renderToSizedSVG } from "./renderToSizedSVG.js"; + +describe("renderToSizedSVG", () => { + it.each([ + "graph TD\n A[Start] --> B[End]", + "sequenceDiagram\n Alice->>Bob: Hello", + "classDiagram\n Animal <|-- Duck", + "stateDiagram-v2\n [*] --> Active", + 'pie\n "One" : 1\n "Two" : 2', + "journey\n title My day\n section Work\n Task: 5: Me", + ])("renders Mermaid syntax without a browser: %s", async (source) => { + const result = await renderToSizedSVG(source); + expect(result.error).toBeUndefined(); + if (result.error !== undefined) { + throw new Error(result.error); + } + expect(result.svg).toContain("pele"); + expect(result.svg).not.toContain(" { + const result = await renderToSizedSVG("graph TD\n A --> B", { scale: 2 }); + if (result.error !== undefined) { + throw new Error(result.error); + } + expect(result.svg).toContain(`width="${result.width * 2}"`); + expect(result.svg).toContain(`height="${result.height * 2}"`); + }); + + it("measures and renders in the supplied font", async () => { + const result = await renderToSizedSVG("graph TD\n A --> B", { + fontFamily: '"Document Font", sans-serif', + }); + if (result.error !== undefined) { + throw new Error(result.error); + } + expect(result.svg).toContain("Document Font"); + }); + + it("returns invalid source as a typed expected error", async () => { + const result = await renderToSizedSVG("not a diagram"); + expect(result).toEqual({ error: expect.any(String) }); + }); + + it("preserves CSS-looking label text when resolving export colors", async () => { + const result = await renderToSizedSVG('graph TD\n A["var(--_fg)"]'); + if (result.error !== undefined) { + throw new Error(result.error); + } + expect(result.svg).toContain(">var(--_fg)"); + expect(result.svg).toContain('fill="#1f1f1f"'); + }); + + it("uses distinct accessibility IDs for separate diagrams", async () => { + const source = "graph TD\n accTitle: Example\n A --> B"; + const first = await renderToSizedSVG(source); + const second = await renderToSizedSVG(source); + if (first.error !== undefined || second.error !== undefined) { + throw new Error("Expected valid diagrams"); + } + expect(first.svg.match(/aria-labelledby="([^"]+)"/)?.[1]).toBeTruthy(); + expect(first.svg.match(/aria-labelledby="([^"]+)"/)?.[1]).not.toBe( + second.svg.match(/aria-labelledby="([^"]+)"/)?.[1], + ); + }); +}); diff --git a/packages/diagram-block/src/helpers/renderToSizedSVG.ts b/packages/diagram-block/src/helpers/renderToSizedSVG.ts index bacd696227..f43f5fca6d 100644 --- a/packages/diagram-block/src/helpers/renderToSizedSVG.ts +++ b/packages/diagram-block/src/helpers/renderToSizedSVG.ts @@ -1,30 +1,20 @@ -import mermaid from "mermaid"; - -import { initializeMermaid } from "./initializeMermaid.js"; -import { setSVGFontFamily } from "./svgFontFamily.js"; - -// Each render call needs its own element ID (Mermaid removes any existing -// document element with the given ID when rendering). -let exportElementId = 0; +import { renderDiagram } from "./renderDiagram.js"; +import { resolveSVGStyles } from "./resolveSVGStyles.js"; /** - * The shared Mermaid step of the export renderers ({@link + * The shared Pele step of the export renderers ({@link * renderDiagramToImage}, {@link renderDiagramToSVG}): validates the source, * renders it, and gives the SVG explicit pixel dimensions. Invalid sources * are expected (they're user input), so they're returned as a typed error - * with a message safe to show to readers - rather than thrown. * - * Mermaid sizes the SVG relatively (`width: 100%`, plus an inline - * `max-width` style for responsive display in the editor), while export - * consumers - canvas rasterization and SVG renderers alike - need explicit - * intrinsic dimensions. Those are set from the view box (the only reliable - * source for the diagram's pixel size), and the style is stripped since CSS - * wins over presentation attributes when computing the intrinsic size. + * Export consumers need explicit intrinsic dimensions. Pele's fixed-size + * output already has them, without a browser DOM or responsive sizing styles. * * `scale` multiplies only the dimension *attributes* (the returned * `width`/`height` stay the natural size) - rasterizing consumers use it to - * rasterize the vector at full canvas resolution. `fontFamily` replaces the - * font in the rendered output - see {@link renderDiagramToSVG}. + * rasterize the vector at full canvas resolution. `fontFamily` is used for + * measurement as well as rendering - see {@link renderDiagramToSVG}. */ export async function renderToSizedSVG( source: string, @@ -39,38 +29,20 @@ export async function renderToSizedSVG( | { error?: undefined; svg: string; width: number; height: number } | { error: string } > { - initializeMermaid(); - - try { - await mermaid.parse(source); - } catch (error) { - // The boundary that converts Mermaid's parse throw into the typed - // result. - return { error: error instanceof Error ? error.message : String(error) }; - } - - const { svg } = await mermaid.render( - `diagram-export-${exportElementId++}`, - source, - ); - - const svgElement = new DOMParser().parseFromString( - svg, - "image/svg+xml", - ).documentElement; - const viewBox = svgElement.getAttribute("viewBox")?.split(/\s+/).map(Number); - const width = Math.ceil(viewBox?.[2] || 800); - const height = Math.ceil(viewBox?.[3] || 600); - svgElement.setAttribute("width", String(width * scale)); - svgElement.setAttribute("height", String(height * scale)); - svgElement.removeAttribute("style"); - - if (fontFamily) { - setSVGFontFamily(svgElement, fontFamily); + const result = await renderDiagram(source, { fontFamily, responsive: false }); + if (result.error !== undefined) { + return result; } + const width = Math.ceil(result.width); + const height = Math.ceil(result.height); return { - svg: new XMLSerializer().serializeToString(svgElement), + // Only rewrite the root's dimension attributes, never node/label markup. + svg: resolveSVGStyles(result.svg).replace(/^]*>/, (root) => + root + .replace(/\bwidth="[^"]*"/, `width="${width * scale}"`) + .replace(/\bheight="[^"]*"/, `height="${height * scale}"`), + ), width, height, }; diff --git a/packages/diagram-block/src/helpers/resolveSVGStyles.ts b/packages/diagram-block/src/helpers/resolveSVGStyles.ts new file mode 100644 index 0000000000..b97cecfc0e --- /dev/null +++ b/packages/diagram-block/src/helpers/resolveSVGStyles.ts @@ -0,0 +1,38 @@ +/** + * Resolve Pele's self-contained theme defaults for SVG consumers without CSS + * custom properties (notably Typst's SVG renderer). Keep preview SVG themeable. + * Only presentation attributes are changed, never labels or accessibility text. + */ +export function resolveSVGStyles(svg: string): string { + const rootStyle = svg.match(/^]*\bstyle="([^"]*)"/)?.[1] ?? ""; + const variables = new Map( + [...rootStyle.matchAll(/(?:^|;)(--[\w-]+):([^;]*)/g)].map((match) => [ + match[1], + match[2], + ]), + ); + + function resolve(value: string, resolving = new Set()): string { + return value.replace( + // Pele emits at most one level of nested fallback functions. + /var\((--[\w-]+)(?:,((?:[^()]|\([^()]*\))*))?\)/g, + (original: string, name: string, fallback: string | undefined) => { + const replacement = resolving.has(name) + ? fallback + : (variables.get(name) ?? fallback); + if (replacement === undefined) { + return original; + } + return resolve(replacement, new Set([...resolving, name])); + }, + ); + } + + return svg.replace(/<[^>]+>/g, (tag) => + tag.replace( + /\s(style|fill|stroke|color|font-family|font-weight|stop-color|flood-color)="([^"]*)"/g, + (_attribute: string, name: string, value: string) => + ` ${name}="${resolve(value)}"`, + ), + ); +} diff --git a/packages/diagram-block/src/helpers/svgFontFamily.browser.test.ts b/packages/diagram-block/src/helpers/svgFontFamily.browser.test.ts deleted file mode 100644 index f3f4d255d4..0000000000 --- a/packages/diagram-block/src/helpers/svgFontFamily.browser.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { describe, expect, test } from "vite-plus/test"; - -import { withSVGFontFamily } from "./svgFontFamily.js"; - -// Browser unit tests (DOMParser/XMLSerializer) for the SVG font rewrite the -// preview and the export renderers share. Runs in the tests package's -// browser suite. -describe("withSVGFontFamily", () => { - test("rewrites style-block declarations and font-family attributes", () => { - const svg = `Hi`; - - const out = withSVGFontFamily(svg, '"Inter", sans-serif'); - - expect(out).not.toContain("trebuchet"); - expect(out).not.toContain("verdana"); - // Both declarations and the attribute point at the new family; unrelated - // declarations are untouched. (The serializer escapes quotes inside the - // attribute value.) - expect(out.match(/font-family:"Inter", sans-serif/g)).toHaveLength(2); - expect(out).toContain(`font-family=""Inter", sans-serif"`); - expect(out).toContain("font-size:16px"); - expect(out).toContain(">Hi"); - }); - - test("leaves an SVG without font declarations unchanged", () => { - const svg = ``; - - expect(withSVGFontFamily(svg, "Inter")).toContain("` block (and some diagram types set font-family - * attributes directly); rewriting the rendered output is the way to restyle - * it - Mermaid's config is page-global and `mermaid.render` takes no - * options. Label metrics were measured with Mermaid's font, which the swap - * tolerates: labels are anchor-centered in padded boxes. - */ -export function setSVGFontFamily( - svgElement: Element, - fontFamily: string, -): void { - for (const styleElement of svgElement.querySelectorAll("style")) { - styleElement.textContent = - styleElement.textContent?.replace( - /font-family:[^;}]*/g, - // A replacer function, because as a replacement *string* `$`-patterns - // are special to String.replace (`$&` splices in the matched text) - - // and `fontFamily` is external input (theme / exporter config) that - // may legally contain them. A function's return value is inserted - // verbatim. - () => `font-family:${fontFamily}`, - ) ?? null; - } - for (const element of svgElement.querySelectorAll("[font-family]")) { - element.setAttribute("font-family", fontFamily); - } -} - -/** String-in/string-out {@link setSVGFontFamily}. */ -export function withSVGFontFamily(svg: string, fontFamily: string): string { - const svgElement = new DOMParser().parseFromString( - svg, - "image/svg+xml", - ).documentElement; - setSVGFontFamily(svgElement, fontFamily); - return new XMLSerializer().serializeToString(svgElement); -} diff --git a/packages/diagram-block/src/helpers/trimDiagramSVG.ts b/packages/diagram-block/src/helpers/trimDiagramSVG.ts deleted file mode 100644 index 7695782f1a..0000000000 --- a/packages/diagram-block/src/helpers/trimDiagramSVG.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Trims excess vertical whitespace from a rendered Mermaid SVG. Some diagram - * types (e.g. `journey`) reserve far more vertical space than their content - * actually uses, leaving a large empty gap below the diagram. This measures - * the real content bounds and shrinks the SVG's `viewBox` to fit. - * - * Only the bottom is trimmed - the horizontal extent and top edge are left - * untouched, so nothing (arrowhead markers, titles) can get clipped. Browser- - * only, since measuring requires the SVG to be laid out in the document. - */ -export const trimDiagramSVG = (svg: string): string => { - // Measured offscreen - the SVG must be in the document for `getBBox`, but it - // shouldn't flash on screen while measuring. - const container = document.createElement("div"); - container.style.position = "absolute"; - container.style.visibility = "hidden"; - container.style.pointerEvents = "none"; - container.innerHTML = svg; - - const svgElement = container.querySelector("svg"); - const viewBox = svgElement?.getAttribute("viewBox")?.split(/\s+/).map(Number); - if (!svgElement || viewBox?.length !== 4 || viewBox.some(isNaN)) { - return svg; - } - - document.body.appendChild(container); - try { - const [vbX, vbY, vbWidth, vbHeight] = viewBox; - const bbox = svgElement.getBBox(); - const contentBottom = bbox.y + bbox.height; - // A little padding so the content doesn't sit flush against the edge. - const trimmedHeight = contentBottom - vbY + 8; - - let changed = false; - if (trimmedHeight < vbHeight) { - svgElement.setAttribute( - "viewBox", - `${vbX} ${vbY} ${vbWidth} ${trimmedHeight}`, - ); - changed = true; - } - - // Mermaid renders most diagrams responsively (width `100%`, height derived - // from the view box's aspect ratio), but pins an explicit pixel height on - // `journey` diagrams. That fixed height makes the SVG letterbox - it stays - // that many pixels tall even when scaled down to fit a narrower container, - // leaving empty space below. Dropping it lets the height follow the view - // box, so the box always hugs the content. - if (svgElement.hasAttribute("height")) { - svgElement.removeAttribute("height"); - changed = true; - } - - return changed ? svgElement.outerHTML : svg; - } finally { - document.body.removeChild(container); - } -}; diff --git a/packages/diagram-block/src/odt-exporter/index.ts b/packages/diagram-block/src/odt-exporter/index.ts index 8be789895e..caecfa873f 100644 --- a/packages/diagram-block/src/odt-exporter/index.ts +++ b/packages/diagram-block/src/odt-exporter/index.ts @@ -30,15 +30,6 @@ type DiagramBlock = BlockFromConfigNoChildren< // (and untranslated English) - the editor is where the author sees and // fixes it. Styled muted like the // other exporters' placeholders. -function errorMessage( - exporter: ODTExporter, - source: string, -): string { - return getDiagramExporterDictionary(exporter).invalid_diagram( - source.split("\n")[0], - ); -} - function errorParagraph(source: string, exporter: ODTExporter) { const styleName = exporter.registerStyle((name) => createElement( @@ -57,17 +48,18 @@ function errorParagraph(source: string, exporter: ODTExporter) { createElement( "text:span", { "text:style-name": styleName }, - errorMessage(exporter, source), + getDiagramExporterDictionary(exporter).invalid_diagram( + source.split("\n")[0], + ), ), ); } /** * Creates an ODT block mapping for `@blocknote/diagram-block` that embeds - * diagrams as images. Rendering runs in the browser by default (Mermaid - * can't render outside of it); when exporting elsewhere (e.g. server-side), - * pass a `renderDiagram` function backed by e.g. `@mermaid-js/mermaid-cli` - * or a Kroki server. Invalid sources render an error placeholder (mirroring + * diagrams as PNG images using Pele and browser canvas. When exporting + * server-side, pass a `renderDiagram` function with a server-side rasterizer. + * Invalid sources render an error placeholder (mirroring * the editor): * * ```ts @@ -103,7 +95,7 @@ export function createDiagramBlockMapping(options?: { (typeof document !== "undefined" ? renderDiagramToImage : undefined); if (!renderDiagram) { throw new Error( - "Rendering diagrams to images requires a browser. When exporting elsewhere, pass a `renderDiagram` function to `createDiagramBlockMapping` (e.g. backed by @mermaid-js/mermaid-cli or a Kroki server).", + "Default PNG rasterization requires a browser. When exporting elsewhere, pass a `renderDiagram` function with a server-side rasterizer to `createDiagramBlockMapping`.", ); } diff --git a/packages/diagram-block/src/typst-exporter/index.ts b/packages/diagram-block/src/typst-exporter/index.ts index 91ce0828c8..1a17e8574a 100644 --- a/packages/diagram-block/src/typst-exporter/index.ts +++ b/packages/diagram-block/src/typst-exporter/index.ts @@ -43,10 +43,8 @@ function errorText( * diagrams as vector SVG images (crisp at any zoom), in a tagged Figure * carrying the diagram source as its alt text (PDF/UA requires figures to * have one). Labels render in the exporter's own `fontFamily`, matching the - * document. Rendering runs in the browser by default (Mermaid can't render - * outside of it); when exporting elsewhere (e.g. server-side), pass a - * `renderDiagram` function backed by e.g. `@mermaid-js/mermaid-cli` or a - * Kroki server (raster output like PNG works too). Invalid sources render + * document. Pele renders SVG in browsers and on the server. A custom + * `renderDiagram` function can override it (PNG works too). Invalid sources render * an error placeholder (mirroring the editor): * * ```ts @@ -83,14 +81,7 @@ export function createDiagramBlockMapping(options?: { .join(", ")}, sans-serif`; const renderDiagram = options?.renderDiagram ?? - (typeof document !== "undefined" - ? (s: string) => renderDiagramToSVG(s, { fontFamily }) - : undefined); - if (!renderDiagram) { - throw new Error( - "Rendering diagrams to images requires a browser. When exporting elsewhere, pass a `renderDiagram` function to `createDiagramBlockMapping` (e.g. backed by @mermaid-js/mermaid-cli or a Kroki server).", - ); - } + ((s: string) => renderDiagramToSVG(s, { fontFamily })); const result = await renderDiagram(source); if (result.error !== undefined) { @@ -117,7 +108,7 @@ export function createDiagramBlockMapping(options?: { /** * Typst block mapping for `@blocknote/diagram-block` with the default - * options - see {@link createDiagramBlockMapping}. Browser-only; when - * exporting elsewhere, use the factory to pass a `renderDiagram` function. + * options - see {@link createDiagramBlockMapping}. Works in browsers and + * on the server; use the factory to override the renderer. */ export const diagramBlockMapping = createDiagramBlockMapping(); diff --git a/packages/diagram-block/src/typst-exporter/typstExporter.test.ts b/packages/diagram-block/src/typst-exporter/typstExporter.test.ts index a7bf2c6d89..1d6f644785 100644 --- a/packages/diagram-block/src/typst-exporter/typstExporter.test.ts +++ b/packages/diagram-block/src/typst-exporter/typstExporter.test.ts @@ -71,7 +71,7 @@ describe("typst exporter mappings", () => { "should embed vector SVG renders and stay PDF/UA-1 conformant", { timeout: 20000 }, async () => { - // The browser default renderer produces SVG (`renderDiagramToSVG`); + // The default renderer produces SVG (`renderDiagramToSVG`); // this covers that path end-to-end with the SVG stub. const exporter = createExporter({ renderDiagram: renderDiagramSVG }); @@ -112,11 +112,37 @@ describe("typst exporter mappings", () => { ).toHaveLength(0); }); - it("should require a renderer outside the browser", async () => { - const exporter = createExporter(); + it.each([ + "graph TD\n A[Start] --> B[End]", + "sequenceDiagram\n Alice->>Bob: Hello", + "classDiagram\n Animal <|-- Duck", + "stateDiagram-v2\n [*] --> Active", + 'pie\n "One" : 1\n "Two" : 2', + "journey\n title My day\n section Work\n Task: 5: Me", + ])( + "compiles real server-rendered diagrams to PDF/UA-1: %s", + async (source) => { + const exporter = createExporter(); + + const typst = await exporter.toTypst( + [ + { + ...diagramDocument[0], + content: [{ type: "text", text: source, styles: {} }], + }, + ], + { title: "Diagram" }, + ); + expect(typst).toContain('image("/assets/asset-0"'); + const svg = exporter.assetFiles.get("/assets/asset-0"); + expect(svg).toBeDefined(); + expect(new TextDecoder().decode(svg)).toContain("pele"); - await expect(exporter.toTypst(diagramDocument)).rejects.toThrow( - "pass a `renderDiagram` function", - ); - }); + const pdf = await compileTypstForTesting(typst, { + assets: exporter.assetFiles, + pdfStandard: "ua-1", + }); + expect(pdf.length).toBeGreaterThan(0); + }, + ); }); diff --git a/packages/react/src/editor/styles.css b/packages/react/src/editor/styles.css index 8ce0056370..d1fbb0ebdd 100644 --- a/packages/react/src/editor/styles.css +++ b/packages/react/src/editor/styles.css @@ -95,6 +95,32 @@ font-family: var(--bn-font-family); } +/* Optional diagram blocks inherit the same theme as the editor and its + portalled previews. CSS-only colors update without re-rendering the SVG. + Low specificity lets applications override any --pele-* variable. Fonts + are passed to Pele at render time so label measurement matches the editor. */ +:where(.bn-root) { + --pele-bg: var(--bn-colors-editor-background); + --pele-fg: var(--bn-colors-editor-text); + --pele-muted: var(--bn-colors-side-menu); + --pele-line: var(--bn-colors-editor-text); + --pele-surface: var(--bn-colors-hovered-background); + --pele-surface-alt: var(--bn-colors-menu-background); + --pele-border: var(--bn-colors-border); + --pele-accent: var(--bn-colors-highlights-blue-text); + --pele-critical: var(--bn-colors-highlights-red-text); + --pele-error: var(--bn-colors-highlights-red-text); + --pele-radius: var(--bn-border-radius); + --pele-series-1: var(--bn-colors-highlights-blue-text); + --pele-series-2: var(--bn-colors-highlights-orange-text); + --pele-series-3: var(--bn-colors-highlights-green-text); + --pele-series-4: var(--bn-colors-highlights-red-text); + --pele-series-5: var(--bn-colors-highlights-purple-text); + --pele-series-6: var(--bn-colors-highlights-pink-text); + --pele-series-7: var(--bn-colors-highlights-yellow-text); + --pele-series-8: var(--bn-colors-highlights-brown-text); +} + /* Editor body styling */ /* Editor styling */ diff --git a/packages/shadcn/src/style.css b/packages/shadcn/src/style.css index fd4477a024..8750524cd8 100644 --- a/packages/shadcn/src/style.css +++ b/packages/shadcn/src/style.css @@ -1,5 +1,27 @@ @import "@blocknote/react/style.css"; +/* Use Shadcn's semantic colors (including .dark changes) when provided by + the host. Missing tokens fall back to the shared BlockNote theme. Keep + specificity low so direct --pele-* customizations still take precedence. */ +:where(.bn-root.bn-shadcn) { + --pele-bg: var(--background, var(--bn-colors-editor-background)); + --pele-fg: var(--foreground, var(--bn-colors-editor-text)); + --pele-muted: var(--muted-foreground, var(--bn-colors-side-menu)); + --pele-line: var(--foreground, var(--bn-colors-editor-text)); + --pele-surface: var(--muted, var(--bn-colors-hovered-background)); + --pele-surface-alt: var(--card, var(--bn-colors-menu-background)); + --pele-border: var(--border, var(--bn-colors-border)); + --pele-accent: var(--primary, var(--bn-colors-highlights-blue-text)); + --pele-critical: var(--destructive, var(--bn-colors-highlights-red-text)); + --pele-error: var(--destructive, var(--bn-colors-highlights-red-text)); + --pele-radius: var(--radius, var(--bn-border-radius)); + --pele-series-1: var(--chart-1, var(--bn-colors-highlights-blue-text)); + --pele-series-2: var(--chart-2, var(--bn-colors-highlights-orange-text)); + --pele-series-3: var(--chart-3, var(--bn-colors-highlights-green-text)); + --pele-series-4: var(--chart-4, var(--bn-colors-highlights-red-text)); + --pele-series-5: var(--chart-5, var(--bn-colors-highlights-purple-text)); +} + .bn-shadcn svg:not([class*="size-"]) { width: revert; height: revert; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6c6ffd4454..ac408accd7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -313,9 +313,6 @@ importers: mathjax-full: specifier: ^3.2.2 version: 3.2.2 - mermaid: - specifier: ^11.0.0 - version: 11.16.0 motion: specifier: ^12.28.1 version: 12.38.0(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -5574,9 +5571,9 @@ importers: '@blocknote/react': specifier: workspace:^ version: link:../react - mermaid: - specifier: ^11.0.0 - version: 11.16.0 + pele: + specifier: ^1.5.2 + version: 1.5.2 devDependencies: '@blocknote/shared': specifier: workspace:^ @@ -6884,9 +6881,6 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@antfu/install-pkg@1.1.0': - resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} - '@ariakit/core@0.4.18': resolution: {integrity: sha512-9urEa+GbZTSyredq3B/3thQjTcSZSUC68XctwCkJNH/xNfKN5O+VThiem2rcJxpsGw8sRUQenhagZi0yB4foyg==} @@ -7262,9 +7256,6 @@ packages: '@blazediff/core@1.9.1': resolution: {integrity: sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==} - '@braintree/sanitize-url@7.1.2': - resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==} - '@bramus/specificity@2.4.2': resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} hasBin: true @@ -7274,9 +7265,6 @@ packages: peerDependencies: html-entities: ^2.3.2 - '@chevrotain/types@11.1.2': - resolution: {integrity: sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==} - '@csstools/color-helpers@6.1.1': resolution: {integrity: sha512-gLNsunvwf3mCi5u5o46/Z/JcJMnhbHSaZ69rkgPzNM3J4s8hWwpPUQB6/tt0EDFyCiWzxANlx+2LJwpYj4zS1w==} engines: {node: '>=20.19.0'} @@ -7899,12 +7887,6 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@iconify/types@2.0.0': - resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} - - '@iconify/utils@3.1.4': - resolution: {integrity: sha512-b1S7B1k9ohZ+iNTi2ATxbRYG9fTrJmUT0rc46bvVnNxqNRGW7dyo/vRREwyniI5IRN2RSJHDcm+s3BjWrSAjHw==} - '@img/colour@1.1.0': resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} engines: {node: '>=18'} @@ -8225,9 +8207,6 @@ packages: '@mdx-js/mdx@3.1.1': resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} - '@mermaid-js/parser@1.2.0': - resolution: {integrity: sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==} - '@mswjs/interceptors@0.37.6': resolution: {integrity: sha512-wK+5pLK5XFmgtH3aQ2YVvA3HohS3xqV/OxuVOdNx9Wpnz7VE/fnC+e1A7ln6LFYeck7gOJ/dsZV6OLplOtAJ2w==} engines: {node: '>=18'} @@ -11109,96 +11088,30 @@ packages: '@types/d3-array@3.2.2': resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} - '@types/d3-axis@3.0.6': - resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} - - '@types/d3-brush@3.0.6': - resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} - - '@types/d3-chord@3.0.6': - resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} - '@types/d3-color@3.1.3': resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} - '@types/d3-contour@3.0.6': - resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} - - '@types/d3-delaunay@6.0.4': - resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} - - '@types/d3-dispatch@3.0.7': - resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==} - - '@types/d3-drag@3.0.7': - resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} - - '@types/d3-dsv@3.0.7': - resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} - '@types/d3-ease@3.0.2': resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} - '@types/d3-fetch@3.0.7': - resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} - - '@types/d3-force@3.0.10': - resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} - - '@types/d3-format@3.0.4': - resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} - - '@types/d3-geo@3.1.0': - resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} - - '@types/d3-hierarchy@3.1.7': - resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} - '@types/d3-interpolate@3.0.4': resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} '@types/d3-path@3.1.1': resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} - '@types/d3-polygon@3.0.2': - resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} - - '@types/d3-quadtree@3.0.6': - resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} - - '@types/d3-random@3.0.4': - resolution: {integrity: sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA==} - - '@types/d3-scale-chromatic@3.1.0': - resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} - '@types/d3-scale@4.0.9': resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} - '@types/d3-selection@3.0.11': - resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} - '@types/d3-shape@3.1.8': resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} - '@types/d3-time-format@4.0.3': - resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} - '@types/d3-time@3.0.4': resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} '@types/d3-timer@3.0.2': resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} - '@types/d3-transition@3.0.9': - resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} - - '@types/d3-zoom@3.0.8': - resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} - - '@types/d3@7.4.3': - resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} - '@types/debug@4.1.13': resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} @@ -11223,9 +11136,6 @@ packages: '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} - '@types/geojson@7946.0.16': - resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} - '@types/hast@3.0.5': resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} @@ -11317,9 +11227,6 @@ packages: '@types/tough-cookie@4.0.5': resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} - '@types/trusted-types@2.0.7': - resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} - '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -11560,9 +11467,6 @@ packages: peerDependencies: '@uppy/core': ^3.13.0 - '@upsetjs/venn.js@2.0.0': - resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==} - '@vercel/analytics@1.6.1': resolution: {integrity: sha512-oH9He/bEM+6oKlv3chWuOOcp8Y6fo6/PSro8hEkgCW3pu9/OiCXiUpRUogDh3Fs3LH2sosDrx8CxeOLBEE+afg==} peerDependencies: @@ -11884,157 +11788,133 @@ packages: resolution: {integrity: sha512-jrOY5WM+AaAqkv51fHP1x28ifto4WgcZVzodLUyMU1jMWn5Sq+VciRdk/n8E0Ey5w4p4cWWE+m5GfXWOYh7Kzw==} cpu: [arm64] os: [android] - deprecated: yuku-codegen is pure JavaScript since 0.14 '@yuku-codegen/binding-darwin-arm64@0.9.5': resolution: {integrity: sha512-4O4lkCQIzPZGjiNB1GORSI6hBECbiiSBS/APZXPvNKYH+nhY4uuqv03LNXA+SET3hoBjvr95P5rIhY8KQaQUBA==} cpu: [arm64] os: [darwin] - deprecated: yuku-codegen is pure JavaScript since 0.14 '@yuku-codegen/binding-darwin-x64@0.9.5': resolution: {integrity: sha512-9EvoUO0SEhD6/d8VHdWuPerepPMSR1y84+UEgz8Un1Ope14Oe7xMvePpEQLTLovoIFZ8Zg3iZ8z8E11pZMqC3g==} cpu: [x64] os: [darwin] - deprecated: yuku-codegen is pure JavaScript since 0.14 '@yuku-codegen/binding-freebsd-x64@0.9.5': resolution: {integrity: sha512-HqT78WwgHTmp8lwujoUa9CrIortX4DdpuiVC18ZPSGvuuJf4ylpIEI6QrQEM78Zwz3muhAWAOXZ5irdiYY+AyA==} cpu: [x64] os: [freebsd] - deprecated: yuku-codegen is pure JavaScript since 0.14 '@yuku-codegen/binding-linux-arm-gnu@0.9.5': resolution: {integrity: sha512-QJXwIW6Ms3QIawbcBIedmrmXnfdpuAOjZJ/eAABq5XTzWvSxZ7lutu9W5yIHahlaTaFnBo7ikrVMD1szLTpPUw==} cpu: [arm] os: [linux] libc: [glibc] - deprecated: yuku-codegen is pure JavaScript since 0.14 '@yuku-codegen/binding-linux-arm-musl@0.9.5': resolution: {integrity: sha512-eHbFy3IHGb+IYarrYwAo0yWSEQV3eAmEn6VrsKA6I1Wy1YPJxWqSJlV69JhqsHtJuJlljUIF3ex0y46yaKIu9w==} cpu: [arm] os: [linux] libc: [musl] - deprecated: yuku-codegen is pure JavaScript since 0.14 '@yuku-codegen/binding-linux-arm64-gnu@0.9.5': resolution: {integrity: sha512-ByoJMbySTaDhjAXSu8q6Lh7HKg3YoesXpcT72aYk0Aiw4PCznmY4ybpLTq0RCvp0RIPhFm/6yECFiGBwyCC1nw==} cpu: [arm64] os: [linux] libc: [glibc] - deprecated: yuku-codegen is pure JavaScript since 0.14 '@yuku-codegen/binding-linux-arm64-musl@0.9.5': resolution: {integrity: sha512-gD9vfXIoBw1toSxhO9rgmSu/FEfy3PMznJAxVjIuH8DrWEiDKXmJO0pJKfj1Ltbe/TmtWVZR+TjISqeSIGQzMg==} cpu: [arm64] os: [linux] libc: [musl] - deprecated: yuku-codegen is pure JavaScript since 0.14 '@yuku-codegen/binding-linux-x64-gnu@0.9.5': resolution: {integrity: sha512-BARvdnvqMGjOr5Iel2JH+9H5vAIE0R6H0Z2fsT02xrvmI/1ZXNB8lkuX+ZGevPhZb3zJ9WeC/R8JZstrsUOK5g==} cpu: [x64] os: [linux] libc: [glibc] - deprecated: yuku-codegen is pure JavaScript since 0.14 '@yuku-codegen/binding-linux-x64-musl@0.9.5': resolution: {integrity: sha512-x8flcevS1fbb7ESrmpOY/pON4eInSaE/7Ktjqx2udOE2W33BNSZmJuwpyUgo54AoHNqrcaM7szqydyJn5fNvew==} cpu: [x64] os: [linux] libc: [musl] - deprecated: yuku-codegen is pure JavaScript since 0.14 '@yuku-codegen/binding-win32-arm64@0.9.5': resolution: {integrity: sha512-KOL/rBatWqH4ZpCNoF8ZtSNdOJbAxBJLmk/VeRciepGROPTbPum1A9t67GpFgOU7qrkUWlPJdJ+KHKbvbmOt+w==} cpu: [arm64] os: [win32] - deprecated: yuku-codegen is pure JavaScript since 0.14 '@yuku-codegen/binding-win32-x64@0.9.5': resolution: {integrity: sha512-FxENahEjWSan59Syh/us/Kf4wNq8qrJLFJ3R2N4Oiwtb6yNdz/rWLNTIoNSssnsp7IoWJdUP6o/Z8ppg7lXMcg==} cpu: [x64] os: [win32] - deprecated: yuku-codegen is pure JavaScript since 0.14 '@yuku-parser/binding-android-arm64@0.9.5': resolution: {integrity: sha512-A2JCFCSHfnficqYEw4Iujpx7XrkMM3UfFcgJFmYpZSo9zM4nyhmdIIE0FogSduuU60lhM0/UcfuUBXIVNBMlGQ==} cpu: [arm64] os: [android] - deprecated: yuku-parser runs on yuku-core since 0.14 '@yuku-parser/binding-darwin-arm64@0.9.5': resolution: {integrity: sha512-3PiyU+Eare4YuKaQ22N98/yAiROPY5o/NQJHraICzDvk4pgS+m+bgLKOvkGBRn33OnV95Vdv1Mn38b+MQHpULQ==} cpu: [arm64] os: [darwin] - deprecated: yuku-parser runs on yuku-core since 0.14 '@yuku-parser/binding-darwin-x64@0.9.5': resolution: {integrity: sha512-blFMAFI7AInI83XaiOF8cIeiRM46Nz9EfpZtZPRLbKxSA9aAr5v8aHYpmfN6NoyXxAv/10pwS4gsAuc1W6fy5Q==} cpu: [x64] os: [darwin] - deprecated: yuku-parser runs on yuku-core since 0.14 '@yuku-parser/binding-freebsd-x64@0.9.5': resolution: {integrity: sha512-TsNuL4qsZdO0tHp5GW43Y5fHeLPpPHA675wdcPNTcdq2ZO5AvsQWFFoiDg8CH4oBktfsQ9tdvKhjLnLYwKvp4g==} cpu: [x64] os: [freebsd] - deprecated: yuku-parser runs on yuku-core since 0.14 '@yuku-parser/binding-linux-arm-gnu@0.9.5': resolution: {integrity: sha512-b0afYK5gHeV8RdmOcqAlgM8ONsye4cax4DMnIBaoJyPMd1UTGvTbpkqQcPVdhmHlcfcGVGMV1laeVovlr/dscw==} cpu: [arm] os: [linux] libc: [glibc] - deprecated: yuku-parser runs on yuku-core since 0.14 '@yuku-parser/binding-linux-arm-musl@0.9.5': resolution: {integrity: sha512-fD3lKzl+r6j6n8DwiMY53qnh5DLqD8KJjCp+NbVud54Nnh3Q9Wprqss3YzyMHP3NSJk663Wlg1E1X5qOuFVFig==} cpu: [arm] os: [linux] libc: [musl] - deprecated: yuku-parser runs on yuku-core since 0.14 '@yuku-parser/binding-linux-arm64-gnu@0.9.5': resolution: {integrity: sha512-aRU/aCphV1MWCl/lvI6NX8LgucHQ68Fx+vz7NBb/5MEr2EuzCxiPOQqeFcMdWh+2AED/p0AvAREch0c+cSnlhA==} cpu: [arm64] os: [linux] libc: [glibc] - deprecated: yuku-parser runs on yuku-core since 0.14 '@yuku-parser/binding-linux-arm64-musl@0.9.5': resolution: {integrity: sha512-5+Guro0l8H473YXlEjVNBRLN/IbPbJdnQh1zo0OLt49xxnON+XMJRRaEyTOTfkn9QSRg0+BhEKGR4W9Gu2uZJQ==} cpu: [arm64] os: [linux] libc: [musl] - deprecated: yuku-parser runs on yuku-core since 0.14 '@yuku-parser/binding-linux-x64-gnu@0.9.5': resolution: {integrity: sha512-pwwSyV9q+GlvzSXlsMZBMlgk22L5bud714/vqZCdeUTivzeUVltQzUaf3IQXOGXdpc59JYTeuMCtbGquaAUoCA==} cpu: [x64] os: [linux] libc: [glibc] - deprecated: yuku-parser runs on yuku-core since 0.14 '@yuku-parser/binding-linux-x64-musl@0.9.5': resolution: {integrity: sha512-z18j6JN3lBHH8vzN7gG1M8fI0nlgItSfnC9PfbmUbt97iHViKfw2iA2G9fGwRMe5LyPRZBrejIlapbygPbpdaw==} cpu: [x64] os: [linux] libc: [musl] - deprecated: yuku-parser runs on yuku-core since 0.14 '@yuku-parser/binding-win32-arm64@0.9.5': resolution: {integrity: sha512-s6Gwttb1dQvtPX6Bgkw+UPC9IO3UBDXc6Zezog8MMgvu3UfJBBB9TQqKBX/2quu0Eyh+lLSAyNlIyyecaagUPg==} cpu: [arm64] os: [win32] - deprecated: yuku-parser runs on yuku-core since 0.14 '@yuku-parser/binding-win32-x64@0.9.5': resolution: {integrity: sha512-FrERt9YWatY3bJfSUdi4YWY+6iQcyo3MzCC821BxlWM5TFZEHijnpz/bknR79VHlXY/9CvXz8ZlaAGPsTtN3nw==} cpu: [x64] os: [win32] - deprecated: yuku-parser runs on yuku-core since 0.14 '@yuku-toolchain/types@0.9.5': resolution: {integrity: sha512-KiuLNNgX9uNealaWAR+G3/cMXnRk9x4TY2EkYe/KIag+UPdwiA0RRf1hr1WAxzTP8KGzCTkqUdLPvqh32sEO3w==} @@ -12617,14 +12497,6 @@ packages: commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - commander@7.2.0: - resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} - engines: {node: '>= 10'} - - commander@8.3.0: - resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} - engines: {node: '>= 12'} - commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} @@ -12686,12 +12558,6 @@ packages: resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} engines: {node: '>= 0.10'} - cose-base@1.0.3: - resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} - - cose-base@2.2.0: - resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==} - cosmiconfig@7.1.0: resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} engines: {node: '>=10'} @@ -12718,129 +12584,34 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - cytoscape-cose-bilkent@4.1.0: - resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} - peerDependencies: - cytoscape: ^3.2.0 - - cytoscape-fcose@2.2.0: - resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==} - peerDependencies: - cytoscape: ^3.2.0 - - cytoscape@3.34.0: - resolution: {integrity: sha512-62rNSrioXw93uliKFBwjukeQyeWwH2PqDrTac31r2P6464u3AUvTk0xS4LVvT251g7IgkFunrI48ZEZGjywSOg==} - engines: {node: '>=0.10'} - - d3-array@2.12.1: - resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} - d3-array@3.2.4: resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} engines: {node: '>=12'} - d3-axis@3.0.0: - resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} - engines: {node: '>=12'} - - d3-brush@3.0.0: - resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} - engines: {node: '>=12'} - - d3-chord@3.0.1: - resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} - engines: {node: '>=12'} - d3-color@3.1.0: resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} engines: {node: '>=12'} - d3-contour@4.0.2: - resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} - engines: {node: '>=12'} - - d3-delaunay@6.0.4: - resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} - engines: {node: '>=12'} - - d3-dispatch@3.0.1: - resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} - engines: {node: '>=12'} - - d3-drag@3.0.0: - resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} - engines: {node: '>=12'} - - d3-dsv@3.0.1: - resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} - engines: {node: '>=12'} - hasBin: true - d3-ease@3.0.1: resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} engines: {node: '>=12'} - d3-fetch@3.0.1: - resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} - engines: {node: '>=12'} - - d3-force@3.0.0: - resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} - engines: {node: '>=12'} - d3-format@3.1.2: resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} engines: {node: '>=12'} - d3-geo@3.1.1: - resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} - engines: {node: '>=12'} - - d3-hierarchy@3.1.2: - resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} - engines: {node: '>=12'} - d3-interpolate@3.0.1: resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} engines: {node: '>=12'} - d3-path@1.0.9: - resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} - d3-path@3.1.0: resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} engines: {node: '>=12'} - d3-polygon@3.0.1: - resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} - engines: {node: '>=12'} - - d3-quadtree@3.0.1: - resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} - engines: {node: '>=12'} - - d3-random@3.0.1: - resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} - engines: {node: '>=12'} - - d3-sankey@0.12.3: - resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==} - - d3-scale-chromatic@3.1.0: - resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} - engines: {node: '>=12'} - d3-scale@4.0.2: resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} engines: {node: '>=12'} - d3-selection@3.0.0: - resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} - engines: {node: '>=12'} - - d3-shape@1.3.7: - resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} - d3-shape@3.2.0: resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} engines: {node: '>=12'} @@ -12857,23 +12628,6 @@ packages: resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} engines: {node: '>=12'} - d3-transition@3.0.1: - resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} - engines: {node: '>=12'} - peerDependencies: - d3-selection: 2 - 3 - - d3-zoom@3.0.0: - resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} - engines: {node: '>=12'} - - d3@7.9.0: - resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} - engines: {node: '>=12'} - - dagre-d3-es@7.0.14: - resolution: {integrity: sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==} - data-urls@7.0.0: resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -12896,9 +12650,6 @@ packages: date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} - dayjs@1.11.21: - resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==} - debounce-fn@6.0.0: resolution: {integrity: sha512-rBMW+F2TXryBwB54Q0d8drNEI+TfoS9JpNTAoVpukbWEhjXQq4rySFYLaqXMFXwdv61Zb2OHtj5bviSoimqxRQ==} engines: {node: '>=18'} @@ -12984,9 +12735,6 @@ packages: defu@6.1.6: resolution: {integrity: sha512-f8mefEW4WIVg4LckePx3mALjQSPQgFlg9U8yaPdlsbdYcHQyj9n2zL2LJEA52smeYxOvmd/nB7TpMtHGMTHcug==} - delaunator@5.1.0: - resolution: {integrity: sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==} - delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -13039,9 +12787,6 @@ packages: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} - dompurify@3.4.12: - resolution: {integrity: sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==} - domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} @@ -13700,9 +13445,6 @@ packages: resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} engines: {node: '>=6.0'} - hachure-fill@0.5.2: - resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} - has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -13802,10 +13544,6 @@ packages: hyphen@1.14.1: resolution: {integrity: sha512-kvL8xYl5QMTh+LwohVN72ciOxC0OEV79IPdJSTwEXok9y9QHebXGdFgrED4sWfiax/ODx++CAMk3hMy4XPJPOw==} - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -13844,9 +13582,6 @@ packages: resolution: {integrity: sha512-OnGy+eYT7wVejH2XWgLRgbmzujhhVIATQH0ztIeRilwHBjTeG3pD+XnH3PKX0r9gJ0BuJmJ68q/oh9qgXnNDQg==} engines: {node: '>=18'} - import-meta-resolve@4.2.0: - resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} - imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -13870,9 +13605,6 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} - internmap@1.0.1: - resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} - internmap@2.0.3: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} @@ -14188,10 +13920,6 @@ packages: jszip@3.10.1: resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} - katex@0.16.47: - resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==} - hasBin: true - katex@0.18.9: resolution: {integrity: sha512-8ad9RyoKsb/g8/yLFE+KAlP+DhbCTRUNi/V9XGsxn0R+trJJltNwzcDNo0q/DEkOy5fQUQTAQyCXCYSE+OakTQ==} hasBin: true @@ -14199,9 +13927,6 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - khroma@2.1.0: - resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} - kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -14214,12 +13939,6 @@ packages: resolution: {integrity: sha512-r2clcf7HLWvDXaVUEvQymXJY4i3bSOIV3xsL/Upy3ZfSv5HeKsk9tsqbBptLvth5qHEIhxeHTA2jNLyQABkLBA==} engines: {node: '>=20.0.0'} - layout-base@1.0.2: - resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} - - layout-base@2.0.1: - resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} - leac@0.6.0: resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} @@ -14443,9 +14162,6 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash-es@4.18.1: - resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} - lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} @@ -14535,11 +14251,6 @@ packages: engines: {node: '>= 18'} hasBin: true - marked@16.4.2: - resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==} - engines: {node: '>= 20'} - hasBin: true - math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -14611,9 +14322,6 @@ packages: merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - mermaid@11.16.0: - resolution: {integrity: sha512-Zvm3kbstgdpvIJPPItlL7fppIZ3kibvc1oZIGxdvk9t6UFz6flv+Jw7FtRGKwfcI8OckmH04LqG6LlS6X4B1pA==} - mhchemparser@4.2.1: resolution: {integrity: sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==} @@ -15178,9 +14886,6 @@ packages: path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - path-data-parser@0.1.0: - resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -15230,6 +14935,10 @@ packages: peberminta@0.9.0: resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} + pele@1.5.2: + resolution: {integrity: sha512-t1XarodlFluWbSy8eXE4PnkFNXUaP1qLUUKJANHiZpTM0VgDoLCEv9CENj3iHM8s507QXyRdSJFMDiZCmkvtGg==} + engines: {node: '>=20'} + perfect-debounce@2.1.0: resolution: {integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==} @@ -15298,12 +15007,6 @@ packages: resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} engines: {node: '>=14.19.0'} - points-on-curve@0.2.0: - resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} - - points-on-path@0.2.1: - resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} - possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -15803,9 +15506,6 @@ packages: resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} hasBin: true - robust-predicates@3.0.3: - resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==} - rolldown@1.0.0-rc.15: resolution: {integrity: sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==} engines: {node: ^20.19.0 || >=22.12.0} @@ -15828,16 +15528,10 @@ packages: rou3@0.7.12: resolution: {integrity: sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg==} - roughjs@4.6.6: - resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} - run-applescript@7.1.0: resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} engines: {node: '>=18'} - rw@1.3.3: - resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} - rxjs@7.8.2: resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} @@ -15859,9 +15553,6 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sax@1.6.0: resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} engines: {node: '>=11.0.0'} @@ -16190,9 +15881,6 @@ packages: stylis@4.2.0: resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} - stylis@4.4.0: - resolution: {integrity: sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==} - supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -16381,10 +16069,6 @@ packages: trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} - ts-dedent@2.3.0: - resolution: {integrity: sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==} - engines: {node: '>=6.10'} - ts-morph@27.0.2: resolution: {integrity: sha512-fhUhgeljcrdZ+9DZND1De1029PrE+cMkIP7ooqkLRTrRLTqcki2AstsyJm0vRNbTbVCNJ0idGlbBrfqc7/nA8w==} @@ -16568,10 +16252,6 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@14.0.1: - resolution: {integrity: sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==} - hasBin: true - uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). @@ -17095,11 +16775,6 @@ snapshots: '@alloc/quick-lru@5.2.0': {} - '@antfu/install-pkg@1.1.0': - dependencies: - package-manager-detector: 1.7.0 - tinyexec: 1.3.1 - '@ariakit/core@0.4.18': {} '@ariakit/react-core@0.4.24(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': @@ -17818,8 +17493,6 @@ snapshots: '@blazediff/core@1.9.1': {} - '@braintree/sanitize-url@7.1.2': {} - '@bramus/specificity@2.4.2': dependencies: css-tree: 3.2.1 @@ -17834,8 +17507,6 @@ snapshots: pako: 2.2.0 tslib: 2.8.1 - '@chevrotain/types@11.1.2': {} - '@csstools/color-helpers@6.1.1': {} '@csstools/css-calc@3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': @@ -18332,14 +18003,6 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@iconify/types@2.0.0': {} - - '@iconify/utils@3.1.4': - dependencies: - '@antfu/install-pkg': 1.1.0 - '@iconify/types': 2.0.0 - import-meta-resolve: 4.2.0 - '@img/colour@1.1.0': optional: true @@ -18675,10 +18338,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@mermaid-js/parser@1.2.0': - dependencies: - '@chevrotain/types': 11.1.2 - '@mswjs/interceptors@0.37.6': dependencies: '@open-draft/deferred-promise': 2.2.0 @@ -21590,121 +21249,28 @@ snapshots: '@types/d3-array@3.2.2': {} - '@types/d3-axis@3.0.6': - dependencies: - '@types/d3-selection': 3.0.11 - - '@types/d3-brush@3.0.6': - dependencies: - '@types/d3-selection': 3.0.11 - - '@types/d3-chord@3.0.6': {} - '@types/d3-color@3.1.3': {} - '@types/d3-contour@3.0.6': - dependencies: - '@types/d3-array': 3.2.2 - '@types/geojson': 7946.0.16 - - '@types/d3-delaunay@6.0.4': {} - - '@types/d3-dispatch@3.0.7': {} - - '@types/d3-drag@3.0.7': - dependencies: - '@types/d3-selection': 3.0.11 - - '@types/d3-dsv@3.0.7': {} - '@types/d3-ease@3.0.2': {} - '@types/d3-fetch@3.0.7': - dependencies: - '@types/d3-dsv': 3.0.7 - - '@types/d3-force@3.0.10': {} - - '@types/d3-format@3.0.4': {} - - '@types/d3-geo@3.1.0': - dependencies: - '@types/geojson': 7946.0.16 - - '@types/d3-hierarchy@3.1.7': {} - '@types/d3-interpolate@3.0.4': dependencies: '@types/d3-color': 3.1.3 '@types/d3-path@3.1.1': {} - '@types/d3-polygon@3.0.2': {} - - '@types/d3-quadtree@3.0.6': {} - - '@types/d3-random@3.0.4': {} - - '@types/d3-scale-chromatic@3.1.0': {} - '@types/d3-scale@4.0.9': dependencies: '@types/d3-time': 3.0.4 - '@types/d3-selection@3.0.11': {} - '@types/d3-shape@3.1.8': dependencies: '@types/d3-path': 3.1.1 - '@types/d3-time-format@4.0.3': {} - '@types/d3-time@3.0.4': {} '@types/d3-timer@3.0.2': {} - '@types/d3-transition@3.0.9': - dependencies: - '@types/d3-selection': 3.0.11 - - '@types/d3-zoom@3.0.8': - dependencies: - '@types/d3-interpolate': 3.0.4 - '@types/d3-selection': 3.0.11 - - '@types/d3@7.4.3': - dependencies: - '@types/d3-array': 3.2.2 - '@types/d3-axis': 3.0.6 - '@types/d3-brush': 3.0.6 - '@types/d3-chord': 3.0.6 - '@types/d3-color': 3.1.3 - '@types/d3-contour': 3.0.6 - '@types/d3-delaunay': 6.0.4 - '@types/d3-dispatch': 3.0.7 - '@types/d3-drag': 3.0.7 - '@types/d3-dsv': 3.0.7 - '@types/d3-ease': 3.0.2 - '@types/d3-fetch': 3.0.7 - '@types/d3-force': 3.0.10 - '@types/d3-format': 3.0.4 - '@types/d3-geo': 3.1.0 - '@types/d3-hierarchy': 3.1.7 - '@types/d3-interpolate': 3.0.4 - '@types/d3-path': 3.1.1 - '@types/d3-polygon': 3.0.2 - '@types/d3-quadtree': 3.0.6 - '@types/d3-random': 3.0.4 - '@types/d3-scale': 4.0.9 - '@types/d3-scale-chromatic': 3.1.0 - '@types/d3-selection': 3.0.11 - '@types/d3-shape': 3.1.8 - '@types/d3-time': 3.0.4 - '@types/d3-time-format': 4.0.3 - '@types/d3-timer': 3.0.2 - '@types/d3-transition': 3.0.9 - '@types/d3-zoom': 3.0.8 - '@types/debug@4.1.13': dependencies: '@types/ms': 2.1.0 @@ -21731,8 +21297,6 @@ snapshots: '@types/estree@1.0.9': {} - '@types/geojson@7946.0.16': {} - '@types/hast@3.0.5': dependencies: '@types/unist': 3.0.3 @@ -21831,9 +21395,6 @@ snapshots: '@types/tough-cookie@4.0.5': {} - '@types/trusted-types@2.0.7': - optional: true - '@types/unist@2.0.11': {} '@types/unist@3.0.3': {} @@ -22043,11 +21604,6 @@ snapshots: '@uppy/core': 3.13.1 '@uppy/utils': 5.9.0 - '@upsetjs/venn.js@2.0.0': - optionalDependencies: - d3-selection: 3.0.0 - d3-transition: 3.0.1(d3-selection@3.0.0) - '@vercel/analytics@1.6.1(next@16.3.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.60.0)(@types/node@25.9.5)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5)': optionalDependencies: next: 16.3.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.60.0)(@types/node@25.9.5)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -22722,7 +22278,7 @@ snapshots: babel-plugin-react-compiler@1.0.0: dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.8 bail@2.0.2: {} @@ -23065,10 +22621,6 @@ snapshots: commander@2.20.3: {} - commander@7.2.0: {} - - commander@8.3.0: {} - commondir@1.0.1: {} compressible@2.0.18: @@ -23136,14 +22688,6 @@ snapshots: object-assign: 4.1.1 vary: 1.1.2 - cose-base@1.0.3: - dependencies: - layout-base: 1.0.2 - - cose-base@2.2.0: - dependencies: - layout-base: 2.0.1 - cosmiconfig@7.1.0: dependencies: '@types/parse-json': 4.0.2 @@ -23171,107 +22715,22 @@ snapshots: csstype@3.2.3: {} - cytoscape-cose-bilkent@4.1.0(cytoscape@3.34.0): - dependencies: - cose-base: 1.0.3 - cytoscape: 3.34.0 - - cytoscape-fcose@2.2.0(cytoscape@3.34.0): - dependencies: - cose-base: 2.2.0 - cytoscape: 3.34.0 - - cytoscape@3.34.0: {} - - d3-array@2.12.1: - dependencies: - internmap: 1.0.1 - d3-array@3.2.4: dependencies: internmap: 2.0.3 - d3-axis@3.0.0: {} - - d3-brush@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-drag: 3.0.0 - d3-interpolate: 3.0.1 - d3-selection: 3.0.0 - d3-transition: 3.0.1(d3-selection@3.0.0) - - d3-chord@3.0.1: - dependencies: - d3-path: 3.1.0 - d3-color@3.1.0: {} - d3-contour@4.0.2: - dependencies: - d3-array: 3.2.4 - - d3-delaunay@6.0.4: - dependencies: - delaunator: 5.1.0 - - d3-dispatch@3.0.1: {} - - d3-drag@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-selection: 3.0.0 - - d3-dsv@3.0.1: - dependencies: - commander: 7.2.0 - iconv-lite: 0.6.3 - rw: 1.3.3 - d3-ease@3.0.1: {} - d3-fetch@3.0.1: - dependencies: - d3-dsv: 3.0.1 - - d3-force@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-quadtree: 3.0.1 - d3-timer: 3.0.1 - d3-format@3.1.2: {} - d3-geo@3.1.1: - dependencies: - d3-array: 3.2.4 - - d3-hierarchy@3.1.2: {} - d3-interpolate@3.0.1: dependencies: d3-color: 3.1.0 - d3-path@1.0.9: {} - d3-path@3.1.0: {} - d3-polygon@3.0.1: {} - - d3-quadtree@3.0.1: {} - - d3-random@3.0.1: {} - - d3-sankey@0.12.3: - dependencies: - d3-array: 2.12.1 - d3-shape: 1.3.7 - - d3-scale-chromatic@3.1.0: - dependencies: - d3-color: 3.1.0 - d3-interpolate: 3.0.1 - d3-scale@4.0.2: dependencies: d3-array: 3.2.4 @@ -23280,12 +22739,6 @@ snapshots: d3-time: 3.1.0 d3-time-format: 4.1.0 - d3-selection@3.0.0: {} - - d3-shape@1.3.7: - dependencies: - d3-path: 1.0.9 - d3-shape@3.2.0: dependencies: d3-path: 3.1.0 @@ -23300,61 +22753,6 @@ snapshots: d3-timer@3.0.1: {} - d3-transition@3.0.1(d3-selection@3.0.0): - dependencies: - d3-color: 3.1.0 - d3-dispatch: 3.0.1 - d3-ease: 3.0.1 - d3-interpolate: 3.0.1 - d3-selection: 3.0.0 - d3-timer: 3.0.1 - - d3-zoom@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-drag: 3.0.0 - d3-interpolate: 3.0.1 - d3-selection: 3.0.0 - d3-transition: 3.0.1(d3-selection@3.0.0) - - d3@7.9.0: - dependencies: - d3-array: 3.2.4 - d3-axis: 3.0.0 - d3-brush: 3.0.0 - d3-chord: 3.0.1 - d3-color: 3.1.0 - d3-contour: 4.0.2 - d3-delaunay: 6.0.4 - d3-dispatch: 3.0.1 - d3-drag: 3.0.0 - d3-dsv: 3.0.1 - d3-ease: 3.0.1 - d3-fetch: 3.0.1 - d3-force: 3.0.0 - d3-format: 3.1.2 - d3-geo: 3.1.1 - d3-hierarchy: 3.1.2 - d3-interpolate: 3.0.1 - d3-path: 3.1.0 - d3-polygon: 3.0.1 - d3-quadtree: 3.0.1 - d3-random: 3.0.1 - d3-scale: 4.0.2 - d3-scale-chromatic: 3.1.0 - d3-selection: 3.0.0 - d3-shape: 3.2.0 - d3-time: 3.1.0 - d3-time-format: 4.1.0 - d3-timer: 3.0.1 - d3-transition: 3.0.1(d3-selection@3.0.0) - d3-zoom: 3.0.0 - - dagre-d3-es@7.0.14: - dependencies: - d3: 7.9.0 - lodash-es: 4.18.1 - data-urls@7.0.0(@noble/hashes@2.0.1): dependencies: whatwg-mimetype: 5.0.0 @@ -23384,8 +22782,6 @@ snapshots: date-fns@4.1.0: {} - dayjs@1.11.21: {} - debounce-fn@6.0.0: dependencies: mimic-function: 5.0.1 @@ -23451,10 +22847,6 @@ snapshots: defu@6.1.6: {} - delaunator@5.1.0: - dependencies: - robust-predicates: 3.0.3 - delayed-stream@1.0.0: {} dequal@2.0.3: {} @@ -23505,10 +22897,6 @@ snapshots: dependencies: domelementtype: 2.3.0 - dompurify@3.4.12: - optionalDependencies: - '@types/trusted-types': 2.0.7 - domutils@3.2.2: dependencies: dom-serializer: 2.0.0 @@ -24288,8 +23676,6 @@ snapshots: section-matter: 1.0.0 strip-bom-string: 1.0.0 - hachure-fill@0.5.2: {} - has-bigints@1.1.0: {} has-flag@4.0.0: {} @@ -24438,10 +23824,6 @@ snapshots: hyphen@1.14.1: {} - iconv-lite@0.6.3: - dependencies: - safer-buffer: 2.1.2 - ieee754@1.2.1: {} ignore@5.3.2: {} @@ -24478,8 +23860,6 @@ snapshots: cjs-module-lexer: 2.2.0 module-details-from-path: 1.0.4 - import-meta-resolve@4.2.0: {} - imurmurhash@0.1.4: {} inherits@2.0.4: {} @@ -24499,8 +23879,6 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 - internmap@1.0.1: {} - internmap@2.0.3: {} is-alphabetical@2.0.1: {} @@ -24798,10 +24176,6 @@ snapshots: readable-stream: 2.3.8 setimmediate: 1.0.5 - katex@0.16.47: - dependencies: - commander: 8.3.0 - katex@0.18.9: dependencies: commander: 15.0.0 @@ -24810,18 +24184,12 @@ snapshots: dependencies: json-buffer: 3.0.1 - khroma@2.1.0: {} - kind-of@6.0.3: {} kleur@3.0.3: {} kysely@0.28.15: {} - layout-base@1.0.2: {} - - layout-base@2.0.1: {} - leac@0.6.0: {} level-codec@9.0.2: @@ -25008,8 +24376,6 @@ snapshots: dependencies: p-locate: 5.0.0 - lodash-es@4.18.1: {} - lodash.debounce@4.0.8: {} lodash.isequal@4.5.0: {} @@ -25077,8 +24443,6 @@ snapshots: marked@15.0.12: {} - marked@16.4.2: {} - math-intrinsics@1.1.0: {} mathjax-full@3.2.2: @@ -25261,30 +24625,6 @@ snapshots: merge-stream@2.0.0: {} - mermaid@11.16.0: - dependencies: - '@braintree/sanitize-url': 7.1.2 - '@iconify/utils': 3.1.4 - '@mermaid-js/parser': 1.2.0 - '@types/d3': 7.4.3 - '@upsetjs/venn.js': 2.0.0 - cytoscape: 3.34.0 - cytoscape-cose-bilkent: 4.1.0(cytoscape@3.34.0) - cytoscape-fcose: 2.2.0(cytoscape@3.34.0) - d3: 7.9.0 - d3-sankey: 0.12.3 - dagre-d3-es: 7.0.14 - dayjs: 1.11.21 - dompurify: 3.4.12 - es-toolkit: 1.45.1 - katex: 0.16.47 - khroma: 2.1.0 - marked: 16.4.2 - roughjs: 4.6.6 - stylis: 4.4.0 - ts-dedent: 2.3.0 - uuid: 14.0.1 - mhchemparser@4.2.1: {} micromark-core-commonmark@2.0.3: @@ -26040,8 +25380,6 @@ snapshots: path-browserify@1.0.1: {} - path-data-parser@0.1.0: {} - path-exists@4.0.0: {} path-expression-matcher@1.2.0: {} @@ -26076,6 +25414,8 @@ snapshots: peberminta@0.9.0: {} + pele@1.5.2: {} + perfect-debounce@2.1.0: {} pg-cloudflare@1.3.0: @@ -26139,13 +25479,6 @@ snapshots: pngjs@7.0.0: {} - points-on-curve@0.2.0: {} - - points-on-path@0.2.1: - dependencies: - path-data-parser: 0.1.0 - points-on-curve: 0.2.0 - possible-typed-array-names@1.1.0: {} postcss-selector-parser@7.1.1: @@ -26780,8 +26113,6 @@ snapshots: dependencies: glob: 10.5.0 - robust-predicates@3.0.3: {} - rolldown@1.0.0-rc.15: dependencies: '@oxc-project/types': 0.124.0 @@ -26842,17 +26173,8 @@ snapshots: rou3@0.7.12: {} - roughjs@4.6.6: - dependencies: - hachure-fill: 0.5.2 - path-data-parser: 0.1.0 - points-on-curve: 0.2.0 - points-on-path: 0.2.1 - run-applescript@7.1.0: {} - rw@1.3.3: {} - rxjs@7.8.2: dependencies: tslib: 2.8.1 @@ -26880,8 +26202,6 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 - safer-buffer@2.1.2: {} - sax@1.6.0: {} saxes@6.0.0: @@ -27288,8 +26608,6 @@ snapshots: stylis@4.2.0: {} - stylis@4.4.0: {} - supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -27426,8 +26744,6 @@ snapshots: trough@2.2.0: {} - ts-dedent@2.3.0: {} - ts-morph@27.0.2: dependencies: '@ts-morph/common': 0.28.1 @@ -27668,8 +26984,6 @@ snapshots: util-deprecate@1.0.2: {} - uuid@14.0.1: {} - uuid@9.0.1: {} valibot@1.3.1(typescript@7.0.2): diff --git a/tests/src/end-to-end/exporters/__screenshots__/emailImages.test.tsx/email-export-chromium-linux.png b/tests/src/end-to-end/exporters/__screenshots__/emailImages.test.tsx/email-export-chromium-linux.png index fa4eff7007..61ea168796 100644 Binary files a/tests/src/end-to-end/exporters/__screenshots__/emailImages.test.tsx/email-export-chromium-linux.png and b/tests/src/end-to-end/exporters/__screenshots__/emailImages.test.tsx/email-export-chromium-linux.png differ diff --git a/tests/src/end-to-end/exporters/__screenshots__/emailImages.test.tsx/email-export-firefox-linux.png b/tests/src/end-to-end/exporters/__screenshots__/emailImages.test.tsx/email-export-firefox-linux.png index 25ad3701cf..c03f86a268 100644 Binary files a/tests/src/end-to-end/exporters/__screenshots__/emailImages.test.tsx/email-export-firefox-linux.png and b/tests/src/end-to-end/exporters/__screenshots__/emailImages.test.tsx/email-export-firefox-linux.png differ diff --git a/tests/src/end-to-end/exporters/__screenshots__/emailImages.test.tsx/email-export-webkit-linux.png b/tests/src/end-to-end/exporters/__screenshots__/emailImages.test.tsx/email-export-webkit-linux.png index 0545ff099b..7cce10db64 100644 Binary files a/tests/src/end-to-end/exporters/__screenshots__/emailImages.test.tsx/email-export-webkit-linux.png and b/tests/src/end-to-end/exporters/__screenshots__/emailImages.test.tsx/email-export-webkit-linux.png differ diff --git a/tests/src/end-to-end/exporters/__screenshots__/typstPdfImages.test.tsx/typst-pdf-page-4-chromium-linux.png b/tests/src/end-to-end/exporters/__screenshots__/typstPdfImages.test.tsx/typst-pdf-page-4-chromium-linux.png index 47deb26fe5..e6ef055324 100644 Binary files a/tests/src/end-to-end/exporters/__screenshots__/typstPdfImages.test.tsx/typst-pdf-page-4-chromium-linux.png and b/tests/src/end-to-end/exporters/__screenshots__/typstPdfImages.test.tsx/typst-pdf-page-4-chromium-linux.png differ diff --git a/tests/src/end-to-end/exporters/exporterTestUtil.tsx b/tests/src/end-to-end/exporters/exporterTestUtil.tsx index 3a4b165555..13581992ec 100644 --- a/tests/src/end-to-end/exporters/exporterTestUtil.tsx +++ b/tests/src/end-to-end/exporters/exporterTestUtil.tsx @@ -19,7 +19,7 @@ import { screenshotFull } from "../../utils/screenshotFull.js"; // An invalid diagram and an invalid formula, whose typed errors must render // as placeholders without failing the export. Unlike the packages' node // suites, this exercises the real error classes through vite's -// bundling/interop of mermaid and mathjax-full - e.g. a mis-resolved +// bundling/interop of Pele and mathjax-full - e.g. a mis-resolved // `TexError` import only breaks here (and in real apps), not in node. export const invalidDiagramBlock = { id: "invalid-diagram", diff --git a/tests/src/end-to-end/exporters/typstPdfImages.test.tsx b/tests/src/end-to-end/exporters/typstPdfImages.test.tsx index 06836911bc..f6aa1c55c7 100644 --- a/tests/src/end-to-end/exporters/typstPdfImages.test.tsx +++ b/tests/src/end-to-end/exporters/typstPdfImages.test.tsx @@ -112,7 +112,7 @@ describe("pdf/ua export through the complete typst pipeline in the browser", () expect(typ).toContain("Invalid diagram"); expect(typ).toContain("Invalid formula"); - // The default browser renderer produced a real Mermaid render as + // The default renderer produced a real Pele render as // vector SVG, and the document's images resolved to registered assets. // (Asset paths are extension-less; identify the formats by bytes.) const assetBytes = [...exporter.assetFiles.values()]; diff --git a/tests/vite.config.browser.ts b/tests/vite.config.browser.ts index 2d50982148..ba07f00e5e 100644 --- a/tests/vite.config.browser.ts +++ b/tests/vite.config.browser.ts @@ -104,7 +104,7 @@ export default defineConfig( name: "e2e", // Besides the end-to-end tests, this suite also runs the packages' // `.browser.test` files: unit tests for browser-only implementations - // (canvas rasterization, Mermaid rendering), colocated with the code + // (canvas rasterization, diagram rendering), colocated with the code // they test but needing a real browser. The packages' own (node) // vitest configs exclude them. include: [