Skip to content

ui: syntax-highlight fenced code blocks, every surface - #85

Merged
samkeen merged 4 commits into
mainfrom
claude/syntax-highlighting-code-blocks-rav87b
Jul 25, 2026
Merged

ui: syntax-highlight fenced code blocks, every surface#85
samkeen merged 4 commits into
mainfrom
claude/syntax-highlighting-code-blocks-rav87b

Conversation

@samkeen

@samkeen samkeen commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Language-specific colours for ```lang fences in the reading view and the
editor, from one engine and one palette.

The dependency is CodeMirror's own registry, @codemirror/language-data —
the pragmatic pick because the editor is already CodeMirror. Handing its
resolver to markdown({ codeLanguages }) parses a fence's body with that
language's grammar; running the same grammars over the reading view's

 keeps read and edit identical, which a standalone
highlighter (highlight.js, Shiki) could not. Grammars are lazily
imported one chunk per language, so a vault of plain prose loads none of
them and the entry bundle is unchanged.

- ui/src/highlight.ts: the shared resolver (fuzzy alias matching, minus a
  guard so ```text doesn't fuzzy-match TeX), a tagHighlighter mapping
  Lezer tags to a `tok-*` vocabulary — ours rather than the stock
  classHighlighter, which covers neither tagName nor attributeName — and
  the reading view's post-render DOM pass.
- style.css: the `tok-*` palette in light and dark, scoped to code on
  both sides so live preview keeps owning the Markdown's own tokens.
- escape.ts: escapeHtml moved out of render.ts (re-exported there) so
  highlight.ts, and its node test, don't pull in the view layer.

Nothing here changes a byte of a note: the reading view's pass re-escapes
every text run on the way back in, and the editor's colours are
decorations over an untouched buffer.

Co-Authored-By: Claude Opus 5 
Claude-Session: https://claude.ai/code/session_01EuFQeYyBA4buEeBnRQ9rMx



## Summary by CodeRabbit

* **New Features**
  * Added syntax highlighting for fenced code blocks in reading view and live preview.
  * Added support for language aliases and consistent highlighting across reading and editing modes.
  * Added light and dark theme colors for highlighted code tokens.
  * Added safeguards for unknown languages and oversized code blocks.

* **Tests**
  * Added coverage for language detection, highlighting, HTML safety, aliases, and size limits.

* **Documentation**
  * Clarified frontend syntax-highlighting behavior in workspace documentation.

Language-specific colours for ```lang fences in the reading view and the
editor, from one engine and one palette.

The dependency is CodeMirror's own registry, @codemirror/language-data —
the pragmatic pick because the editor is already CodeMirror. Handing its
resolver to markdown({ codeLanguages }) parses a fence's body with that
language's grammar; running the same grammars over the reading view's
<pre><code> keeps read and edit identical, which a standalone
highlighter (highlight.js, Shiki) could not. Grammars are lazily
imported one chunk per language, so a vault of plain prose loads none of
them and the entry bundle is unchanged.

- ui/src/highlight.ts: the shared resolver (fuzzy alias matching, minus a
  guard so ```text doesn't fuzzy-match TeX), a tagHighlighter mapping
  Lezer tags to a `tok-*` vocabulary — ours rather than the stock
  classHighlighter, which covers neither tagName nor attributeName — and
  the reading view's post-render DOM pass.
- style.css: the `tok-*` palette in light and dark, scoped to code on
  both sides so live preview keeps owning the Markdown's own tokens.
- escape.ts: escapeHtml moved out of render.ts (re-exported there) so
  highlight.ts, and its node test, don't pull in the view layer.

Nothing here changes a byte of a note: the reading view's pass re-escapes
every text run on the way back in, and the editor's colours are
decorations over an untouched buffer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EuFQeYyBA4buEeBnRQ9rMx
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The UI adds CodeMirror-based fenced-code highlighting with shared language resolution and token styling across reading and editing surfaces. It introduces HTML escaping, asynchronous post-render painting, theme variables, grammar dependencies, and focused highlighting tests.

Changes

Fenced-code highlighting

