You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
-**Todo notes Mermaid preview (Phase 1B)** - Optional Mermaid rendering for fenced ` ```mermaid ` blocks inside the existing todo Notes **preview** tab. Mermaid stays preview-only: notes still persist as raw `todos.body`, and board cards, notifications, exports, and server payloads do not render Markdown or diagrams.
10
+
11
+
### Improvements
12
+
13
+
-**Layered feature gates** - Added **`SCRUMBOY_MERMAID_NOTES_ENABLED`** as a Markdown sub-flag. Mermaid is active only when both **`SCRUMBOY_MARKDOWN_NOTES_ENABLED=1`** and **`SCRUMBOY_MERMAID_NOTES_ENABLED=1`** are set.
14
+
15
+
-**Preview safety** - Mermaid lazy-loads from a vendored runtime, initializes once with **`securityLevel: "sandbox"`**, strips user `%%{init: ...}%%` / `%%{initialize: ...}%%` directives before render, and falls back to escaped source for diagram-specific failures instead of dropping the user out of preview mode.
16
+
17
+
### Frontend
18
+
19
+
-**Markdown preview pipeline** - Mermaid fence placeholders are resolved after the existing Markdown sanitization step, keeping the normal allow-list unchanged for non-Mermaid Markdown.
20
+
21
+
-**Runtime loading** - Ships **`/vendor/mermaid.min.js`** through the vendor sync/verify pipeline, but does not eagerly load or service-worker precache Mermaid.
22
+
23
+
### Tests
24
+
25
+
-**Server/config** - Added coverage for **`SCRUMBOY_MERMAID_NOTES_ENABLED`** parsing and **`mermaidNotesEnabled`** on auth status responses.
26
+
27
+
-**Preview rendering** - Added Mermaid fence rendering, directive stripping, fallback behavior, and stale async rerender cancellation coverage.
28
+
29
+
### Documentation
30
+
31
+
-**Operator docs** - Updated **`docs/markdown&mermaid.md`**, **`FAQ.md`**, and **`scrumboy.env.example`** for Markdown + Mermaid preview enablement.
Copy file name to clipboardExpand all lines: FAQ.md
+32-2Lines changed: 32 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3,6 +3,7 @@
3
3
## Contents
4
4
5
5
-[How do I enable Markdown in my notes?](#how-do-i-enable-markdown-in-my-notes)
6
+
-[How do I enable Mermaid diagrams in my notes?](#how-do-i-enable-mermaid-diagrams-in-my-notes)
6
7
-[How do I edit several todos at once?](#how-do-i-edit-several-todos-at-once)
7
8
-[What does the done lane mean for dashboard stats?](#what-does-the-done-lane-mean-for-dashboard-stats)
8
9
-[Are tag colors personal, or shared with the team?](#are-tag-colors-personal-or-shared-with-the-team)
@@ -19,11 +20,40 @@ Set `SCRUMBOY_MARKDOWN_NOTES_ENABLED=1` on the server (also accepts `true`, `on`
19
20
20
21
After a restart, the todo dialog **Notes** field shows **markdown** and **preview** tabs. **markdown** is the source editor; **preview** is a sanitized rendered view. Supported syntax includes headings, emphasis, lists, blockquotes, inline and fenced code, horizontal rules (`---` on its own line with blank lines around it), and safe `http`/`https` links.
21
22
22
-
Notes are still stored as raw markdown in `todos.body`. Todo titles and board card titles stay plain text. The server exposes `markdownNotesEnabled` on `/api/auth/status` so the UI only shows the tabs when the feature is enabled.
23
+
Notes are still stored as raw markdown in `todos.body`. Todo titles and board card titles stay plain text. The server exposes `markdownNotesEnabled` on `/api/auth/status` so the UI only enables preview when the server has opted in.
23
24
24
25
Preview hardening: HTML in notes is not rendered; images stay as escaped text; dangerous link schemes and embedded content are stripped or neutralized.
25
26
26
-
For architecture, security, and source references, see [`docs/markdown.md`](docs/markdown.md).
27
+
For architecture, security, and source references, see [`docs/markdown&mermaid.md`](docs/markdown&mermaid.md).
28
+
29
+
## How do I enable Mermaid diagrams in my notes?
30
+
31
+
Mermaid is a **sub-feature of Markdown preview**. You need Markdown preview enabled first (`SCRUMBOY_MARKDOWN_NOTES_ENABLED=1`), then set **`SCRUMBOY_MERMAID_NOTES_ENABLED=1`** on the server (same truthy values: `1`, `true`, `on`, or `yes`; case-insensitive). Turning on Mermaid alone does nothing if Markdown preview is off.
32
+
33
+
After a restart, fenced **` ```mermaid `** blocks in a todo note render as diagrams when you open the **preview** tab. Regular Markdown in the same note still works; non-Mermaid fenced code blocks stay as code.
34
+
35
+
Example:
36
+
37
+
````markdown
38
+
```mermaid
39
+
graph TD
40
+
A[Start] --> B{Decision}
41
+
B -- Yes --> C[Result One]
42
+
B -- No --> D[Result Two]
43
+
```
44
+
````
45
+
46
+
Mermaid does **not** render on board cards, notifications, exports, or server responses. Notes are still saved as raw text in `todos.body`.
47
+
48
+
Preview limits (per note): up to **4** Mermaid blocks, **4000** characters per block, and **8000** characters total Mermaid source. Over-limit or syntax errors show a local warning with the original source instead of breaking the whole preview.
49
+
50
+
Diagrams follow the app’s light/dark theme in preview. Optional yes/no-style branch **label backgrounds** (green/red for pairs like yes/no) can be customized via `/mermaid-semantic-edges.json`; override with `$DATA_DIR/mermaid-semantic-edges.json` (see `data/mermaid-semantic-edges.json.example`).
51
+
52
+
User-authored Mermaid `%%{init: ...}%%` directive blocks are stripped before render so site security settings stay authoritative. Mermaid runs in **strict** mode (inline SVG in the preview pane only).
53
+
54
+
The server exposes `mermaidNotesEnabled` on `/api/auth/status` alongside `markdownNotesEnabled`.
55
+
56
+
For full architecture and security details, see [`docs/markdown&mermaid.md`](docs/markdown&mermaid.md).
@@ -193,7 +193,7 @@ Simplicity of a light Kanban, with the power of structured systems: Roles, sprin
193
193
194
194
- Sticky-Note Wall - per-project scratchpad of draggable sticky notes on the board (see `docs/WALL.md`).
195
195
196
-
- Todo notes Markdown preview - markdown/preview tabs in the todo Notes field(see `FAQ.md`, `docs/markdown.md`).
196
+
- Todo notes Markdown preview (optional) - **markdown** / **preview** tabs in the todo Notes field; optional Mermaid diagrams in fenced ` ```mermaid ` blocks in preview only (see `FAQ.md`, `docs/markdown&mermaid.md`).
Phase 1 adds an **optional, client-side preview** of Markdown in the todo dialog **Notes** field. The server stores and serves notes as **plain text**; it does not parse or sanitize Markdown. Enabling the feature only exposes UI and loads browser libraries—the preview pipeline runs entirely in the SPA.
3
+
Scrumboy supports an **optional, client-side preview** of Markdown in the todo dialog **Notes** field. Mermaid is an optional **sub-feature** of that preview. The server stores and serves notes as **plain text**; it does not parse or sanitize Markdown or Mermaid. Enabling these features only exposes UI and loads browser libraries—the preview pipeline runs entirely in the SPA.
4
4
5
5
For operator setup, see [`FAQ.md`](../FAQ.md). This document describes architecture, configuration, and the preview security model.
6
6
@@ -13,6 +13,7 @@ For operator setup, see [`FAQ.md`](../FAQ.md). This document describes architect
13
13
|**markdown** / **preview** tabs in the Edit/New Todo dialog (`#todoBody`, `#todoBodyPreview`) | Rendering Markdown on the board, dashboard, or notifications |
14
14
| Preview of `todos.body` when the user opens the **preview** tab | Server-side Markdown → HTML conversion |
15
15
| Sanitized HTML in the preview pane only | WYSIWYG editing (Notes stay a `<textarea>`) |
16
+
| Optional Mermaid rendering for fenced ` ```mermaid ` blocks in the preview pane only | Rendering Mermaid on cards, notifications, exports, or any server path |
16
17
| Raw Markdown persisted on create/patch | Images, raw HTML, or non-http(s) links in preview |
17
18
18
19
Todo **titles** and card titles on the board are always escaped plain text (see `board-rendering` tests).
@@ -24,12 +25,13 @@ Todo **titles** and card titles on the board are always escaped plain text (see
24
25
| Variable | Default | Enabled when |
25
26
|----------|---------|--------------|
26
27
|`SCRUMBOY_MARKDOWN_NOTES_ENABLED`| off (unset) |`1`, `true`, `on`, or `yes` (trimmed, case-insensitive) |
28
+
|`SCRUMBOY_MERMAID_NOTES_ENABLED`| off (unset) |`1`, `true`, `on`, or `yes`**and** Markdown notes are already enabled |
27
29
28
-
Parsed in `internal/config/config.go` (`markdownNotesEnabledFromEnv`), passed through `cmd/scrumboy/main.go` into the HTTP server (`ServerOpts.MarkdownNotesEnabled`), and exposed to the SPA as **`markdownNotesEnabled`** on:
30
+
Parsed in `internal/config/config.go` (`markdownNotesEnabledFromEnv`, `mermaidNotesEnabledFromEnv`), passed through `cmd/scrumboy/main.go` into the HTTP server (`ServerOpts.MarkdownNotesEnabled`, `ServerOpts.MermaidNotesEnabled`), and exposed to the SPA as **`markdownNotesEnabled`** and **`mermaidNotesEnabled`** on:
29
31
30
32
-`GET /api/auth/status` (authenticated and anonymous status payloads in `internal/httpapi/routing_auth.go`)
31
33
32
-
The UI reads that flag once during auth bootstrap (`modules/router.ts` → `setMarkdownNotesEnabled`) and gates tab visibility in `modules/dialogs/todo.ts` (`markdownNotesPreviewEnabled()`).
34
+
The UI reads those flags once during auth bootstrap (`modules/router.ts` → `setMarkdownNotesEnabled` / `setMermaidNotesEnabled`). Markdown gates tab visibility in `modules/dialogs/todo.ts` (`markdownNotesPreviewEnabled()`); Mermaid only affects whether fenced Mermaid blocks are upgraded inside the preview pane.
33
35
34
36
There is **no per-project** or per-user toggle; it is instance-wide.
35
37
@@ -48,6 +50,10 @@ renderMarkdownPreviewInto()
48
50
│
49
51
├─► markdown-it.render() (html: false)
50
52
└─► DOMPurify + link policy + DOM cleanup
53
+
│
54
+
├─► (optional) detect Mermaid placeholders
55
+
├─► lazy-load /vendor/mermaid.min.js on first Mermaid preview use
56
+
└─► mermaid.run() in preview-only hosts (strict mode)
51
57
│
52
58
▼
53
59
#todoBodyPreview innerHTML (ephemeral, not saved)
@@ -70,14 +76,14 @@ renderMarkdownPreviewInto()
70
76
-**`todoNotesMode`:**`"markdown"` | `"preview"`.
71
77
-**`syncTodoNotesModeUI()`:** toggles `hidden` on textarea vs preview, `is-active` / `aria-pressed` on tabs.
72
78
-**`renderTodoNotesPreview()`:** calls `renderMarkdownPreviewInto`; on vendor/render failure, shows a toast and falls back to markdown mode.
73
-
-**`input` listener:** re-renders preview live while the preview tab is active.
79
+
-**`input` listener:** re-renders preview live while the preview tab is active; stale async Mermaid renders are cancelled by container-scoped render epochs.
Empty or whitespace-only notes set `todo-markdown-preview--empty` and clear the container (placeholder via CSS `::before`).
83
89
@@ -88,6 +94,7 @@ Preview typography and colors are scoped under `#todoDialog .todo-markdown-previ
88
94
### State
89
95
90
96
-`current._markdownNotesEnabled` in `modules/state/state.ts`, set from auth status only (not from board payloads).
97
+
-`current._mermaidNotesEnabled` in `modules/state/state.ts`, also set from auth status only.
91
98
92
99
---
93
100
@@ -125,6 +132,20 @@ On a detached `<template>`:
125
132
-**External** (`http://` / `https://`): set `target="_blank"` and `rel="noopener noreferrer"`.
126
133
-**Unsafe href:** replace anchor with text node (link text only).
127
134
135
+
### 4. Mermaid (optional, preview-only)
136
+
137
+
When `mermaidEnabled` is true and the Markdown contains fenced Mermaid blocks:
138
+
139
+
1.`renderer.rules.fence` emits opaque placeholders for ` ```mermaid ` blocks while keeping non-Mermaid fences as normal code blocks.
140
+
2. After sanitized HTML is placed into the preview container, placeholders are replaced with local Mermaid hosts.
141
+
3.`/vendor/mermaid.min.js` is lazy-loaded on demand.
142
+
4. Mermaid initializes with preview theme tokens (`theme: "base"` + `themeVariables` from `--panel2`, `--text`, `--accent`, `--border`) so diagram backgrounds match the preview pane in light and dark mode. Re-initializes when the effective theme changes.
143
+
5. All user-authored Mermaid directive blocks in the `%%{...}%%` form are stripped before render so site security settings remain authoritative.
144
+
6. Preview-side limits cap Mermaid work per note: only the first 4 Mermaid fences render, each fence is capped at 4000 characters, and the total Mermaid preview budget is capped at 8000 characters.
145
+
7. Limit overages and diagram syntax failures do **not** tear down preview mode; the preview shows a local warning/error block with the original source.
146
+
8. Changing app theme while the todo preview is open re-renders diagrams via the same preview pipeline (render epochs prevent stale async work from committing).
147
+
9. Optional yes/no-style branch **label backgrounds** only: `/mermaid-semantic-edges.json` (override via `$DATA_DIR/mermaid-semantic-edges.json`; see `data/mermaid-semantic-edges.json.example`). Applied after render via DOM; does not modify diagram source or connector lines.
148
+
128
149
---
129
150
130
151
## Supported Markdown (preview)
@@ -145,6 +166,7 @@ Verified in `modules/markdown-preview.test.ts`:
145
166
- Raw HTML in source → escaped in output
146
167
- Dangerous or non-web link schemes → plain text
147
168
- Protocol-relative URLs (`//host/...`)
169
+
- Mermaid directives (`%%{...}%%`) are ignored for rendering; site-level Mermaid config wins
148
170
149
171
---
150
172
@@ -154,6 +176,8 @@ Verified in `modules/markdown-preview.test.ts`:
154
176
-**CSP / trust:** preview depends on vendored `markdown-it` and DOMPurify; `npm test` in `internal/httpapi/web` runs `verify-vendor.mjs` before Vitest.
155
177
-**Link exfiltration:** only `http`/`https` external navigation; `noopener noreferrer` on external tabs.
156
178
-**No Markdown on titles** avoids XSS or layout surprises on shared boards and SSE-driven card updates.
179
+
-**Mermaid isolation:** in Mermaid `11.15.0`, the preview uses `securityLevel: "strict"` (Mermaid's secure default), which encodes HTML in diagram text and renders inline SVG. Diagram output is never passed through the general Markdown allow-list as arbitrary SVG/HTML. Inline (rather than sandboxed-iframe) rendering is what lets the semantic label coloring recolor matched label backgrounds via the DOM after render.
180
+
-**Mermaid scope:** diagrams only render in the todo dialog preview, never on the board or server.
157
181
158
182
---
159
183
@@ -163,9 +187,16 @@ Verified in `modules/markdown-preview.test.ts`:
163
187
|-------|------|
164
188
|`/vendor/markdown-it.min.js`| Parser (eager script in `index.html`) |
165
189
|`/vendor/purify.min.js`| Sanitizer (eager script in `index.html`) |
190
+
|`/vendor/mermaid.min.js`| Mermaid runtime (lazy-loaded only when Mermaid preview is needed) |
Service worker (`sw.js`) precaches vendor scripts and `dist/markdown-preview.js` for offline-capable loads after first visit.
193
+
Service worker (`sw.js`) precaches the eager Markdown assets and `dist/markdown-preview.js`. Mermaid is **not** install-time precached. It is fetched lazily, and because `/vendor/mermaid.min.js` is handled by the service worker's same-origin cache-first asset branch, a successful first fetch while the service worker is active makes it available for later offline reuse from cache.
194
+
195
+
Manual verification for offline reuse:
196
+
197
+
- Load a note that contains a Mermaid fence while online and wait for the preview to render once.
198
+
- Disconnect or simulate offline mode, then reopen the same note's preview.
199
+
- Expected result: the preview can still resolve `/vendor/mermaid.min.js` from cache; if the runtime was never fetched successfully before going offline, Mermaid preview is not guaranteed.
0 commit comments