Skip to content

Add Mermaid-aware source syntax highlighting - #105

Merged
donovan-yohan merged 1 commit into
mainfrom
codex/issue-100-mermaid-syntax
Aug 11, 2026
Merged

donovan-yohan merged 1 commit into
mainfrom
codex/issue-100-mermaid-syntax

Conversation

@donovan-yohan

@donovan-yohan donovan-yohan commented Aug 11, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • replace generic Markdown mode with a focused, fail-soft CodeMirror 6 Mermaid tokenizer
  • cover all 30 current Mermaid families, compact connectors, directives/frontmatter, comments, strings, numbers, punctuation, keywords, and explicit invalid tokens
  • add accessible light/dark/system/forced-colors token styling without changing canonical Yjs source or parser behavior
  • preserve selection, caret, IME, undo/history, collaboration, copy/export, and incremental editor behavior
  • add catalog-derived token-range, large-source performance, and production source-flyout/Yjs convergence evidence

Review

One broad adversarial review found compact-connector, catalog-keyword, and browser-evidence gaps. They were batched into one fix pass. Independent narrow reruns then corrected two E2E contract errors (CodeMirror auto-indent and MCP catalog-vs-read projection). Final review approved exact head with no remaining P0/P1/P2.

Exact-head evidence

Base: 47ef53374af744edd0a2d1527481097e36494516
Head: bb097fcda4b595c493c0f3a54f543f4155d19a82
Tree: 0fe8d95bf82c16b35bc71539d2cb0cc0ef135490

  • shared build: pass
  • lint: pass
  • typecheck: pass
  • tests: shared 7, server 110, web 536
  • production build: pass
  • workspace UX production gate: two consecutive passes on exact head
  • protected collaboration: pass for unchanged runtime integration
  • diff check and worktree clean

Closes #100

Summary by CodeRabbit

  • New Features

    • Added syntax highlighting for Mermaid source code in the workspace editor.
    • Added support for Mermaid headers, keywords, comments, labels, connectors, numbers, strings, and invalid characters.
    • Added light, dark, and forced-colors accessibility themes.
    • Preserved document selection and synchronized edits during incremental editing.
  • Bug Fixes

    • Improved handling of partial, invalid, and large Mermaid inputs without disrupting editing.

@vercel

vercel Bot commented Aug 11, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
arielcharts Ready Ready Preview Aug 11, 2026 8:18pm

@coderabbitai

coderabbitai Bot commented Aug 11, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds a bounded Mermaid tokenizer and CodeMirror language extension. The session editor uses Mermaid highlighting instead of Markdown highlighting. Theme variables support light, dark, and forced-colors modes. Tests cover parsing, accessibility, editing, and Yjs synchronization.

Changes

Mermaid source highlighting

Layer / File(s) Summary
Mermaid tokenizer and language extension
apps/web/src/lib/mermaid-language.ts, apps/web/src/lib/mermaid-language.test.ts, apps/web/package.json, docs/context-map.md
Adds stateful Mermaid tokenization, family-specific keywords, syntax styles, fail-soft parsing, stress fixtures, and focused tests.
Editor wiring and accessible theme tokens
apps/web/src/components/session-workspace.tsx, apps/web/src/app/globals.css, apps/web/src/app/globals.test.ts
Replaces Markdown highlighting with the shared Mermaid extension and adds light, dark, and forced-colors syntax variables with tests.
Workspace behavior validation
e2e-workspace-ux-validate.ts
Validates syntax colors, incremental editing, cursor preservation, Yjs synchronization, and workspace test registration.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SessionWorkspace
  participant mermaidSourceLanguage
  participant CodeMirror
  participant YjsSession
  User->>SessionWorkspace: Open Mermaid source editor
  SessionWorkspace->>mermaidSourceLanguage: Configure language extension
  mermaidSourceLanguage->>CodeMirror: Tokenize and highlight source
  User->>CodeMirror: Edit source incrementally
  CodeMirror->>YjsSession: Synchronize source update
  YjsSession-->>CodeMirror: Preserve collaborative document state
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the main change: Mermaid-aware source syntax highlighting.
Linked Issues check ✅ Passed The changes replace Markdown mode with focused Mermaid highlighting, add accessible themes, preserve editor and Yjs behavior, and add broad grammar, invalid-input, performance, and UX coverage.
Out of Scope Changes check ✅ Passed All listed changes support Mermaid highlighting, accessibility, editor integration, validation, dependencies, or the documented editor-language architecture.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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 codex/issue-100-mermaid-syntax

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 `@e2e-workspace-ux-validate.ts`:
- Around line 3543-3544: Update the keyboard input in the Mermaid editor test to
include the two-space indentation after the newline, either by typing the
indented text directly or by pressing Enter separately before typing B-->C, so
it matches the expected value.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cbb7ebf4-6cd6-4513-9e2a-e69f25813f32