Layer / File(s) Summary
Highlighting engine and HTML safety
ui/src/highlight.ts, ui/src/escape.ts, ui/src/render.ts, ui/package.json
Adds cached CodeMirror grammar loading, language resolution, escaped tok-* HTML generation, DOM painting, and a shared HTML escaping helper.
Reading and editor integration
ui/src/main.ts
Applies highlighting after reading-view renders and configures live preview and markdown editing with the shared resolver and palette.
Token themes and validation
ui/style.css, ui/src/highlight.test.ts, ui/package.json
Adds themed token styles for reading and editor fences and tests language parsing, aliases, escaping, round-tripping, and size limits.
Highlighting documentation
CLAUDE.md
Documents the shared grammar resolver and token palette across display and editing surfaces.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant NoteRender
  participant HighlightPass
  participant CodeMirror
  participant CodeBlock
  NoteRender->>HighlightPass: schedule fence painting
  HighlightPass->>CodeMirror: resolve language and load grammar
  CodeMirror-->>HighlightPass: return highlighted escaped HTML
  HighlightPass->>CodeBlock: replace connected block contents
Loading

Possibly related PRs

  • AlteredCraft/B2#82: Introduces the frontmatter mini-editor flow that this change explicitly integrates with.

Suggested reviewers: claude

Poem

I hop through fences, bright with glow,
Where tiny tokens bloom and show.
In dark or light, the colors sing,
Safe escaped strings take to wing.
Read or edit, the hues agree—
A carrot-code symphony! 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding fenced-code syntax highlighting across reading and editing surfaces.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/syntax-highlighting-code-blocks-rav87b

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ui/src/main.ts`:
- Around line 1604-1614: Update livePreviewConf in ui/src/main.ts at lines
1604-1614 to register b2Highlighter when state.sourceOpen, retaining
defaultHighlightStyle only as the fallback if needed. Extend the token styling
in ui/style.css at lines 1019-1072 to apply the resulting tok-* classes in
source-editor mode as well as reading view and .lp-fence live-preview blocks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fddb86f9-06fb-4181-bf04-c39e5c4cc208

📥 Commits

Reviewing files that changed from the base of the PR and between 4b9391e and 8c71727.

⛔ Files ignored due to path filters (1)
  • ui/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • CLAUDE.md
  • ui/package.json
  • ui/src/escape.ts
  • ui/src/highlight.test.ts
  • ui/src/highlight.ts
  • ui/src/main.ts
  • ui/src/render.ts
  • ui/style.css

Comment thread ui/src/main.ts Outdated
claude added 3 commits July 25, 2026 16:34
Conflict: ui/package.json's `test` script. main replaced the explicit
per-file list with node's own runner globbing src/*.test.ts — took
main's, which is strictly better here (highlight.test.ts is picked up
without registration, and can never be silently skipped).
Source mode (`</>`) was still on CodeMirror's stock defaultHighlightStyle,
so it missed the tok-* palette the reading view and live preview share —
and, now that codeLanguages nests real grammars, its fence bodies came out
in a light-only palette that reads as ink-on-ink against the dark theme.

Register b2Highlighter there instead, and drop defaultHighlightStyle: with
another highlighter registered its {fallback: true} would never apply, so
keeping it would have been dead weight.

What differs between the surfaces is now purely CSS reach, and the scope
names say so: source mode takes the palette document-wide via a new
`src-body` content class (the symmetric twin of live preview's `lp-body`),
while live preview stays bounded to `.lp-fence` — the Markdown's own markup
there is already spoken for by the `.lp-*` decorations.

That makes Markdown's marks a source-mode surface rather than a code-only
one, so `processingInstruction` moves from tok-keyword to tok-punct: it is
what lezer-markdown tags `#`, `**` and the fences with, and keyword-purple
markup drowns the prose. highlight.test.ts now pins the class pairs the
CSS rule order depends on, and parses through the editor's own configured
parser so the codeLanguages wiring is covered end to end.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EuFQeYyBA4buEeBnRQ9rMx
An empty 81-byte lockfile ("name": "ui", no packages) — the artifact of
an `npm install` run from the wrong directory, committed by accident in
d8a82dc. The real one is ui/package-lock.json; nothing reads this.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EuFQeYyBA4buEeBnRQ9rMx
@samkeen samkeen changed the title ui: syntax-highlight fenced code blocks, both surfaces ui: syntax-highlight fenced code blocks, every surface Jul 25, 2026
@samkeen
samkeen merged commit facd467 into main Jul 25, 2026
1 check passed
@samkeen
samkeen deleted the claude/syntax-highlighting-code-blocks-rav87b branch July 25, 2026 17:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants