Instead of leaving the config empty, the editor now manages the theme: after every validation and on site light/dark changes, syncManagedTheme sets it to the diagram type's own mermaid default (the config section's theme, e.g. redux-color for flowcharts, else the global default) or, in dark mode, the redux -dark variant (redux-dark-color) or dark. Only editor-set themes are replaced: a missing theme, the global default, a section default or any of their dark variants (isManagedTheme). A theme the user chose, such as forest, is never touched. Detector ids that do not match their config section (flowchart-v2, flowchart-elk, classDiagram, stateDiagram, xychart, railroadAbnf/Ebnf/Peg) are aliased in mermaid.ts; ids without a section (wardley, zenuml) use the global default. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
5.7 KiB
CLAUDE.md
Commands
Uses pnpm (install via corepack enable pnpm). Node >= 24 required.
pnpm dev— dev server on http://localhost:3000pnpm build— production build (static site, output todocs/)pnpm check— svelte-check type checkingpnpm lint/pnpm lint:fix— prettier + eslintpnpm test:unit— vitest in watch mode;pnpm vitest runfor a single pass- Single unit test:
pnpm vitest run src/lib/util/serde.test.ts(or-t "test name") pnpm test:e2e— Playwright tests intests/; auto-starts the dev server on port 3000 (reuses one already running).pnpm test:e2e:ui/:debugfor interactive runspnpm test— unit + e2e
Gotchas:
- If
.claude/worktrees/contains repo copies, vitest picks up their test files too — scope withpnpm vitest run --dir src. - Local Playwright runs can be flaky under parallel workers; retry or use
--workers=1before assuming a real failure. - CI runs lint, check, unit tests, and Playwright (against a production build) on PRs to master.
Architecture
SvelteKit 2 + Svelte 5 (runes) SPA using @sveltejs/adapter-static. docs/ is build output (GitHub Pages convention) — never edit it. Routes: / (redirects), /edit (main editor), /view (read-only viewer).
State flow (the core of the app)
src/lib/util/state.svelte.ts is the single source of truth:
inputState— a$staterune holdingState(code, mermaid config JSON, pan/zoom, etc.), initialized from localStorage keycodeStore. All writes must go through the exported update functions (updateCode,updateCodeStore,updateConfig,replaceInputState, ...), which callpersistAndProcess(): persist to localStorage → async re-validate viamermaid.parse→ publish tovalidatedState. Reads inside update functions are wrapped inuntrackso calling effects don't depend on the whole input state.validatedState— read-only validated snapshot (addserror,errorMarkers,serialized,diagramType). Internal reads should use this, but it is never persisted or shared externally.- The diagram
themein the config is editor-managed: after every validation and on site light/dark changes,syncManagedThemesets it to the diagram type's mermaid default (its config section'stheme, e.g.redux-colorfor flowcharts) or the redux-darkvariant /darkin dark mode. A theme outside that family (isManagedThemeinmermaid.ts) is the user's choice and is never touched. The default config is therefore{}until the first validation fills the theme in. - The serialized state is mirrored into the URL hash (debounced) — the URL is the sharing mechanism.
src/lib/util/serde.tsencodes state aspako:<base64 deflate>(legacy plain-base64 supported).mermaid.inkPNG/SVG links, Kroki, and Mermaid Chart URLs are derived from it inurls. - App startup goes through
initHandler()insrc/lib/util/util.ts: migrations → load state from URL hash → optional gist/URL loading (fileLoaders/) → start URL-hash subscription → analytics.
Persistence
src/lib/util/persist.svelte.ts provides readJSON/writeJSON and a persisted() localStorage-backed rune, shared by state, migrations (migrations.svelte.ts), promos, and History. History (src/lib/components/History/historyState.svelte.ts) auto-saves a timeline of diagrams for the whole session (startAutoSave() in the edit page).
Rendering
src/lib/util/mermaid.ts wraps mermaid and registers the tidy-tree layout engine plus ZenUML at module load (ELK is bundled with mermaid 12). View.svelte renders the diagram; panZoom.ts wraps svg-pan-zoom; "rough" hand-drawn mode uses svg2roughjs. Sample diagrams come from @mermaid-js/examples.
Editors
Two editor implementations behind Editor.svelte: Monaco for desktop (DesktopEditor.svelte; custom mermaid language, completions, and error markers in monacoExtra.ts) and CodeMirror for mobile (MobileEditor.svelte). Both edit either the diagram code or the mermaid config JSON depending on editorMode.
Security
States loaded from URLs pass through sanitizeConfig (state.svelte.ts), which detects unsafe mermaid config (XSS vectors, prototype pollution, secure keys) and asks the user before stripping it. Keep this in mind when touching anything that deserializes external state.
UI / styling
- shadcn-svelte components live in
src/lib/components/ui/(bits-ui based, registry config incomponents.json) — treat as vendored; several lint rules are disabled there. - Tailwind CSS 4 (via
@tailwindcss/vite). - Icons via unplugin-icons:
import X from '~icons/material-symbols/...'; custom icons load fromstatic/iconsunder~icons/custom/*. - Path alias:
$/*→src/lib/*(in addition to SvelteKit's$lib).
Environment / build quirks
- Env vars use the
MERMAID_prefix (import.meta.env), read insrc/lib/util/env.ts. Defaults in.env; copy to.env.localfor local overrides. These control renderer URL (mermaid.ink), Kroki, analytics, and Mermaid Chart integration. - HMR is deliberately disabled — every change triggers a full page reload (see
alwaysFullReloadinvite.config.js) because HMR corrupts app state. vite.config.jspins monaco/mermaid/codemirror into named vendor chunks for long-term caching — keep that intact when touching build config.
Conventions
- ESLint enforces alphabetically sorted object keys in
src/for objects with 5+ keys (sort-keys/sort-keys-fix), plus typescript-eslint strict and unicorn rules. - Unit tests are colocated with source (
*.test.tsinsrc/); vitest runs with jsdom and also supports in-source tests (import.meta.vitest). Playwright e2e specs live intests/and usedata-testidconstants fromsrc/lib/constants.ts(TID).