mirror of
https://github.com/backnotprop/plannotator.git
synced 2026-09-14 14:17:26 +08:00
8e0c51f5ff
Follow-ups from the Workspaces adoption of 0.33.0: a math-slot module hosts can redirect Mermaid's own katex import to (importer-scoped resolveId recipe in HANDOFF) so a math document fetches one KaTeX chunk owned by the host; HtmlViewer bridgeErrorDisplay ('banner' default, 'none' lets a host own the failure banner while onBridgeUnavailable still fires); the documented bridge alias narrowed to relative sibling imports; resetMathRenderer keeps a registered loader and discards stale in-flight loads via an epoch, with setMathRendererLoader(null) and getMathRendererLoader added. Plannotator's own bundles unchanged (markers and sizes within noise of main). Bumps @plannotator/ui to 0.34.0; core stays 0.25.0.
AI-assisted (Claude) under maintainer direction.
89 lines
3.5 KiB
TypeScript
89 lines
3.5 KiB
TypeScript
/**
|
|
* Mermaid's KaTeX served from the math slot (utils/mermaid-math-slot.ts).
|
|
*
|
|
* What regresses if these fail:
|
|
* - a host that redirected Mermaid's `katex` import to this module stops
|
|
* getting its registered renderer for `$$` labels (the default would be
|
|
* requested, or a label would render through the wrong renderer);
|
|
* - Mermaid's own options (`throwOnError: true`, `displayMode: true`, the
|
|
* MathML `output` mode) stop reaching the renderer, changing the markup
|
|
* Mermaid produced from its direct import;
|
|
* - an empty slot fails silently instead of naming the cause;
|
|
* - `hasMermaidMath` drifts from Mermaid's own `$$...$$` test, so the block
|
|
* stops warming the slot for a label Mermaid will render (or warms it for
|
|
* every diagram).
|
|
*
|
|
* That this module never names `katex` (which would re-create the chunk the
|
|
* redirect removes) is pinned on source in tests/entry-assets.test.ts.
|
|
*/
|
|
import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
|
|
import katex from 'katex';
|
|
import mermaidKatex, { MERMAID_MATH_SLOT_EMPTY_MESSAGE, hasMermaidMath } from './mermaid-math-slot';
|
|
import {
|
|
getMathRenderer,
|
|
getMathRendererSource,
|
|
resetMathRenderer,
|
|
setMathRenderer,
|
|
setMathRendererLoader,
|
|
type MathRenderer,
|
|
} from './math';
|
|
|
|
const savedRenderer = getMathRenderer();
|
|
const savedSource = getMathRendererSource();
|
|
|
|
beforeEach(() => {
|
|
resetMathRenderer();
|
|
setMathRendererLoader(null);
|
|
});
|
|
|
|
afterEach(() => {
|
|
resetMathRenderer();
|
|
setMathRendererLoader(null);
|
|
if (savedRenderer) setMathRenderer(savedRenderer, savedSource ?? 'host');
|
|
});
|
|
|
|
/** The exact call Mermaid 11 makes in `renderKatexUnsanitized`. */
|
|
const MERMAID_OPTIONS = { throwOnError: true, displayMode: true, output: 'mathml' } as const;
|
|
|
|
describe('default export (what Mermaid receives for `katex`)', () => {
|
|
test('delegates to the registered renderer with the options Mermaid passed', () => {
|
|
const seen: unknown[] = [];
|
|
const hostRenderer: MathRenderer = {
|
|
renderToString: (tex, options) => {
|
|
seen.push([tex, options]);
|
|
return `<host>${tex}</host>`;
|
|
},
|
|
};
|
|
setMathRenderer(hostRenderer);
|
|
expect(mermaidKatex.renderToString('\\sqrt{2}', MERMAID_OPTIONS)).toBe('<host>\\sqrt{2}</host>');
|
|
expect(seen).toEqual([['\\sqrt{2}', MERMAID_OPTIONS]]);
|
|
});
|
|
|
|
test('with KaTeX in the slot the markup is what Mermaid got from its direct import', () => {
|
|
setMathRenderer(katex);
|
|
expect(mermaidKatex.renderToString('x^2', MERMAID_OPTIONS)).toBe(katex.renderToString('x^2', MERMAID_OPTIONS));
|
|
});
|
|
|
|
test('an empty slot throws a message naming the cause instead of rendering nothing', () => {
|
|
expect(() => mermaidKatex.renderToString('x', MERMAID_OPTIONS)).toThrow(MERMAID_MATH_SLOT_EMPTY_MESSAGE);
|
|
});
|
|
|
|
test('follows the slot: a later registration is picked up without re-importing', () => {
|
|
const first: MathRenderer = { renderToString: () => 'first' };
|
|
const second: MathRenderer = { renderToString: () => 'second' };
|
|
setMathRenderer(first);
|
|
expect(mermaidKatex.renderToString('x')).toBe('first');
|
|
setMathRenderer(second);
|
|
expect(mermaidKatex.renderToString('x')).toBe('second');
|
|
});
|
|
});
|
|
|
|
describe('hasMermaidMath', () => {
|
|
test('matches exactly the `$$...$$` labels Mermaid renders through KaTeX', () => {
|
|
expect(hasMermaidMath('flowchart LR\n A["$$\\sqrt{2}$$"] --> B')).toBe(true);
|
|
expect(hasMermaidMath('A["$$$$"] --> B')).toBe(true);
|
|
expect(hasMermaidMath('flowchart LR\n A[Cost: $5] --> B[$10]')).toBe(false);
|
|
expect(hasMermaidMath('flowchart LR\n A --> B')).toBe(false);
|
|
});
|
|
});
|