@open-agent-toolkit/docs-theme 0.3.34 → 0.3.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -62,9 +62,15 @@ export function Page({ toc, chart }) {
62
62
  `@source '../node_modules/@open-agent-toolkit/docs-theme/dist/**/*.js';`
63
63
  to your Tailwind CSS so those theme classes are generated.
64
64
 
65
- `Mermaid` diagrams take their colors from the site's Fumadocs color tokens
66
- (`--color-fd-*`) in light and dark mode, and open in the same full-screen view
67
- when clicked.
65
+ `Mermaid` renders Mermaid diagram source with [Pele](https://pele.run), loading
66
+ each diagram type on demand. Diagrams take their colors from the site's
67
+ Fumadocs color tokens (`--color-fd-*`) through Pele's `--pele-*` CSS variables,
68
+ so they follow light and dark mode without re-rendering, and open in the same
69
+ full-screen view when clicked. To recolor diagrams, set the `--color-fd-*`
70
+ tokens: the component sets `--pele-*` inline, which overrides any `--pele-*`
71
+ values in a stylesheet. Equations written as `$$...$$` in labels and messages
72
+ render as MathML through KaTeX, which loads only for diagrams that contain
73
+ them; no KaTeX stylesheet is needed.
68
74
 
69
75
  ## Docs
70
76
 
@@ -1 +1 @@
1
- {"version":3,"file":"mermaid.d.ts","sourceRoot":"","sources":["../src/mermaid.tsx"],"names":[],"mappings":"AAQA,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,YAAY,2CA4D9C"}
1
+ {"version":3,"file":"mermaid.d.ts","sourceRoot":"","sources":["../src/mermaid.tsx"],"names":[],"mappings":"AAQA,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,YAAY,2CAqD9C"}
package/dist/mermaid.js CHANGED
@@ -1,27 +1,26 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { useTheme } from 'next-themes';
4
- import { useEffect, useRef, useState } from 'react';
5
- import { siteMermaidThemeVariables } from './mermaid-theme.js';
3
+ import { useEffect, useId, useRef, useState } from 'react';
4
+ import { sitePeleTheme } from './pele-theme.js';
5
+ import { measureIn, renderChart } from './render-chart.js';
6
6
  import { ZoomFrame } from './zoom-frame.js';
7
7
  export function Mermaid({ chart }) {
8
8
  const containerRef = useRef(null);
9
- const [svg, setSvg] = useState('');
10
- const { resolvedTheme } = useTheme();
9
+ const [svg, setSvg] = useState({ inline: '', zoomed: '' });
10
+ // Unique per instance so the inline and full-screen copies of every diagram
11
+ // on a page get distinct element IDs (title, desc, markers).
12
+ const idPrefix = `pele-${useId().replace(/[^\w-]/g, '')}`;
11
13
  useEffect(() => {
12
14
  let cancelled = false;
13
15
  async function render() {
14
- const mermaid = (await import('mermaid')).default;
15
- const dark = resolvedTheme === 'dark';
16
- const themeVariables = siteMermaidThemeVariables(dark);
17
- mermaid.initialize({
18
- startOnLoad: false,
19
- ...(themeVariables
20
- ? { theme: 'base', themeVariables }
21
- : { theme: dark ? 'dark' : 'default' }),
16
+ const host = containerRef.current;
17
+ if (!host)
18
+ return;
19
+ const rendered = await renderChart(chart, {
20
+ idPrefix,
21
+ fontFamily: getComputedStyle(host).fontFamily,
22
+ measure: measureIn(host),
22
23
  });
23
- const id = `mermaid-${Math.random().toString(36).slice(2, 9)}`;
24
- const { svg: rendered } = await mermaid.render(id, chart);
25
24
  if (!cancelled) {
26
25
  setSvg(rendered);
27
26
  }
@@ -30,12 +29,10 @@ export function Mermaid({ chart }) {
30
29
  return () => {
31
30
  cancelled = true;
32
31
  };
33
- }, [chart, resolvedTheme]);
34
- return (_jsx(ZoomFrame, { label: 'Diagram', disabled: !svg, zoomed: _jsx("div", { className: 'mermaid',
35
- // oxlint-disable-next-line react/no-danger -- same trusted SVG as above
36
- dangerouslySetInnerHTML: {
37
- __html: svg.replace(/<svg\b([^>]*?)\sstyle="[^"]*"/, '<svg$1 style="width:100%;height:auto;max-width:none"'),
38
- } }), children: _jsx("div", { ref: containerRef, className: 'mermaid',
39
- // oxlint-disable-next-line react/no-danger -- mermaid renders SVG from trusted chart definitions
40
- dangerouslySetInnerHTML: { __html: svg } }) }));
32
+ }, [chart, idPrefix]);
33
+ return (_jsx(ZoomFrame, { label: 'Diagram', disabled: !svg.inline, zoomed: _jsx("div", { className: 'mermaid', style: sitePeleTheme,
34
+ // oxlint-disable-next-line react/no-danger -- same trusted source as above
35
+ dangerouslySetInnerHTML: { __html: svg.zoomed } }), children: _jsx("div", { ref: containerRef, className: 'mermaid', style: sitePeleTheme,
36
+ // oxlint-disable-next-line react/no-danger -- Pele escapes diagram text and filters links and styles; KaTeX escapes math with trust off
37
+ dangerouslySetInnerHTML: { __html: svg.inline } }) }));
41
38
  }
@@ -0,0 +1,13 @@
1
+ import type { CSSProperties } from 'react';
2
+ /**
3
+ * Maps Pele's theme variables to the site's Fumadocs color tokens, so diagrams
4
+ * follow the docs palette in light and dark mode.
5
+ *
6
+ * Applied as inline custom properties on the diagram wrapper, the same way the
7
+ * other components here reference `--color-fd-*` tokens, so consumers need no
8
+ * extra stylesheet. Pele's SVG reads `var(--pele-*)` from its ancestors, so a
9
+ * theme switch recolors diagrams without re-rendering. A token the site does
10
+ * not define leaves the variable invalid, and Pele falls back to its default.
11
+ */
12
+ export declare const sitePeleTheme: CSSProperties;
13
+ //# sourceMappingURL=pele-theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pele-theme.d.ts","sourceRoot":"","sources":["../src/pele-theme.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,EASrB,aAAa,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Maps Pele's theme variables to the site's Fumadocs color tokens, so diagrams
3
+ * follow the docs palette in light and dark mode.
4
+ *
5
+ * Applied as inline custom properties on the diagram wrapper, the same way the
6
+ * other components here reference `--color-fd-*` tokens, so consumers need no
7
+ * extra stylesheet. Pele's SVG reads `var(--pele-*)` from its ancestors, so a
8
+ * theme switch recolors diagrams without re-rendering. A token the site does
9
+ * not define leaves the variable invalid, and Pele falls back to its default.
10
+ */
11
+ export const sitePeleTheme = {
12
+ '--pele-bg': 'var(--color-fd-card)',
13
+ '--pele-fg': 'var(--color-fd-foreground)',
14
+ '--pele-muted': 'var(--color-fd-muted-foreground)',
15
+ '--pele-line': 'var(--color-fd-muted-foreground)',
16
+ '--pele-surface': 'var(--color-fd-secondary)',
17
+ '--pele-surface-alt': 'var(--color-fd-muted)',
18
+ '--pele-border': 'var(--color-fd-primary)',
19
+ '--pele-accent': 'var(--color-fd-primary)',
20
+ };
@@ -0,0 +1,22 @@
1
+ /** Size of rendered math markup at a font size, in CSS pixels. */
2
+ export type MeasureMath = (html: string, size: number) => {
3
+ width: number;
4
+ height: number;
5
+ } | null;
6
+ export interface RenderChartOptions {
7
+ idPrefix: string;
8
+ fontFamily?: string;
9
+ measure: MeasureMath;
10
+ }
11
+ export interface RenderedChart {
12
+ inline: string;
13
+ zoomed: string;
14
+ }
15
+ /**
16
+ * Renders a chart's inline SVG and its full-screen copy with distinct element
17
+ * IDs. KaTeX loads only for charts that contain `$$`.
18
+ */
19
+ export declare function renderChart(chart: string, { idPrefix, fontFamily, measure }: RenderChartOptions): Promise<RenderedChart>;
20
+ /** Measures math markup in a hidden probe inside `host`, inheriting its fonts. */
21
+ export declare function measureIn(host: HTMLElement): MeasureMath;
22
+ //# sourceMappingURL=render-chart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-chart.d.ts","sourceRoot":"","sources":["../src/render-chart.ts"],"names":[],"mappings":"AAIA,kEAAkE;AAClE,MAAM,MAAM,WAAW,GAAG,CACxB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,KACT;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC;AAE9C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AA6BD;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,EACb,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,kBAAkB,GACpD,OAAO,CAAC,aAAa,CAAC,CAYxB;AAED,kFAAkF;AAClF,wBAAgB,SAAS,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW,CAYxD"}
@@ -0,0 +1,57 @@
1
+ import { fillWidth } from './zoom-svg.js';
2
+ // Pele places `$$...$$` equations from labels and messages through this
3
+ // resolver and needs their size up front. KaTeX's MathML output renders
4
+ // natively in browsers, so no KaTeX stylesheet is required.
5
+ async function loadMath(measure) {
6
+ const { default: katex } = await import('katex');
7
+ return (tex, size) => {
8
+ const html = katex.renderToString(tex, {
9
+ output: 'mathml',
10
+ // Mermaid renders `$$...$$` as display math, which `\tag` needs.
11
+ displayMode: true,
12
+ throwOnError: false,
13
+ // Pele inserts this markup unsanitized; keep KaTeX's escaping strict.
14
+ trust: false,
15
+ });
16
+ // A host that is not laid out yet (a hidden tab or collapsed section)
17
+ // measures 0x0; estimate from the source so the equation still renders.
18
+ return { html, ...(measure(html, size) ?? estimateBox(tex, size)) };
19
+ };
20
+ }
21
+ function estimateBox(tex, size) {
22
+ return {
23
+ width: Math.ceil(tex.length * size * 0.6),
24
+ height: Math.ceil(size * 1.5),
25
+ };
26
+ }
27
+ /**
28
+ * Renders a chart's inline SVG and its full-screen copy with distinct element
29
+ * IDs. KaTeX loads only for charts that contain `$$`.
30
+ */
31
+ export async function renderChart(chart, { idPrefix, fontFamily, measure }) {
32
+ const [{ renderAsync }, math] = await Promise.all([
33
+ import('pele/lazy'),
34
+ chart.includes('$$') ? loadMath(measure) : undefined,
35
+ ]);
36
+ const inline = await renderAsync(chart, { idPrefix, fontFamily, math });
37
+ const zoomed = await renderAsync(chart, {
38
+ idPrefix: `${idPrefix}-zoom`,
39
+ fontFamily,
40
+ math,
41
+ });
42
+ return { inline: inline.svg, zoomed: fillWidth(zoomed.svg) };
43
+ }
44
+ /** Measures math markup in a hidden probe inside `host`, inheriting its fonts. */
45
+ export function measureIn(host) {
46
+ return (html, size) => {
47
+ const probe = host.ownerDocument.createElement('span');
48
+ probe.style.cssText = `position:absolute;visibility:hidden;left:0;top:0;white-space:nowrap;line-height:normal;font-size:${size}px`;
49
+ probe.innerHTML = html;
50
+ host.append(probe);
51
+ const { width, height } = probe.getBoundingClientRect();
52
+ probe.remove();
53
+ return width > 0 && height > 0
54
+ ? { width: Math.ceil(width), height: Math.ceil(height) }
55
+ : null;
56
+ };
57
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Sizes a rendered diagram to fill the full-screen view. Pele sizes its root
3
+ * `<svg>` to the diagram's natural width, capped at the container, and keeps
4
+ * its theme variables in the same style attribute, so append the sizing
5
+ * rather than replace the attribute; the later declarations win.
6
+ */
7
+ export declare function fillWidth(svg: string): string;
8
+ //# sourceMappingURL=zoom-svg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zoom-svg.d.ts","sourceRoot":"","sources":["../src/zoom-svg.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAK7C"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Sizes a rendered diagram to fill the full-screen view. Pele sizes its root
3
+ * `<svg>` to the diagram's natural width, capped at the container, and keeps
4
+ * its theme variables in the same style attribute, so append the sizing
5
+ * rather than replace the attribute; the later declarations win.
6
+ */
7
+ export function fillWidth(svg) {
8
+ return svg.replace(/^(<svg\b[^>]*?\sstyle=")([^"]*)"/, '$1$2;width:100%;height:auto;max-width:none"');
9
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-agent-toolkit/docs-theme",
3
- "version": "0.3.34",
3
+ "version": "0.3.36",
4
4
  "private": false,
5
5
  "description": "Theme components for OAT documentation sites",
6
6
  "homepage": "https://github.com/voxmedia/open-agent-toolkit/tree/main/packages/docs-theme",
@@ -30,8 +30,8 @@
30
30
  "access": "public"
31
31
  },
32
32
  "dependencies": {
33
- "mermaid": "^11.6.0",
34
- "next-themes": "^0.4.6"
33
+ "katex": "0.19.1",
34
+ "pele": "1.10.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/node": "^22.10.0",
@@ -1,7 +0,0 @@
1
- /**
2
- * Mermaid theme variables for the current page, or `undefined` when the site
3
- * does not define the Fumadocs color tokens (Mermaid then uses its stock
4
- * theme).
5
- */
6
- export declare function siteMermaidThemeVariables(dark: boolean): Record<string, string | boolean> | undefined;
7
- //# sourceMappingURL=mermaid-theme.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mermaid-theme.d.ts","sourceRoot":"","sources":["../src/mermaid-theme.ts"],"names":[],"mappings":"AAiCA;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,OAAO,GACZ,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,SAAS,CAqD9C"}
@@ -1,88 +0,0 @@
1
- // Builds Mermaid `base` theme variables from the site's Fumadocs color tokens
2
- // so diagrams follow the docs site's palette in light and dark mode.
3
- const TOKENS = {
4
- background: '--color-fd-background',
5
- card: '--color-fd-card',
6
- foreground: '--color-fd-foreground',
7
- mutedForeground: '--color-fd-muted-foreground',
8
- muted: '--color-fd-muted',
9
- secondary: '--color-fd-secondary',
10
- border: '--color-fd-border',
11
- primary: '--color-fd-primary',
12
- };
13
- // Mermaid derives shades from its theme variables and only understands sRGB
14
- // hex/rgb input, while sites may define tokens in any CSS color space. Paint
15
- // the color into a 1x1 canvas and read the sRGB pixel back.
16
- function toHex(color, ctx) {
17
- if (!color)
18
- return undefined;
19
- ctx.clearRect(0, 0, 1, 1);
20
- ctx.fillStyle = '#000';
21
- ctx.fillStyle = color;
22
- ctx.fillRect(0, 0, 1, 1);
23
- const [r, g, b, a] = ctx.getImageData(0, 0, 1, 1).data;
24
- if (a === 0)
25
- return undefined;
26
- return `#${[r, g, b].map((n) => n.toString(16).padStart(2, '0')).join('')}`;
27
- }
28
- /**
29
- * Mermaid theme variables for the current page, or `undefined` when the site
30
- * does not define the Fumadocs color tokens (Mermaid then uses its stock
31
- * theme).
32
- */
33
- export function siteMermaidThemeVariables(dark) {
34
- if (typeof document === 'undefined')
35
- return undefined;
36
- const ctx = document
37
- .createElement('canvas')
38
- .getContext('2d', { willReadFrequently: true });
39
- if (!ctx)
40
- return undefined;
41
- const style = getComputedStyle(document.documentElement);
42
- const c = {};
43
- for (const [name, prop] of Object.entries(TOKENS)) {
44
- const hex = toHex(style.getPropertyValue(prop).trim(), ctx);
45
- if (!hex)
46
- return undefined;
47
- c[name] = hex;
48
- }
49
- return {
50
- darkMode: dark,
51
- fontFamily: getComputedStyle(document.body).fontFamily,
52
- background: c.card,
53
- textColor: c.foreground,
54
- titleColor: c.foreground,
55
- lineColor: c.mutedForeground,
56
- primaryColor: c.secondary,
57
- primaryTextColor: c.foreground,
58
- primaryBorderColor: c.primary,
59
- secondaryColor: c.muted,
60
- secondaryTextColor: c.foreground,
61
- secondaryBorderColor: c.border,
62
- tertiaryColor: c.card,
63
- tertiaryTextColor: c.foreground,
64
- tertiaryBorderColor: c.border,
65
- mainBkg: c.secondary,
66
- nodeBorder: c.primary,
67
- nodeTextColor: c.foreground,
68
- clusterBkg: c.muted,
69
- clusterBorder: c.border,
70
- edgeLabelBackground: c.card,
71
- noteBkgColor: c.muted,
72
- noteTextColor: c.foreground,
73
- noteBorderColor: c.border,
74
- actorBkg: c.secondary,
75
- actorBorder: c.primary,
76
- actorTextColor: c.foreground,
77
- actorLineColor: c.mutedForeground,
78
- signalColor: c.foreground,
79
- signalTextColor: c.foreground,
80
- labelBoxBkgColor: c.secondary,
81
- labelBoxBorderColor: c.border,
82
- labelTextColor: c.foreground,
83
- loopTextColor: c.foreground,
84
- activationBkgColor: c.muted,
85
- activationBorderColor: c.primary,
86
- sequenceNumberColor: c.card,
87
- };
88
- }