From 5dcf66c28417ef79ebcc2ef70c1a6499b9ba0a3f Mon Sep 17 00:00:00 2001 From: Patrick Buckley Date: Wed, 8 Jul 2026 01:34:20 -0700 Subject: [PATCH] fix(webui): share renderer-output CSS so the console + coordinator highlight code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit highlight.js, KaTeX and Mermaid all run on every surface via the shared renderer (renderer.js), but their theme/wrapper CSS lived only in ui/static/style.css. The console and coordinator load /static/style.css from console/static/ — a different file on a different server — so hljs token spans fell back to --fg (flat monospace for several releases), and the KaTeX/Mermaid wrappers lacked their overflow containers, letting wide equations/diagrams overflow the pane. Move the hljs theme, .katex-display/.katex-error and the .mermaid-* wrappers into shared_static/chat.css, which every surface loads via /shared/chat.css. Restate the mermaid width-clamp for the preview pane (.preview-markdown) too, since its content isn't a .msg.assistant message. Drop the redundant background on .msg.assistant pre code.hljs so the
carries the code surface on every surface — otherwise the console/coordinator
(where the pre is --panel, not --code-bg) showed a darker box inside a lighter
padding band.
---
 turnstone/shared_static/chat.css    | 165 ++++++++++++++++++++++++++++
 turnstone/shared_static/preview.css |  19 +++-
 turnstone/ui/static/style.css       | 149 +------------------------
 3 files changed, 187 insertions(+), 146 deletions(-)

diff --git a/turnstone/shared_static/chat.css b/turnstone/shared_static/chat.css
index dfb562d9..0a49a76e 100644
--- a/turnstone/shared_static/chat.css
+++ b/turnstone/shared_static/chat.css
@@ -1200,6 +1200,171 @@
   background: var(--panel-2);
 }
 
+/* Syntax highlighting — highlight.js theme (instrument panel).
+   Lives here (shared) rather than in a per-surface style.css so the
+   server UI, the console, AND the coordinator all colour hljs token
+   spans identically.  highlight.js emits the 
+   markup on every surface (postRenderHljs in renderer.js); without
+   these rules the spans render in the default --fg and code blocks
+   look unhighlighted.  Colours resolve against the palette variables
+   in base.css (also shared). */
+.msg.assistant pre code.hljs {
+  /* No background here on purpose — the 
 carries the code-surface colour
+     on every surface (--code-bg on the server UI via .msg.assistant pre, --panel
+     on the console/coordinator via the shared .msg-body pre).  Painting it on
+     the inner  too made a darker box inside a lighter pad band wherever
+     the two differ (the console/coordinator seam).  Let the code stay
+     transparent and show the pre. */
+  color: var(--fg);
+  padding: 0;
+}
+.hljs-keyword,
+.hljs-selector-tag,
+.hljs-built_in,
+.hljs-type {
+  color: var(--magenta);
+}
+.hljs-string,
+.hljs-attr,
+.hljs-template-tag,
+.hljs-template-variable {
+  color: var(--green);
+}
+.hljs-number,
+.hljs-literal,
+.hljs-variable,
+.hljs-symbol,
+.hljs-bullet {
+  color: var(--cyan);
+}
+.hljs-title,
+.hljs-title.class_,
+.hljs-title.function_ {
+  color: var(--accent);
+}
+.hljs-comment,
+.hljs-quote {
+  color: var(--fg-dim);
+  font-style: italic;
+}
+.hljs-meta {
+  color: var(--fg-dim);
+}
+.hljs-doctag {
+  color: var(--cyan);
+  font-weight: 500;
+}
+.hljs-char.escape_ {
+  color: var(--cyan);
+}
+.hljs-emphasis {
+  font-style: italic;
+}
+.hljs-strong {
+  font-weight: bold;
+  color: var(--fg-bright);
+}
+.hljs-attribute {
+  color: var(--yellow);
+}
+.hljs-regexp,
+.hljs-link {
+  color: var(--cyan);
+}
+.hljs-selector-id,
+.hljs-selector-class {
+  color: var(--yellow);
+}
+.hljs-section {
+  color: var(--accent);
+  font-weight: bold;
+}
+.hljs-tag {
+  color: var(--fg-dim);
+}
+.hljs-name {
+  color: var(--magenta);
+}
+.hljs-params {
+  color: var(--fg);
+}
+.hljs-addition {
+  color: var(--green);
+  background: rgba(52, 211, 153, 0.08);
+  display: inline-block;
+  width: 100%;
+}
+.hljs-deletion {
+  color: var(--red);
+  background: rgba(248, 113, 113, 0.08);
+  display: inline-block;
+  width: 100%;
+}
+.msg.assistant pre.code-terminal {
+  border-left: 2px solid var(--green);
+}
+[data-theme="light"] .hljs-addition {
+  background: rgba(4, 120, 87, 0.06);
+}
+[data-theme="light"] .hljs-deletion {
+  background: rgba(220, 38, 38, 0.06);
+}
+
+/* KaTeX math wrappers — shared for the same reason as the hljs theme above:
+   the console + coordinator load /static/style.css from their own directory
+   (and do NOT load preview.css), so the app-level frame for rendered math has
+   to live here.  The vendored katex.min.css draws the glyphs; this gives block
+   equations a horizontal scroll container (wide equations overflow without it)
+   and colours parse errors. */
+.msg.assistant .katex-display {
+  margin: 8px 0;
+  padding: 8px 0;
+  overflow-x: auto;
+  overflow-y: hidden;
+}
+.msg.assistant .katex-error {
+  color: var(--red) !important;
+  font-size: 12px;
+}
+
+/* Mermaid diagram wrappers — shared for the same reason.  mermaid.js draws a
+   self-contained SVG, so diagrams appear even with these missing; but without
+   them the SVG isn't centred or width-clamped (a wide diagram overflows the
+   pane) and the loading/error states render unstyled. */
+.msg.assistant .mermaid-container {
+  margin: 8px 0;
+  text-align: center;
+  border-radius: var(--radius);
+  overflow-x: auto;
+  min-height: 40px;
+}
+.msg.assistant .mermaid-container svg {
+  max-width: 100%;
+  height: auto;
+}
+.msg.assistant .mermaid-loading {
+  padding: 16px;
+  color: var(--fg-dim);
+  font-size: 12px;
+  background: var(--bg-surface);
+  border: 1px solid var(--border);
+}
+.msg.assistant .mermaid-error {
+  text-align: left;
+}
+.msg.assistant .mermaid-error-msg {
+  padding: 8px 12px;
+  color: var(--red);
+  font-size: 12px;
+  font-weight: 600;
+  border-bottom: 1px solid var(--border);
+}
+@media (prefers-reduced-motion: reduce) {
+  .msg.assistant .mermaid-container svg * {
+    animation: none !important;
+  }
+}
+
 .msg-body a {
   color: var(--accent);
   text-decoration: underline;
diff --git a/turnstone/shared_static/preview.css b/turnstone/shared_static/preview.css
index 8b557ada..d3c24555 100644
--- a/turnstone/shared_static/preview.css
+++ b/turnstone/shared_static/preview.css
@@ -145,9 +145,10 @@
   font-size: 13px;
   color: var(--ink-1);
 }
-/* Code-block chrome + katex display: the per-ui style.css rules are scoped to
-   .msg.assistant and don't reach the pane, so restate them on DS tokens.  The
-   hljs TOKEN colors are global and already apply. */
+/* Code-block chrome + katex display + mermaid: the .msg.assistant-scoped rules
+   in shared chat.css don't reach the pane (content is .preview-markdown, not a
+   .msg.assistant message), so restate them on DS tokens.  The hljs TOKEN colors
+   are global and already apply. */
 .preview-markdown code {
   padding: 1px 4px;
   font-family: var(--font-mono);
@@ -173,6 +174,18 @@
   margin: 8px 0;
   overflow-x: auto;
 }
+/* Mermaid: mermaid.js draws a self-contained SVG, but without a width clamp a
+   wide diagram overflows the pane (the .msg.assistant mermaid rules in chat.css
+   don't reach .preview-markdown). */
+.preview-markdown .mermaid-container {
+  margin: 8px 0;
+  text-align: center;
+  overflow-x: auto;
+}
+.preview-markdown .mermaid-container svg {
+  max-width: 100%;
+  height: auto;
+}
 .preview-markdown img {
   max-width: 100%;
 }
diff --git a/turnstone/ui/static/style.css b/turnstone/ui/static/style.css
index a6c80b74..891ed0e9 100644
--- a/turnstone/ui/static/style.css
+++ b/turnstone/ui/static/style.css
@@ -340,149 +340,12 @@ body {
 .msg.assistant blockquote blockquote blockquote {
   border-left-color: var(--border);
 }
-.msg.assistant .katex-display {
-  margin: 8px 0;
-  padding: 8px 0;
-  overflow-x: auto;
-  overflow-y: hidden;
-}
-.msg.assistant .katex-error {
-  color: var(--red) !important;
-  font-size: 12px;
-}
-
-/* Syntax highlighting — highlight.js theme (instrument panel) */
-.msg.assistant pre code.hljs {
-  background: var(--code-bg);
-  color: var(--fg);
-  padding: 0;
-}
-.hljs-keyword,
-.hljs-selector-tag,
-.hljs-built_in,
-.hljs-type {
-  color: var(--magenta);
-}
-.hljs-string,
-.hljs-attr,
-.hljs-template-tag,
-.hljs-template-variable {
-  color: var(--green);
-}
-.hljs-number,
-.hljs-literal,
-.hljs-variable,
-.hljs-symbol,
-.hljs-bullet {
-  color: var(--cyan);
-}
-.hljs-title,
-.hljs-title.class_,
-.hljs-title.function_ {
-  color: var(--accent);
-}
-.hljs-comment,
-.hljs-quote {
-  color: var(--fg-dim);
-  font-style: italic;
-}
-.hljs-meta {
-  color: var(--fg-dim);
-}
-.hljs-doctag {
-  color: var(--cyan);
-  font-weight: 500;
-}
-.hljs-char.escape_ {
-  color: var(--cyan);
-}
-.hljs-emphasis {
-  font-style: italic;
-}
-.hljs-strong {
-  font-weight: bold;
-  color: var(--fg-bright);
-}
-.hljs-attribute {
-  color: var(--yellow);
-}
-.hljs-regexp,
-.hljs-link {
-  color: var(--cyan);
-}
-.hljs-selector-id,
-.hljs-selector-class {
-  color: var(--yellow);
-}
-.hljs-section {
-  color: var(--accent);
-  font-weight: bold;
-}
-.hljs-tag {
-  color: var(--fg-dim);
-}
-.hljs-name {
-  color: var(--magenta);
-}
-.hljs-params {
-  color: var(--fg);
-}
-.hljs-addition {
-  color: var(--green);
-  background: rgba(52, 211, 153, 0.08);
-  display: inline-block;
-  width: 100%;
-}
-.hljs-deletion {
-  color: var(--red);
-  background: rgba(248, 113, 113, 0.08);
-  display: inline-block;
-  width: 100%;
-}
-.msg.assistant pre.code-terminal {
-  border-left: 2px solid var(--green);
-}
-[data-theme="light"] .hljs-addition {
-  background: rgba(4, 120, 87, 0.06);
-}
-[data-theme="light"] .hljs-deletion {
-  background: rgba(220, 38, 38, 0.06);
-}
-
-/* Mermaid diagrams */
-.msg.assistant .mermaid-container {
-  margin: 8px 0;
-  text-align: center;
-  border-radius: var(--radius);
-  overflow-x: auto;
-  min-height: 40px;
-}
-.msg.assistant .mermaid-container svg {
-  max-width: 100%;
-  height: auto;
-}
-.msg.assistant .mermaid-loading {
-  padding: 16px;
-  color: var(--fg-dim);
-  font-size: 12px;
-  background: var(--bg-surface);
-  border: 1px solid var(--border);
-}
-.msg.assistant .mermaid-error {
-  text-align: left;
-}
-.msg.assistant .mermaid-error-msg {
-  padding: 8px 12px;
-  color: var(--red);
-  font-size: 12px;
-  font-weight: 600;
-  border-bottom: 1px solid var(--border);
-}
-@media (prefers-reduced-motion: reduce) {
-  .msg.assistant .mermaid-container svg * {
-    animation: none !important;
-  }
-}
+/* Rendered-markdown decoration for code (highlight.js), math (KaTeX
+   .katex-display/.katex-error) and diagrams (Mermaid .mermaid-*) moved to
+   shared_static/chat.css.  These style the output of the SHARED renderer.js,
+   which runs on the server UI, the console AND the coordinator — but those
+   last two load /static/style.css from their own directory, not this file, so
+   the rules have to be shared.  Do not re-add them here. */
 
 /* Safe HTML elements */
 .msg.assistant details {