📥 Commits

Reviewing files that changed from the base of the PR and between 47ef533 and bb097fc.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (8)
  • apps/web/package.json
  • apps/web/src/app/globals.css
  • apps/web/src/app/globals.test.ts
  • apps/web/src/components/session-workspace.tsx
  • apps/web/src/lib/mermaid-language.test.ts
  • apps/web/src/lib/mermaid-language.ts
  • docs/context-map.md
  • e2e-workspace-ux-validate.ts

Comment on lines +3543 to +3544
await page.keyboard.type('\nB-->C');
const expected = `${MERMAID_HIGHLIGHT_BROWSER_FIXTURE}\n B-->C`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- target lines ---'
sed -n '3515,3565p' e2e-workspace-ux-validate.ts
printf '%s\n' '--- related fixture and editor setup ---'
rg -n -C 8 'MERMAID_HIGHLIGHT_BROWSER_FIXTURE|page\.keyboard\.type|B-->C|contenteditable|Monaco|CodeMirror|textarea' e2e-workspace-ux-validate.ts
printf '%s\n' '--- candidate editor handling ---'
rg -n -S -g '*.{ts,tsx,js,jsx}' 'insertText|beforeinput|keydown|Enter|indent|auto.?indent|mermaid' . | head -n 240

Repository: donovan-yohan/arielcharts

Length of output: 46354


🏁 Script executed:

#!/bin/bash
set -eu
sed -n '3515,3565p' e2e-workspace-ux-validate.ts
rg -n -C 8 'MERMAID_HIGHLIGHT_BROWSER_FIXTURE|page\.keyboard\.type|B-->C' e2e-workspace-ux-validate.ts

Repository: donovan-yohan/arielcharts

Length of output: 16155


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- Playwright declarations and version ---'
rg -n -S 'playwright|`@playwright/test`' package.json package-lock.json pnpm-lock.yaml yarn.lock bun.lockb 2>/dev/null || true
printf '%s\n' '--- editor implementation and newline-related handlers ---'
rg -n -S -g '*.{ts,tsx,js,jsx}' 'y-codemirror|CodeMirror|EditorView|indentOnInput|insertNewline|newline|indent' apps packages e2e | head -n 260
printf '%s\n' '--- nearby input helpers ---'
rg -n -C 12 'async function (replaceSource|waitForSource|ensureSourceFlyoutOpen)' e2e-workspace-ux-validate.ts

Repository: donovan-yohan/arielcharts

Length of output: 50381


🌐 Web query:

Playwright Keyboard.type newline \n behavior documentation source

💡 Result:

In Playwright, the keyboard.type() method (and related locator-based typing methods) interprets the newline character \n as an Enter key press [1][2][3]. When you provide a string containing \n to keyboard.type() or pressSequentially(), Playwright treats the newline character as a command to generate an Enter keystroke [1][3]. This behavior is designed to simulate how a real keyboard interacts with elements like textareas or input fields [1]. Key Technical Details: - Behavior: The \n character triggers a keydown, keypress, and keyup sequence for the Enter key [4][1]. - Alternatives: - If your goal is to set the value of an element without simulating keystrokes, use locator.fill(), which does not trigger Enter key events and is generally recommended for performance and reliability [4][2][5]. - If you need to type the literal characters \n without triggering an Enter press, you may need to escape the character (e.g., using \\n) or utilize keyboard.insertText() to inject the text directly into the focused element without generating individual key events [4][3]. Note that keyboard.type() is intended for situations requiring special keyboard handling; for standard text input, locator.fill() remains the official, preferred approach [4][5].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- session workspace editor configuration ---'
sed -n '1,35p' apps/web/src/components/session-workspace.tsx
sed -n '1300,1415p' apps/web/src/components/session-workspace.tsx
printf '%s\n' '--- source replacement helper ---'
sed -n '55,115p' e2e/support/workspace.ts
printf '%s\n' '--- all direct editor key handling in the workspace component ---'
rg -n -C 6 'keymap|indent|Enter|insertText|yCollab|EditorView\.updateListener|new EditorView|basicSetup|defaultKeymap' apps/web/src/components/session-workspace.tsx

