Code Syntax Highlighting

When an agent includes a fenced code block with an explicit language tag in its markdown response, the session view tokenises the content and paints keywords, strings, numbers, comments and other syntax classes with theme-aware colours. Plain prose in unlabelled blocks is left untouched.

How It Works

The agent writes a standard fenced code block with a language name on the opening fence:

```typescript
interface User {
  readonly id: string
  readonly name: string
}

function greet(user: User): string {
  return `Hello, ${user.name}!`
}
```

The markdown renderer pipes the block through rehype-highlight, which uses highlight.js to classify each token and wrap it in a <span class="hljs-*">. Those spans are then styled by a set of .acp-markdown .hljs-* rules that resolve their colour from theme-aware CSS custom properties, so the palette tracks the active light or dark theme automatically.

No server round-trip is involved — tokenisation runs in the browser as the message renders.

Auto-Detection Is Disabled

Highlight.js ships an auto-detector that will guess a language when no tag is given on the fence. In practice, for short snippets of prose it often misidentifies common English words like as, is, or with as language keywords, producing false-positive colouring on plain text.

To avoid this, auto-detection is turned off (detect: false). An unlabelled fence:

```
This is plain text. No syntax highlighting is applied.
```

renders as plain monospace text with no token spans. If you want highlighting, tag the fence with a language name.

Note on paste auto-fencing. When you paste multi-line content into the prompt input, a separate detector — the VS Code language-detection model, run server-side — does attempt to identify the language and wrap the paste in a tagged fence. That happens before the agent ever sees the text; by the time the message is rendered, the fence tag is already present and the rendering path described here runs with detect: false as usual. See Pasting Code.

Supported Languages

Highlight.js recognises over 190 languages out of the box. Common ones you will encounter from agent output include:

  • Web: html, css, scss, javascript, typescript, json, yaml, xml
  • Systems: c, cpp, rust, go, zig
  • Scripting: bash, shell, python, ruby, perl, lua, php
  • JVM/CLR: java, kotlin, scala, groovy, csharp, fsharp
  • Functional: haskell, elixir, erlang, ocaml, clojure
  • Data/query: sql, graphql, protobuf
  • Diff/patch: diff

Use whichever language name or alias you normally would on GitHub fenced blocks — ts and typescript, py and python, sh and bash all work.

Unknown language names are silently ignored (via ignoreMissing: true). The block still renders — just without token colouring — so you will never see a "language not supported" error in place of your code.

Token Palette

Each syntax class resolves to a CSS custom property that is defined twice — once on :root for the light theme, once on [data-theme="dark"] for the dark theme:

Syntax classCSS variableTypical tokens
Keyword--hljs-keywordif, return, function, class, const
String--hljs-string"...", '...', template strings
Number / literal--hljs-number, --hljs-literal42, 3.14, true, false, null
Comment--hljs-comment// ..., # ..., /* ... */
Function / title--hljs-function, --hljs-titleFunction names at definition and call sites
Built-in--hljs-built-inArray, console, Object, print
Variable--hljs-variable$var, @var, template variables
Attribute / property--hljs-attr, --hljs-attributeJSON keys, HTML attributes, YAML keys
Parameter--hljs-paramsFunction parameter names
Type--hljs-typeType annotations, class titles
Tag--hljs-tag<div>, <span>, HTML element names
Operator--hljs-operator=, +, ->, =>, punctuation
Meta--hljs-metaDecorators, directives, shebangs
Symbol--hljs-symbolRuby symbols, atoms, bullets
Regexp--hljs-regexp/foo/g literals
Link--hljs-linkURLs in markdown/docstrings
Section heading--hljs-sectionMarkdown headings, INI sections
Deletion / addition--hljs-deletion, --hljs-additionDiff - / + lines

Keywords and function names are rendered bold; comments are italic; deletions and additions in diff blocks are coloured red and green respectively to match the diff viewer's conventions.

When the user toggles theme (via settings or OS preference), the token spans keep their class names but re-resolve the --hljs-* variables from the new theme's root, so the palette switches instantly with no re-render.

Interaction With Other Block Types

Copy Button

Every highlighted fenced block still shows the standard copy button in its top-right corner on hover. The text copied to the clipboard is the original source — no hljs-* spans or class names end up in the copied content.

Inline Code

Inline code spans (single backticks inside a paragraph — `like this`) are not tokenised. They continue to render in the app's inline-code style: small monospace text on a subtle background tint. Syntax highlighting only applies to fenced blocks.

Diagram Blocks

Fenced blocks tagged mermaid, likec4, or c4 are not affected by syntax highlighting. They continue to route to the Mermaid and LikeC4 diagram renderers and display as interactive diagrams. When you toggle the Source view on a diagram block, the raw DSL currently renders as plain monospace text — syntax highlighting for Mermaid and LikeC4 grammars is not yet provided by highlight.js, so these sources are not tokenised.

Implementation Notes

The highlight pipeline runs as a rehype plugin on the same react-markdown tree that produces the rest of the message content. The relevant configuration lives in apps/braide/src/components/sessions/cards/copy-markdown.tsx:

const rehypePlugins: PluggableList = [
  [rehypeHighlight, { detect: false, ignoreMissing: true }],
]

The CSS that maps hljs-* classes to the theme palette lives in apps/braide/src/app/globals.css, immediately after the code block copy-button rules and the mermaid theme tokens, so all markdown rendering styles are co-located.