From fef303e78d763673fa9a9ab661f533834198e35b Mon Sep 17 00:00:00 2001 From: Dimitri Kennedy Date: Sat, 24 Oct 2026 10:51:58 -0400 Subject: [PATCH 1/6] feat(hd): adopt Pele 1.9.0 for Mermaid with Hack Dance house theme - Pin pele to exactly 1.9.0 (MIT, npm provenance from obsidianmd/pele). - Centralize PELE_SAFE_OPTIONS (links:false, images:false). - Add --hd-diagram-* house theme mapped to --pele-* and T3 tokens (light/dark). - Show render errors as plain text under the raw source block. - Add a file snapshot test for the rendered SVG. --- apps/web/package.json | 2 +- .../src/components/chat/MermaidDiagram.tsx | 20 ++++---- .../__snapshots__/peleRender.flowchart.svg | 1 + apps/web/src/components/chat/peleOptions.ts | 6 +++ .../src/components/chat/peleRender.test.ts | 28 +++++++++++ apps/web/src/hd-diagram-theme.css | 49 +++++++++++++++++++ apps/web/src/index.css | 15 +----- pnpm-lock.yaml | 10 ++-- 8 files changed, 102 insertions(+), 29 deletions(-) create mode 100644 apps/web/src/components/chat/__snapshots__/peleRender.flowchart.svg create mode 100644 apps/web/src/components/chat/peleOptions.ts create mode 100644 apps/web/src/components/chat/peleRender.test.ts create mode 100644 apps/web/src/hd-diagram-theme.css diff --git a/apps/web/package.json b/apps/web/package.json index 1fc7fd6eb5fa..0df51bcade53 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -66,7 +66,7 @@ "lucide": "^0.564.0", "lucide-react": "^0.564.0", "morphicons": "^1.7.1", - "pele": "^1.8.0", + "pele": "1.9.0", "react": "catalog:", "react-dom": "catalog:", "react-markdown": "^10.1.0", diff --git a/apps/web/src/components/chat/MermaidDiagram.tsx b/apps/web/src/components/chat/MermaidDiagram.tsx index 11f98ec57a73..8d91c929e526 100644 --- a/apps/web/src/components/chat/MermaidDiagram.tsx +++ b/apps/web/src/components/chat/MermaidDiagram.tsx @@ -4,6 +4,7 @@ import { use, useLayoutEffect, useRef, useState } from "react"; import { serializeCodeBlockToMarkdown } from "../../markdown-clipboard"; import { Button } from "../ui/button"; +import { PELE_SAFE_OPTIONS } from "./peleOptions"; type Pele = typeof import("pele/lazy"); type PeleLoad = { readonly status: "loaded"; readonly pele: Pele } | { readonly status: "failed" }; @@ -239,8 +240,7 @@ export function MermaidDiagram({ const container = containerRef.current; if (load.status !== "loaded" || !container) return; const mounted = load.pele.mount(container, trimmedSource, { - links: false, - images: false, + ...PELE_SAFE_OPTIONS, zoom: false, // Keep the author's direction; a wide diagram scales down to fit instead of turning vertical. autoDirection: false, @@ -302,20 +302,22 @@ function DiagramError({ }) { return (
+
+        {source}
+      
-

Unable to render diagram: {message}

+

+ Unable to render diagram: {message} +

{onRetry ? ( ) : null}
-
-        {source}
-      
); } diff --git a/apps/web/src/components/chat/__snapshots__/peleRender.flowchart.svg b/apps/web/src/components/chat/__snapshots__/peleRender.flowchart.svg new file mode 100644 index 000000000000..c2924c7986c5 --- /dev/null +++ b/apps/web/src/components/chat/__snapshots__/peleRender.flowchart.svg @@ -0,0 +1 @@ +readsprimaryfallbacktooltipAgentSidecarExtensioncua-driver \ No newline at end of file diff --git a/apps/web/src/components/chat/peleOptions.ts b/apps/web/src/components/chat/peleOptions.ts new file mode 100644 index 000000000000..39a73e7c0a3b --- /dev/null +++ b/apps/web/src/components/chat/peleOptions.ts @@ -0,0 +1,6 @@ +/** + * Options every Pele render in T3 uses. Diagrams can come from untrusted + * text, so they never emit links or load images. Fleet renderers + * (pele-render, the KB build) use the same pair. + */ +export const PELE_SAFE_OPTIONS = { links: false, images: false } as const; diff --git a/apps/web/src/components/chat/peleRender.test.ts b/apps/web/src/components/chat/peleRender.test.ts new file mode 100644 index 000000000000..bed72dc7a37b --- /dev/null +++ b/apps/web/src/components/chat/peleRender.test.ts @@ -0,0 +1,28 @@ +import { render } from "pele"; +import { describe, expect, it } from "vite-plus/test"; + +import { PELE_SAFE_OPTIONS } from "./peleOptions"; + +const FLOWCHART = `flowchart LR + A[Agent] -->|reads| B{Sidecar} + B -->|primary| C[Extension] + B -.->|fallback| D[cua-driver] + click A "https://example.com/phish" "tooltip" +`; + +describe("pele render", () => { + it("renders a flowchart to a stable SVG", async () => { + const { svg } = render(FLOWCHART, PELE_SAFE_OPTIONS); + expect(svg.startsWith(" { + const { svg } = render(FLOWCHART, PELE_SAFE_OPTIONS); + expect(svg).not.toMatch(/]|href=|]|example\.com/); + }); + + it("throws a readable error for invalid source", () => { + expect(() => render("flowchart LR\n A -->", PELE_SAFE_OPTIONS)).toThrow(); + }); +}); diff --git a/apps/web/src/hd-diagram-theme.css b/apps/web/src/hd-diagram-theme.css new file mode 100644 index 000000000000..ee319d932296 --- /dev/null +++ b/apps/web/src/hd-diagram-theme.css @@ -0,0 +1,49 @@ +/* Hack Dance house style for Pele (Mermaid) diagrams. + Source of truth: roodboi/machine files/pele/hack-dance-theme.css. Keep the + series palette here in sync with it. + + Tweak the --hd-diagram-* knobs. Neutral knobs are unset by default, so + diagrams follow the active T3 theme tokens in light and dark; set one to + pin a color. Pele reads its colors through --pele-*, so a theme change + recolors diagrams without re-rendering. */ +:root { + --hd-diagram-series-1: #e4572e; + --hd-diagram-series-2: #2e86ab; + --hd-diagram-series-3: #7b4fd6; + --hd-diagram-series-4: #17a398; + --hd-diagram-series-5: #d99a1e; + --hd-diagram-series-6: #c2304a; + --hd-diagram-series-7: #4f7a3a; + --hd-diagram-series-8: #6b7280; +} + +.dark { + --hd-diagram-series-1: #ff8a5c; + --hd-diagram-series-2: #5fb4dc; + --hd-diagram-series-3: #a98bff; + --hd-diagram-series-4: #4fd1c0; + --hd-diagram-series-5: #f5c15a; + --hd-diagram-series-6: #ff6b81; + --hd-diagram-series-7: #8cc56c; + --hd-diagram-series-8: #a3aab5; +} + +.pele { + --pele-bg: var(--hd-diagram-bg, var(--background)); + --pele-fg: var(--hd-diagram-fg, var(--foreground)); + --pele-muted: var(--hd-diagram-muted, var(--muted-foreground)); + --pele-line: var(--hd-diagram-line, var(--muted-foreground)); + --pele-surface: var(--hd-diagram-surface, var(--secondary)); + --pele-surface-alt: var(--hd-diagram-surface-alt, var(--accent)); + --pele-border: var(--hd-diagram-border, color-mix(in srgb, var(--foreground) 28%, transparent)); + --pele-accent: var(--hd-diagram-accent, var(--primary)); + --pele-critical: var(--hd-diagram-critical, var(--destructive)); + --pele-series-1: var(--hd-diagram-series-1); + --pele-series-2: var(--hd-diagram-series-2); + --pele-series-3: var(--hd-diagram-series-3); + --pele-series-4: var(--hd-diagram-series-4); + --pele-series-5: var(--hd-diagram-series-5); + --pele-series-6: var(--hd-diagram-series-6); + --pele-series-7: var(--hd-diagram-series-7); + --pele-series-8: var(--hd-diagram-series-8); +} diff --git a/apps/web/src/index.css b/apps/web/src/index.css index 01fa601ab666..e53ca1f19708 100644 --- a/apps/web/src/index.css +++ b/apps/web/src/index.css @@ -1,4 +1,5 @@ @import "tailwindcss"; +@import "./hd-diagram-theme.css"; @custom-variant dark (&:is(.dark, .dark *)); @custom-variant light (&:not(.dark, .dark *)); @@ -1729,20 +1730,6 @@ code { --diffs-header-font-family: var(--font-sans); } -/* Pele diagrams read their colors through these, so a theme change recolors - them without re-rendering. */ -.pele { - --pele-bg: var(--background); - --pele-fg: var(--foreground); - --pele-muted: var(--muted-foreground); - --pele-line: var(--muted-foreground); - --pele-surface: var(--secondary); - --pele-surface-alt: var(--accent); - --pele-border: color-mix(in srgb, var(--foreground) 28%, transparent); - --pele-accent: var(--primary); - --pele-critical: var(--destructive); -} - /* Window drag region (frameless titlebar) */ .drag-region { -webkit-app-region: drag; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c004aa9a0c30..f42293c537f0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1131,8 +1131,8 @@ importers: specifier: ^1.7.1 version: 1.7.1(react-native-svg@15.16.0(react-native@0.88.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.88.0(@babel/core@7.29.7(supports-color@8.1.1))(bufferutil@4.1.0)(supports-color@8.1.1)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(supports-color@8.1.1)(utf-8-validate@6.0.6))(react@19.3.0))(react-native@0.88.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.88.0(@babel/core@7.29.7(supports-color@8.1.1))(bufferutil@4.1.0)(supports-color@8.1.1)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(supports-color@8.1.1)(utf-8-validate@6.0.6))(react@19.3.0) pele: - specifier: ^1.8.0 - version: 1.8.0 + specifier: 1.9.0 + version: 1.9.0 react: specifier: 'catalog:' version: 19.3.0 @@ -10269,8 +10269,8 @@ packages: resolution: {integrity: sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==} engines: {node: '>=12', npm: '>=6'} - pele@1.8.0: - resolution: {integrity: sha512-AYmHttE70cwNJVXdfPMCANGivFlmc3bGGpMG0GqH5S5vE3RgtEuLlkqbhRfe94Csv/ZEd1TUbv2dMUHpw4p1Bg==} + pele@1.9.0: + resolution: {integrity: sha512-KUmBt7P5JduVKQaP6FKfXiRgH3LBVIKsmdgPJZU7jD3ye6F+Z8fbms+OjivE7rWY09Qx+5X65VKe/9ywKFJ9Sw==} engines: {node: '>=20'} pend@1.2.0: @@ -20775,7 +20775,7 @@ snapshots: pe-library@0.4.1: {} - pele@1.8.0: {} + pele@1.9.0: {} pend@1.2.0: {} From fddf4025ec965ca1ccf31dbbd2b6b167b0668597 Mon Sep 17 00:00:00 2001 From: Dimitri Kennedy Date: Sat, 24 Oct 2026 10:56:22 -0400 Subject: [PATCH 2/6] test(web): cover remote image blocking in pele snapshot --- .../src/components/chat/__snapshots__/peleRender.flowchart.svg | 2 +- apps/web/src/components/chat/peleRender.test.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/web/src/components/chat/__snapshots__/peleRender.flowchart.svg b/apps/web/src/components/chat/__snapshots__/peleRender.flowchart.svg index c2924c7986c5..1c610379851f 100644 --- a/apps/web/src/components/chat/__snapshots__/peleRender.flowchart.svg +++ b/apps/web/src/components/chat/__snapshots__/peleRender.flowchart.svg @@ -1 +1 @@ -readsprimaryfallbacktooltipAgentSidecarExtensioncua-driver \ No newline at end of file +readsprimaryfallbacktooltipAgentSidecarExtensioncua-driverRemote \ No newline at end of file diff --git a/apps/web/src/components/chat/peleRender.test.ts b/apps/web/src/components/chat/peleRender.test.ts index bed72dc7a37b..ef9902aaa6b1 100644 --- a/apps/web/src/components/chat/peleRender.test.ts +++ b/apps/web/src/components/chat/peleRender.test.ts @@ -7,6 +7,8 @@ const FLOWCHART = `flowchart LR A[Agent] -->|reads| B{Sidecar} B -->|primary| C[Extension] B -.->|fallback| D[cua-driver] + I@{ img: "https://example.com/track.png", label: "Remote", pos: "t" } + C --> I click A "https://example.com/phish" "tooltip" `; From a19100ad46878eddfba09639fa55562afed1b170 Mon Sep 17 00:00:00 2001 From: Dimitri Kennedy Date: Sat, 24 Oct 2026 10:59:33 -0400 Subject: [PATCH 3/6] feat(web): Hack Dance house diagram theme (Lucy kit) mapped to T3 tokens, live/wait/ok/fail/idle classes --- apps/web/src/hd-diagram-theme.css | 172 +++++++++++++++++++++++------- 1 file changed, 131 insertions(+), 41 deletions(-) diff --git a/apps/web/src/hd-diagram-theme.css b/apps/web/src/hd-diagram-theme.css index ee319d932296..91916f3b00b7 100644 --- a/apps/web/src/hd-diagram-theme.css +++ b/apps/web/src/hd-diagram-theme.css @@ -1,49 +1,139 @@ -/* Hack Dance house style for Pele (Mermaid) diagrams. - Source of truth: roodboi/machine files/pele/hack-dance-theme.css. Keep the - series palette here in sync with it. - - Tweak the --hd-diagram-* knobs. Neutral knobs are unset by default, so - diagrams follow the active T3 theme tokens in light and dark; set one to - pin a color. Pele reads its colors through --pele-*, so a theme change - recolors diagrams without re-rendering. */ +/* Hack Dance house theme for Pele (Mermaid) diagrams, mapped to T3 tokens. + Source of truth: roodboi/machine files/pele/hackdance.css (Lucy's style kit). + Neutrals, accent, error and the live/wait/ok/fail/idle state colors follow the + active T3 theme tokens (light and dark, custom themes too); the series palette + and font stacks come from the house theme. Pele reads colors through --pele-*, + so a theme change recolors diagrams without re-rendering. + Authors use house classes only: `classDef live stroke-width:1.6px` + `class X live`. */ :root { - --hd-diagram-series-1: #e4572e; - --hd-diagram-series-2: #2e86ab; - --hd-diagram-series-3: #7b4fd6; - --hd-diagram-series-4: #17a398; - --hd-diagram-series-5: #d99a1e; - --hd-diagram-series-6: #c2304a; - --hd-diagram-series-7: #4f7a3a; - --hd-diagram-series-8: #6b7280; + --hd-series-1: #5b3df5; + --hd-series-2: #0e9f8a; + --hd-series-3: #e08a00; + --hd-series-4: #d6345a; + --hd-series-5: #2f7de1; + --hd-series-6: #8a5cf6; + --hd-series-7: #4f9d2d; + --hd-series-8: #a0674b; } .dark { - --hd-diagram-series-1: #ff8a5c; - --hd-diagram-series-2: #5fb4dc; - --hd-diagram-series-3: #a98bff; - --hd-diagram-series-4: #4fd1c0; - --hd-diagram-series-5: #f5c15a; - --hd-diagram-series-6: #ff6b81; - --hd-diagram-series-7: #8cc56c; - --hd-diagram-series-8: #a3aab5; + --hd-series-1: #9a86ff; + --hd-series-2: #3fd1b8; + --hd-series-3: #ffb547; + --hd-series-4: #ff6b8b; + --hd-series-5: #6aa8ff; + --hd-series-6: #c4a5ff; + --hd-series-7: #8fd16a; + --hd-series-8: #d29b7f; } .pele { - --pele-bg: var(--hd-diagram-bg, var(--background)); - --pele-fg: var(--hd-diagram-fg, var(--foreground)); - --pele-muted: var(--hd-diagram-muted, var(--muted-foreground)); - --pele-line: var(--hd-diagram-line, var(--muted-foreground)); - --pele-surface: var(--hd-diagram-surface, var(--secondary)); - --pele-surface-alt: var(--hd-diagram-surface-alt, var(--accent)); - --pele-border: var(--hd-diagram-border, color-mix(in srgb, var(--foreground) 28%, transparent)); - --pele-accent: var(--hd-diagram-accent, var(--primary)); - --pele-critical: var(--hd-diagram-critical, var(--destructive)); - --pele-series-1: var(--hd-diagram-series-1); - --pele-series-2: var(--hd-diagram-series-2); - --pele-series-3: var(--hd-diagram-series-3); - --pele-series-4: var(--hd-diagram-series-4); - --pele-series-5: var(--hd-diagram-series-5); - --pele-series-6: var(--hd-diagram-series-6); - --pele-series-7: var(--hd-diagram-series-7); - --pele-series-8: var(--hd-diagram-series-8); + --pele-font: "Geist", var(--font-sans, ui-sans-serif, system-ui, sans-serif); + --pele-font-mono: "Geist Mono", var(--font-mono, ui-monospace, monospace); + --pele-bg: var(--background); + --pele-fg: var(--foreground); + --pele-muted: var(--muted-foreground); + --pele-line: color-mix(in srgb, var(--foreground) 62%, transparent); + --pele-surface: var(--card); + --pele-surface-alt: var(--secondary); + --pele-border: color-mix(in srgb, var(--foreground) 22%, transparent); + --pele-accent: var(--primary); + --pele-critical: var(--destructive); + --pele-error: var(--destructive); + --pele-radius: 8px; + --pele-title-weight: 600; + --pele-heading-weight: 600; + --pele-bold-weight: 600; + --pele-series-1: var(--hd-series-1); + --pele-series-2: var(--hd-series-2); + --pele-series-3: var(--hd-series-3); + --pele-series-4: var(--hd-series-4); + --pele-series-5: var(--hd-series-5); + --pele-series-6: var(--hd-series-6); + --pele-series-7: var(--hd-series-7); + --pele-series-8: var(--hd-series-8); + /* semantic state colors (house classes) */ + --hd-live: var(--primary); + --hd-wait: var(--warning); + --hd-ok: var(--success); + --hd-fail: var(--destructive); + --hd-idle: var(--muted-foreground); + --hd-live-bg: color-mix(in srgb, var(--hd-live) 14%, var(--background)); + --hd-wait-bg: color-mix(in srgb, var(--hd-wait) 14%, var(--background)); + --hd-ok-bg: color-mix(in srgb, var(--hd-ok) 14%, var(--background)); + --hd-fail-bg: color-mix(in srgb, var(--hd-fail) 14%, var(--background)); + --hd-idle-bg: var(--secondary); +} + +/* polish */ +.pele .pele-edge path { + stroke-width: 1.4px; + stroke-linecap: round; +} +.pele .pele-edge-label { + font-size: 0.92em; +} +.pele .pele-edge-label text { + fill: var(--pele-muted); +} +.pele .pele-cluster-label, +.pele .pele-title { + letter-spacing: 0.01em; +} +.pele .pele-node > rect, +.pele .pele-node > path, +.pele .pele-node > polygon { + stroke-width: 1.2px; +} + +/* house semantic classes */ +.pele .live > rect, +.pele .live > path, +.pele .live > polygon { + fill: var(--hd-live-bg) !important; + stroke: var(--hd-live) !important; +} +.pele .wait > rect, +.pele .wait > path, +.pele .wait > polygon { + fill: var(--hd-wait-bg) !important; + stroke: var(--hd-wait) !important; +} +.pele .ok > rect, +.pele .ok > path, +.pele .ok > polygon { + fill: var(--hd-ok-bg) !important; + stroke: var(--hd-ok) !important; +} +.pele .fail > rect, +.pele .fail > path, +.pele .fail > polygon { + fill: var(--hd-fail-bg) !important; + stroke: var(--hd-fail) !important; +} +.pele .idle > rect, +.pele .idle > path, +.pele .idle > polygon { + fill: var(--hd-idle-bg) !important; + stroke: var(--hd-idle) !important; + stroke-dasharray: 4 3; +} +.pele .live .pele-label { + fill: var(--hd-live); + font-weight: 600; +} +.pele .wait .pele-label { + fill: var(--hd-wait); + font-weight: 600; +} +.pele .ok .pele-label { + fill: var(--hd-ok); + font-weight: 600; +} +.pele .fail .pele-label { + fill: var(--hd-fail); + font-weight: 600; +} +.pele .idle .pele-label { + fill: var(--hd-idle); } From f4ff121aa86ff1634f07b6ecb0f0e5a1469c2751 Mon Sep 17 00:00:00 2001 From: Dimitri Kennedy Date: Sat, 24 Oct 2026 11:05:59 -0400 Subject: [PATCH 4/6] fix(web): keep house styles in expanded diagrams; style circle/ellipse state nodes --- .../src/components/chat/MermaidDiagram.tsx | 32 +++++++++++++++++++ apps/web/src/hd-diagram-theme.css | 24 ++++++++++---- 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/apps/web/src/components/chat/MermaidDiagram.tsx b/apps/web/src/components/chat/MermaidDiagram.tsx index 8d91c929e526..3ea817d30eca 100644 --- a/apps/web/src/components/chat/MermaidDiagram.tsx +++ b/apps/web/src/components/chat/MermaidDiagram.tsx @@ -204,6 +204,37 @@ let expandedImageUrl: string | null = null; * A blob URL, unlike a data URL, passes the desktop connect-src policy that media * save and copy actions fetch through. */ +// The house theme (hd-diagram-theme.css) styles shapes and labels from the page +// stylesheet, which a detached SVG image cannot see. Copy the resolved +// presentation values onto the clone so expanded/saved diagrams keep them. +const HOUSE_STYLED_SELECTOR = "rect, path, polygon, circle, ellipse, line, polyline, text, tspan"; +const HOUSE_STYLED_PROPERTIES = [ + "fill", + "stroke", + "stroke-width", + "stroke-dasharray", + "stroke-linecap", + "font-family", + "font-size", + "font-weight", + "letter-spacing", +] as const; + +function inlineHouseStyles(source: SVGSVGElement, clone: SVGSVGElement): void { + const from = source.querySelectorAll(HOUSE_STYLED_SELECTOR); + const to = clone.querySelectorAll(HOUSE_STYLED_SELECTOR); + if (from.length !== to.length) return; + from.forEach((node, index) => { + const target = to[index]; + if (!target) return; + const computed = getComputedStyle(node); + for (const name of HOUSE_STYLED_PROPERTIES) { + const value = computed.getPropertyValue(name).trim(); + if (value) target.style.setProperty(name, value); + } + }); +} + function diagramImageUrl(svg: SVGSVGElement): string { const element = svg.cloneNode(true) as SVGSVGElement; const computed = getComputedStyle(svg); @@ -211,6 +242,7 @@ function diagramImageUrl(svg: SVGSVGElement): string { const value = computed.getPropertyValue(name).trim(); if (value) element.style.setProperty(name, value); } + inlineHouseStyles(svg, element); element.style.maxWidth = "none"; element.style.backgroundColor = getComputedStyle(document.body).backgroundColor; if (expandedImageUrl) URL.revokeObjectURL(expandedImageUrl); diff --git a/apps/web/src/hd-diagram-theme.css b/apps/web/src/hd-diagram-theme.css index 91916f3b00b7..46e472230fff 100644 --- a/apps/web/src/hd-diagram-theme.css +++ b/apps/web/src/hd-diagram-theme.css @@ -82,38 +82,50 @@ } .pele .pele-node > rect, .pele .pele-node > path, -.pele .pele-node > polygon { +.pele .pele-node > polygon, +.pele .pele-node > circle, +.pele .pele-node > ellipse { stroke-width: 1.2px; } /* house semantic classes */ .pele .live > rect, .pele .live > path, -.pele .live > polygon { +.pele .live > polygon, +.pele .live > circle, +.pele .live > ellipse { fill: var(--hd-live-bg) !important; stroke: var(--hd-live) !important; } .pele .wait > rect, .pele .wait > path, -.pele .wait > polygon { +.pele .wait > polygon, +.pele .wait > circle, +.pele .wait > ellipse { fill: var(--hd-wait-bg) !important; stroke: var(--hd-wait) !important; } .pele .ok > rect, .pele .ok > path, -.pele .ok > polygon { +.pele .ok > polygon, +.pele .ok > circle, +.pele .ok > ellipse { fill: var(--hd-ok-bg) !important; stroke: var(--hd-ok) !important; } .pele .fail > rect, .pele .fail > path, -.pele .fail > polygon { +.pele .fail > polygon, +.pele .fail > circle, +.pele .fail > ellipse { fill: var(--hd-fail-bg) !important; stroke: var(--hd-fail) !important; } .pele .idle > rect, .pele .idle > path, -.pele .idle > polygon { +.pele .idle > polygon, +.pele .idle > circle, +.pele .idle > ellipse { fill: var(--hd-idle-bg) !important; stroke: var(--hd-idle) !important; stroke-dasharray: 4 3; From d8ea54726db73b5e414c6ca5008ba59737661675 Mon Sep 17 00:00:00 2001 From: Dimitri Kennedy Date: Sat, 24 Oct 2026 11:11:37 -0400 Subject: [PATCH 5/6] fix(web): readable state label tokens; keep house radius in expanded diagrams --- apps/web/src/components/chat/MermaidDiagram.tsx | 3 +++ apps/web/src/hd-diagram-theme.css | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/web/src/components/chat/MermaidDiagram.tsx b/apps/web/src/components/chat/MermaidDiagram.tsx index 3ea817d30eca..4fcf117b9ae4 100644 --- a/apps/web/src/components/chat/MermaidDiagram.tsx +++ b/apps/web/src/components/chat/MermaidDiagram.tsx @@ -243,6 +243,9 @@ function diagramImageUrl(svg: SVGSVGElement): string { if (value) element.style.setProperty(name, value); } inlineHouseStyles(svg, element); + // Shapes use rx: var(--_r), which resolves from --pele-radius set by the house theme. + const radius = computed.getPropertyValue("--pele-radius").trim(); + if (radius) element.style.setProperty("--pele-radius", radius); element.style.maxWidth = "none"; element.style.backgroundColor = getComputedStyle(document.body).backgroundColor; if (expandedImageUrl) URL.revokeObjectURL(expandedImageUrl); diff --git a/apps/web/src/hd-diagram-theme.css b/apps/web/src/hd-diagram-theme.css index 46e472230fff..3f50a4d4eaf3 100644 --- a/apps/web/src/hd-diagram-theme.css +++ b/apps/web/src/hd-diagram-theme.css @@ -135,15 +135,15 @@ font-weight: 600; } .pele .wait .pele-label { - fill: var(--hd-wait); + fill: var(--warning-foreground, var(--hd-wait)); font-weight: 600; } .pele .ok .pele-label { - fill: var(--hd-ok); + fill: var(--success-foreground, var(--hd-ok)); font-weight: 600; } .pele .fail .pele-label { - fill: var(--hd-fail); + fill: var(--destructive-foreground, var(--hd-fail)); font-weight: 600; } .pele .idle .pele-label { From 901f1d146e72be80c63460bbcabc03a9cf23837e Mon Sep 17 00:00:00 2001 From: Dimitri Kennedy Date: Mon, 26 Oct 2026 10:38:51 -0400 Subject: [PATCH 6/6] chore(web): drop internal source reference from diagram theme header --- apps/web/src/hd-diagram-theme.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/hd-diagram-theme.css b/apps/web/src/hd-diagram-theme.css index 3f50a4d4eaf3..ba7e48705394 100644 --- a/apps/web/src/hd-diagram-theme.css +++ b/apps/web/src/hd-diagram-theme.css @@ -1,5 +1,5 @@ /* Hack Dance house theme for Pele (Mermaid) diagrams, mapped to T3 tokens. - Source of truth: roodboi/machine files/pele/hackdance.css (Lucy's style kit). + Keep in sync with the Hack Dance house theme source. Neutrals, accent, error and the live/wait/ok/fail/idle state colors follow the active T3 theme tokens (light and dark, custom themes too); the series palette and font stacks come from the house theme. Pele reads colors through --pele-*,