Repository: donovan-yohan/arielcharts

Length of output: 10615


🌐 Web query:

Playwright 1.59.1 keyboard.type newline source keyboardKeyForString Enter

💡 Result:

In Playwright, keyboard.type is intended for typing characters rather than triggering special key actions like a newline or Enter [1][2]. Including a newline character (\n) in a string passed to keyboard.type does not reliably trigger an Enter key press and can cause unexpected behavior; the Playwright team explicitly recommends against using keyboard.type for standard text entry in favor of locator.fill [3][1][2]. If you need to press the Enter key, you should use the dedicated locator.press or keyboard.press methods [1][4][2]. There is no internal method named keyboardKeyForString; the logic for handling keys is managed internally by Playwright's keyboard input handler, which maps strings to key codes [5]. Recommended patterns: 1. For submitting a form or pressing Enter: Use locator.press('Enter') [4][2]. await page.locator('#your-input').press('Enter'); 2. For typing characters one-by-one (if special keyboard handling is required): Use locator.pressSequentially [4][2]. await page.locator('#your-input').pressSequentially('your text'); await page.locator('#your-input').press('Enter'); 3. For general text input: Use locator.fill, which is the most stable and recommended approach [3][1][2]. await page.locator('#your-input').fill('your text'); The keyboard.type method is considered a legacy or low-level API and should generally be avoided for modern Playwright automation [1][6][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- Playwright 1.59.1 keyboard implementation ---'
curl -fsSL https://raw.githubusercontent.com/microsoft/playwright/v1.59.1/packages/playwright-core/src/server/input.ts \
  | rg -n -C 12 'type\(text|insertText|press\(key|keyboardKey'
printf '%s\n' '--- local Mermaid language configuration ---'
rg -n -C 20 'mermaidSourceLanguage|indentService|indentNodeProp|foldNodeProp|languageData' apps/web/src/components/session-workspace.tsx apps/web/src

Repository: donovan-yohan/arielcharts

Length of output: 24373


🌐 Web query:

site:github.com/microsoft/playwright v1.59.1 keyboard.ts type(text) newline

💡 Result:

In Playwright (including version 1.59.1), the keyboard.type(text) method is designed to send keydown, keypress, and keyup events for each character in the provided string [1][2]. Regarding the handling of newline characters (e.g., '\n'): 1. Behavior: When you include a newline character in the string passed to keyboard.type(text), Playwright processes it as part of the text input [3]. If the target element (like a <textarea>) supports newlines, the newline character will typically result in a new line being inserted [3]. 2. Limitations: The keyboard.type method is intended for typing visible text characters [1]. For special keys, including 'Enter', Playwright documentation explicitly recommends using the keyboard.press method instead [1]. 3. Recommended Approach: If you need to trigger an 'Enter' keypress to submit a form or explicitly move to a new line in a way that triggers keyboard events for the Enter key, you should use keyboard.press('Enter') [1]. If you need to insert multiple lines of text that include newlines, you can use keyboard.type(text) containing '\n' characters, but be aware that it simulates typing each character individually, and if you need the specific 'Enter' key behavior (which may differ from simply inserting a character), keyboard.press('Enter') is the preferred and more robust API for that specific action [1][3]. In the internal implementation, keyboard.type iterates through the string and calls insertText for characters not found in the standard US keyboard layout map, while using press for those that are [2][4]. The logic for handling input is contained within the packages/playwright-core/src/server/input.ts file [2][4].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
python3 - <<'PY'
import re
import urllib.request

url = "https://raw.githubusercontent.com/microsoft/playwright/v1.59.1/packages/playwright-core/src/server/input.ts"
src = urllib.request.urlopen(url, timeout=15).read().decode()
type_body = re.search(r"async type\(progress: Progress, text: string, options\?: \{ delay\?: number \}\) \{(.*?)\n  \}", src, re.S)
assert type_body, "Playwright type() implementation not found"
body = type_body.group(1)
print("type() uses press for layout characters:", "usKeyboardLayout.has(char)" in body)
print("type() uses insertText for non-layout characters:", "this._insertText(progress, char)" in body)
print("newline is explicitly mapped in type():", bool(re.search(r"\\n", body)))
print("newline code point:", repr("\n"))
PY
printf '%s\n' '--- Mermaid language indentation declarations ---'
sed -n '1,180p' apps/web/src/lib/mermaid-language.ts | rg -n -C 3 'StreamLanguage|indent|indentNodeProp|indentService|languageData'
printf '%s\n' '--- codemirror package versions ---'
rg -n -A 3 -B 2 '^  codemirror@|^  "`@codemirror/`(state|view|language)@' pnpm-lock.yaml

Repository: donovan-yohan/arielcharts

Length of output: 2449


🌐 Web query:

CodeMirror 6 insertText newline indentation indentOnInput input handler

💡 Result:

In CodeMirror 6, managing text insertion, newlines, and indentation is handled through a combination of core state transactions, command extensions, and language-specific features [1][2]. For inserting text with proper indentation, the recommended approach depends on whether you are reacting to user input or performing a programmatic update. Newline and Indentation Commands To handle the Enter key with automatic indentation, CodeMirror 6 provides built-in commands in the @codemirror/commands package [3]: - insertNewlineAndIndent: Standard behavior for the Enter key, which calculates indentation based on the language's syntax tree [1][3]. - insertNewlineKeepIndent: A simpler alternative that preserves the indentation of the previous line without invoking complex syntax-based re-indentation [3]. You can configure these in your keymap: keymap.of([{ key: "Enter", run: insertNewlineAndIndent }]) [4]. Automatic Re-indentation (indentOnInput) The indentOnInput extension automatically re-indents the current line when a user types specific characters (e.g., closing braces) [5][1]. It relies on language data defined in your language extension [5][2]: 1. Enable it by adding the indentOnInput extension to your EditorView [5][6]. 2. Configure your language data to include an indentOnInput field, which must be a Regular Expression [5][1]. 3. When the text from the start of the line to the cursor matches this expression, CodeMirror will trigger a re-indentation of that line [5][1]. Programmatic Text Insertion When inserting text programmatically (e.g., via a custom button or API), use the view.dispatch method [7]. To ensure correct multi-cursor behavior, use the state.changeByRange helper, which allows you to define changes relative to each selection range independently [7][8]. For example: view.dispatch(view.state.changeByRange(range => ({ changes: { from: range.from, insert: "your text" }, range: EditorSelection.cursor(range.from + "your text".length) }))) [7][3]. If you need to calculate indentation for inserted text programmatically, you can use the getIndentation function (from @codemirror/language) to retrieve the correct indentation level for a specific position [5][1]. Top Results: [5], [1], [3], [6]

Citations:


Insert the expected indentation explicitly.

page.keyboard.type('\nB-->C') inserts the newline without CodeMirror’s Enter indentation. The resulting source lacks the two spaces required by expected. Type '\n B-->C', or press Enter separately before typing B-->C.

🤖 Prompt for 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.

In `@e2e-workspace-ux-validate.ts` around lines 3543 - 3544, Update the keyboard
input in the Mermaid editor test to include the two-space indentation after the
newline, either by typing the indented text directly or by pressing Enter
separately before typing B-->C, so it matches the expected value.

@donovan-yohan
donovan-yohan merged commit fe5632e into main Aug 11, 2026
7 checks passed
@donovan-yohan
donovan-yohan deleted the codex/issue-100-mermaid-syntax branch August 11, 2026 20:31

This branch was successfully deployed

1 active deployment
Preview — bb097fcd Deployed Aug 11, 2026 by vercel[bot]
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.

Add Mermaid-aware syntax highlighting to the source editor

1 participant