Vibecode Mermaid Chart
track this build5 phases, 15 steps, beginner friendly0%Mermaid Chart's solo core is compact: build a browser-based diagram-as-code editor with drag, resize, connect, keyboard shortcuts, autosave, and SVG or PNG export. A competent builder can reach a useful personal version in one sitting, while the paid product mainly wins on interaction polish, collaboration.
You are building a lean indie version of Mermaid Chart. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Mermaid Chart · indie build An editor with the diagram source on one side and a rendered view on the other, rendering as you type without losing your place, exporting deterministically so a diagram in a repository produces the same image on any machine, and writing visual adjustments back into the text so there is only ever one source. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Rendering | The diagram language's own renderer, in the browser | reimplementing the layout of a diagram language is a project of its own and the renderer is the specification | | Runtime | TypeScript with Vite | an editor and a preview pane, with no server needed | | Editor | A code editing component with syntax highlighting for the diagram language | text editing is solved and the errors it surfaces are half the usability | | Export | Deterministic SVG and PNG rendered from the same source | a diagram in a repository must produce the same image in continuous integration as on a laptop | | Storage | Files on disk, or the browser with file open and save | the diagram is text and belongs in whatever holds the rest of your text | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the build and the batch render command. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **The diagram language's renderer** · free - Why: It is the specification of the language. Reimplementing its layout is a separate project and would diverge immediately. - Get it: Install the renderer as a dependency. Pin the version, since a renderer update changes layout and therefore every committed image. - Verify: A sample diagram renders to SVG - [ ] **A font you may embed** · free with an open licence - Why: Embedding is what makes exports identical across machines, and embedding is redistribution which not every licence permits. - Get it: An openly licensed family with its licence file kept alongside. - Verify: The licence permits embedding and the exported SVG carries the font - [ ] **A second machine or container with different fonts** · free - Why: Phase 4's determinism check is meaningless on the machine that produced the reference, since it has exactly the fonts that were used. - Get it: A container image with a minimal font set is the easiest way, and it is also what your continuous integration will look like. - Verify: The same source exports identically in both environments - [ ] **Real diagrams from your own documentation** · free - Why: Error handling and re-render performance both need real sources, which are longer and messier than examples and contain the syntax you actually get wrong. - Get it: Take the diagrams from a project you maintain, or write the ones you have been meaning to. - Verify: At least five real diagrams render, including one large one ## Quick start ```sh npm create vite@latest . -- --template vanilla-ts ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Import and export of proprietary diagram formats. - Multiplayer editing. - Presentation and organisational controls. - Exact layout control beyond the language. - high-fidelity import and export - presentation and enterprise controls - multiplayer cursors and comments - large template libraries If one of those is essential to you, that is the reason to keep paying for Mermaid Chart, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Mermaid Chart The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a diagram editor where the text is the source of truth and visual edits write back to it to replace Mermaid Chart. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero The text is the diagram. Everything else is a view. That is the whole reason to build this rather than a visual editor: a diagram in text lives in version control, diffs in a pull request, and can be generated by a script. The consequence is the hard part. If you let someone drag a node and store the position somewhere the text does not know about, you now have two sources of truth and they will disagree within a session. Either a visual edit writes back into the text, or there is no visual edit. Decide that in Phase 3 rather than discovering it after building both halves. ### Stack (fixed, do not substitute) - Rendering: The diagram language's own renderer, in the browser · reimplementing the layout of a diagram language is a project of its own and the renderer is the specification - Runtime: TypeScript with Vite · an editor and a preview pane, with no server needed - Editor: A code editing component with syntax highlighting for the diagram language · text editing is solved and the errors it surfaces are half the usability - Export: Deterministic SVG and PNG rendered from the same source · a diagram in a repository must produce the same image in continuous integration as on a laptop - Storage: Files on disk, or the browser with file open and save · the diagram is text and belongs in whatever holds the rest of your text ### Data model (create this before Phase 1) - The source text is the model. These are the derived structures the editor keeps. - documents(id, path, source, updated_at) - parse_result(document_id, ok, ast_json, error_line, error_message, parsed_at) - render_result(document_id, svg, width, height, node_positions_json, rendered_at) - history(document_id, at, source) · text revisions, since the text is what is worth versioning - Node positions come out of the renderer and are never stored as input. If a visual edit needs to change one, it must change the text that produced it. ### Phase 1 · Type and see, without losing your place Build: A split editor and preview. Re-render on a debounce. Preserve the preview's pan and zoom across renders so a small edit does not reset the view. Keep the last good render on screen while the text is temporarily invalid. Trap: Clearing the preview whenever the source fails to parse means the diagram vanishes on every keystroke that lands mid-token, which is most of them. Keep the last successful render visible, show the error alongside it, and replace only when a new render succeeds. Done when: Typing updates the diagram without the view resetting its pan or zoom. A syntax error keeps the last good diagram on screen with the error shown alongside. The error names a line number that matches the source. A large diagram re-renders within a debounce window without blocking typing. Do not build yet: No export, visual editing or version control. ### Phase 2 · Errors a person can act on Build: Surface parse errors with the line, a caret at the position where possible, and the text of what was expected. Highlight the offending line in the editor. Add a small set of common mistakes with a plainer explanation. Trap: Showing the renderer's raw error message is the difference between a tool people learn and one they abandon, because those messages are written for the parser rather than the author. Map the common ones to plain wording, and always show the line. Done when: An unclosed bracket produces an error naming its line and highlighted in the editor. A misspelled diagram type produces a message listing the valid types. Fixing the error re-renders without needing a manual refresh. At least five common mistakes have plainer explanations than the raw message. Do not build yet: No export or visual editing yet. ### Phase 3 · The visual editing decision Build: Pick one. If you allow visual edits, implement them as text transformations: dragging a node reorders or re-parents it in the source, and renaming edits the label in place. If you do not, make the preview explicitly read-only and say so. Trap: Storing a dragged position in a sidecar file that the diagram language does not know about creates a second source of truth, and the moment someone edits the text in another editor the two disagree with no way to reconcile. Round trip through the text or do not offer the gesture at all. Done when: Every visual edit produces a text change visible in the editor, or the preview is read-only and labelled as such. Renaming a node in the preview updates the source text and the change survives a reload. No position or styling is stored outside the source text, verified by inspecting what is saved. Undo works from the text side regardless of where the edit was made. Do not build yet: No export or version control integration yet. ### Phase 4 · Export that is the same everywhere Build: Export SVG and PNG from the same render. Embed or outline fonts. Pin any setting that affects layout. Then render the same file on a second machine and compare the output byte for byte. Trap: A diagram whose export depends on the fonts installed locally will render differently in continuous integration than on a laptop, and the difference shows up as a mysteriously changed image in a pull request. Bundle the font, embed it in the SVG, and verify by rendering somewhere with a different font set. Done when: The same source exports byte-identical SVG on two different machines. An exported SVG opens in a different application with the correct fonts. A PNG at two times scale has exactly double the dimensions and is sharper rather than merely larger. Layout-affecting settings are pinned in the document rather than taken from the environment. Do not build yet: No version control workflow yet. ### Phase 5 · Diagrams in a repository Build: A command that renders a directory of diagram files to images. A check that fails when a committed image does not match its source. Documentation of how to embed the rendered output in your documentation. Trap: Committing a rendered image without a check means it drifts from its source silently, and readers see a diagram that no longer matches the text that supposedly produced it. A check that re-renders and compares turns that into a failed build rather than a lie in your documentation. Done when: The command renders a directory of sources to images. The check fails when a committed image differs from a fresh render of its source. Editing a source and forgetting to re-render is caught by the check. A diagram change produces a readable text diff in a pull request. Do not build yet: Sharing comes last. ### Phase 6 · Sharing without pretending to collaborate Build: Share by encoding the source into a link, or by publishing the rendered output. State that editing is single user. Keep shared links versioned so an edit after sharing does not silently change what a recipient sees. Trap: Because the source is text, it is tempting to sync it live over a socket, and a naive last-write-wins sync on a text document loses characters when two people type. If you want collaborative text editing, that is a conflict-free data type and a substantial project rather than a socket. Done when: A shared link renders the diagram for someone who has never used the application. Editing after sharing does not change what a recipient already received unless you re-share. The interface states that editing is single user. A link too long for practical use falls back to publishing the rendered output. Do not build yet: Nothing after this. ### Out of scope (and why) - High-fidelity import from and export to proprietary diagram formats, which is reverse engineering with no end. - Multiplayer cursors and comments, which for a text document means collaborative text editing rather than a shared canvas. - The presentation and organisational controls that make the hosted product a team tool. - Layout control beyond what the diagram language offers. If you need to place things exactly, a visual editor is the right tool and this is not it. ### README must contain - That the text is the source of truth and nothing is stored outside it - Which editing model was chosen, and that visual edits round trip through the text if offered - That fonts are embedded so exports match across machines - That the drift check keeps committed images matching their sources - That editing is single user by design ===== AGENTS.md ===== # Agent instructions · Mermaid Chart indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: The diagram language's own renderer, in the browser, TypeScript with Vite, A code editing component with syntax highlighting for the diagram language, Deterministic SVG and PNG rendered from the same source, Files on disk, or the browser with file open and save. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Clearing the preview whenever the source fails to parse means the diagram vanishes on every keystroke that lands mid-token, which is most of them. Keep the last successful render visible, show the error alongside it, and replace only when a new render succeeds. - Showing the renderer's raw error message is the difference between a tool people learn and one they abandon, because those messages are written for the parser rather than the author. Map the common ones to plain wording, and always show the line. - Storing a dragged position in a sidecar file that the diagram language does not know about creates a second source of truth, and the moment someone edits the text in another editor the two disagree with no way to reconcile. Round trip through the text or do not offer the gesture at all. - A diagram whose export depends on the fonts installed locally will render differently in continuous integration than on a laptop, and the difference shows up as a mysteriously changed image in a pull request. Bundle the font, embed it in the SVG, and verify by rendering somewhere with a different font set. - Committing a rendered image without a check means it drifts from its source silently, and readers see a diagram that no longer matches the text that supposedly produced it. A check that re-renders and compares turns that into a failed build rather than a lie in your documentation. ===== BUILD_PLAN.md ===== # Build plan · Mermaid Chart An editor with the diagram source on one side and a rendered view on the other, rendering as you type without losing your place, exporting deterministically so a diagram in a repository produces the same image on any machine, and writing visual adjustments back into the text so there is only ever one source. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Type and see, without losing your place The diagram updates as you type and the view does not jump around. ### Steps 1. Set up the split editor and preview Code editor with syntax highlighting on one side, rendered output on the other. Files: `src/editor.ts` ```sh npm create vite@latest . -- --template vanilla-ts ``` 2. Preserve view state across renders Pan and zoom survive a re-render. Without this, typing is unusable on anything large. Files: `src/preview.ts` 3. Keep the last good render On a parse failure, show the error beside the previous diagram rather than blanking it. Files: `src/render.ts` ### Done when - [ ] Typing does not reset pan or zoom - [ ] A syntax error keeps the last good diagram visible - [ ] The error names a matching line number - [ ] A large diagram re-renders without blocking typing ### Watch out - Clearing the preview whenever the source fails to parse means the diagram vanishes on every keystroke that lands mid-token, which is most of them. Keep the last successful render visible, show the error alongside it, and replace only when a new render succeeds. ## Phase 2 · Errors a person can act on A mistake in the source tells you where it is and what it expected. ### Steps 1. Extract line and position from the error Different diagram types report differently. Normalise into one shape. Files: `src/errors.ts` 2. Highlight in the editor The line and, where known, the column. Reading a line number and scrolling is friction. Files: `src/editor.ts` 3. Map common mistakes to plain wording Five is enough to start. Add one whenever you confuse yourself. Files: `src/error-map.ts` ### Done when - [ ] An unclosed bracket names and highlights its line - [ ] An invalid diagram type lists the valid ones - [ ] Fixing an error re-renders automatically - [ ] Five common mistakes have plainer explanations ### Watch out - Showing the renderer's raw error message is the difference between a tool people learn and one they abandon, because those messages are written for the parser rather than the author. Map the common ones to plain wording, and always show the line. ## Phase 3 · The visual editing decision Either a visual change writes back into the text, or there is no visual change. ### Steps 1. Decide and document Read-only preview is a legitimate and much simpler answer. Write down which you chose and why. Files: `docs/editing-model.md` 2. Implement edits as text transformations If you offer them. The gesture produces a source change and nothing else. Files: `src/visual-edit.ts` 3. Verify nothing lives outside the text Inspect the saved file. Any extra state is the second source of truth this phase prevents. ### Done when - [ ] Every visual edit produces a visible text change, or the preview is read-only - [ ] A visual rename survives a reload - [ ] Nothing is stored outside the source text - [ ] Undo works from the text side ### Watch out - Storing a dragged position in a sidecar file that the diagram language does not know about creates a second source of truth, and the moment someone edits the text in another editor the two disagree with no way to reconcile. Round trip through the text or do not offer the gesture at all. ## Phase 4 · Export that is the same everywhere The same source produces the same image on any machine. ### Steps 1. Bundle and embed the font Rather than relying on what is installed. This is the main source of environment drift. Files: `src/export.ts` 2. Pin layout settings in the document So the file carries everything needed to reproduce its rendering. Files: `src/document.ts` 3. Verify on a second machine Or a container with a different font set. Comparing on the machine that made it proves nothing. ### Done when - [ ] The same source exports identically on two machines - [ ] An exported SVG opens correctly elsewhere - [ ] A two times PNG has double the dimensions and more detail - [ ] Layout settings come from the document rather than the environment ### Watch out - A diagram whose export depends on the fonts installed locally will render differently in continuous integration than on a laptop, and the difference shows up as a mysteriously changed image in a pull request. Bundle the font, embed it in the SVG, and verify by rendering somewhere with a different font set. ## Phase 5 · Diagrams in a repository A diagram lives beside the code it describes and its changes are reviewable. ### Steps 1. Write the batch render command Directory in, images out, deterministic. Suitable for continuous integration. Files: `src/cli.ts` 2. Add a drift check Re-render and compare. Failing the build is what keeps documentation honest. Files: `src/check.ts` 3. Document the embedding pattern How to reference the rendered output from your documentation. Files: `README.md` ### Done when - [ ] The command renders a directory deterministically - [ ] The drift check fails on a stale committed image - [ ] A forgotten re-render is caught - [ ] A diagram change produces a readable text diff ### Watch out - Committing a rendered image without a check means it drifts from its source silently, and readers see a diagram that no longer matches the text that supposedly produced it. A check that re-renders and compares turns that into a failed build rather than a lie in your documentation. ## Not in this build - Import and export of proprietary diagram formats. - Multiplayer editing. - Presentation and organisational controls. - Exact layout control beyond the language. ## After v1, if you want it - A diagram gallery generated from the repository, which makes existing diagrams discoverable - Rendering diagrams from code comments, so an architecture diagram lives next to what it describes - A visual diff of rendered output in pull requests, alongside the text diff ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Pinned, because a renderer update changes layout and therefore every committed image. VITE_RENDERER_VERSION=pinned-in-package-json # Required. Bundled font embedded in exports, with its licence file beside it. VITE_FONT_PATH=/fonts/inter.woff2 # Required. Delay before re-rendering while typing. Longer on large diagrams. VITE_RENDER_DEBOUNCE_MS=250 # Required. Raster scale for PNG export, rendered from vector at that scale rather than upscaled. EXPORT_SCALE=2 # Required. Where the batch command looks for sources and writes images. DIAGRAMS_DIR=./docs/diagrams
You are building a lean indie version of Mermaid Chart. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Mermaid Chart · indie build An editor with the diagram source on one side and a rendered view on the other, rendering as you type without losing your place, exporting deterministically so a diagram in a repository produces the same image on any machine, and writing visual adjustments back into the text so there is only ever one source. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Rendering | The diagram language's own renderer, in the browser | reimplementing the layout of a diagram language is a project of its own and the renderer is the specification | | Runtime | TypeScript with Vite | an editor and a preview pane, with no server needed | | Editor | A code editing component with syntax highlighting for the diagram language | text editing is solved and the errors it surfaces are half the usability | | Export | Deterministic SVG and PNG rendered from the same source | a diagram in a repository must produce the same image in continuous integration as on a laptop | | Storage | Files on disk, or the browser with file open and save | the diagram is text and belongs in whatever holds the rest of your text | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the build and the batch render command. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **The diagram language's renderer** · free - Why: It is the specification of the language. Reimplementing its layout is a separate project and would diverge immediately. - Get it: Install the renderer as a dependency. Pin the version, since a renderer update changes layout and therefore every committed image. - Verify: A sample diagram renders to SVG - [ ] **A font you may embed** · free with an open licence - Why: Embedding is what makes exports identical across machines, and embedding is redistribution which not every licence permits. - Get it: An openly licensed family with its licence file kept alongside. - Verify: The licence permits embedding and the exported SVG carries the font - [ ] **A second machine or container with different fonts** · free - Why: Phase 4's determinism check is meaningless on the machine that produced the reference, since it has exactly the fonts that were used. - Get it: A container image with a minimal font set is the easiest way, and it is also what your continuous integration will look like. - Verify: The same source exports identically in both environments - [ ] **Real diagrams from your own documentation** · free - Why: Error handling and re-render performance both need real sources, which are longer and messier than examples and contain the syntax you actually get wrong. - Get it: Take the diagrams from a project you maintain, or write the ones you have been meaning to. - Verify: At least five real diagrams render, including one large one ## Quick start ```sh npm create vite@latest . -- --template vanilla-ts ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Import and export of proprietary diagram formats. - Multiplayer editing. - Presentation and organisational controls. - Exact layout control beyond the language. - high-fidelity import and export - presentation and enterprise controls - multiplayer cursors and comments - large template libraries If one of those is essential to you, that is the reason to keep paying for Mermaid Chart, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Mermaid Chart The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a diagram editor where the text is the source of truth and visual edits write back to it to replace Mermaid Chart. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero The text is the diagram. Everything else is a view. That is the whole reason to build this rather than a visual editor: a diagram in text lives in version control, diffs in a pull request, and can be generated by a script. The consequence is the hard part. If you let someone drag a node and store the position somewhere the text does not know about, you now have two sources of truth and they will disagree within a session. Either a visual edit writes back into the text, or there is no visual edit. Decide that in Phase 3 rather than discovering it after building both halves. ### Stack (fixed, do not substitute) - Rendering: The diagram language's own renderer, in the browser · reimplementing the layout of a diagram language is a project of its own and the renderer is the specification - Runtime: TypeScript with Vite · an editor and a preview pane, with no server needed - Editor: A code editing component with syntax highlighting for the diagram language · text editing is solved and the errors it surfaces are half the usability - Export: Deterministic SVG and PNG rendered from the same source · a diagram in a repository must produce the same image in continuous integration as on a laptop - Storage: Files on disk, or the browser with file open and save · the diagram is text and belongs in whatever holds the rest of your text ### Data model (create this before Phase 1) - The source text is the model. These are the derived structures the editor keeps. - documents(id, path, source, updated_at) - parse_result(document_id, ok, ast_json, error_line, error_message, parsed_at) - render_result(document_id, svg, width, height, node_positions_json, rendered_at) - history(document_id, at, source) · text revisions, since the text is what is worth versioning - Node positions come out of the renderer and are never stored as input. If a visual edit needs to change one, it must change the text that produced it. ### Phase 1 · Type and see, without losing your place Build: A split editor and preview. Re-render on a debounce. Preserve the preview's pan and zoom across renders so a small edit does not reset the view. Keep the last good render on screen while the text is temporarily invalid. Trap: Clearing the preview whenever the source fails to parse means the diagram vanishes on every keystroke that lands mid-token, which is most of them. Keep the last successful render visible, show the error alongside it, and replace only when a new render succeeds. Done when: Typing updates the diagram without the view resetting its pan or zoom. A syntax error keeps the last good diagram on screen with the error shown alongside. The error names a line number that matches the source. A large diagram re-renders within a debounce window without blocking typing. Do not build yet: No export, visual editing or version control. ### Phase 2 · Errors a person can act on Build: Surface parse errors with the line, a caret at the position where possible, and the text of what was expected. Highlight the offending line in the editor. Add a small set of common mistakes with a plainer explanation. Trap: Showing the renderer's raw error message is the difference between a tool people learn and one they abandon, because those messages are written for the parser rather than the author. Map the common ones to plain wording, and always show the line. Done when: An unclosed bracket produces an error naming its line and highlighted in the editor. A misspelled diagram type produces a message listing the valid types. Fixing the error re-renders without needing a manual refresh. At least five common mistakes have plainer explanations than the raw message. Do not build yet: No export or visual editing yet. ### Phase 3 · The visual editing decision Build: Pick one. If you allow visual edits, implement them as text transformations: dragging a node reorders or re-parents it in the source, and renaming edits the label in place. If you do not, make the preview explicitly read-only and say so. Trap: Storing a dragged position in a sidecar file that the diagram language does not know about creates a second source of truth, and the moment someone edits the text in another editor the two disagree with no way to reconcile. Round trip through the text or do not offer the gesture at all. Done when: Every visual edit produces a text change visible in the editor, or the preview is read-only and labelled as such. Renaming a node in the preview updates the source text and the change survives a reload. No position or styling is stored outside the source text, verified by inspecting what is saved. Undo works from the text side regardless of where the edit was made. Do not build yet: No export or version control integration yet. ### Phase 4 · Export that is the same everywhere Build: Export SVG and PNG from the same render. Embed or outline fonts. Pin any setting that affects layout. Then render the same file on a second machine and compare the output byte for byte. Trap: A diagram whose export depends on the fonts installed locally will render differently in continuous integration than on a laptop, and the difference shows up as a mysteriously changed image in a pull request. Bundle the font, embed it in the SVG, and verify by rendering somewhere with a different font set. Done when: The same source exports byte-identical SVG on two different machines. An exported SVG opens in a different application with the correct fonts. A PNG at two times scale has exactly double the dimensions and is sharper rather than merely larger. Layout-affecting settings are pinned in the document rather than taken from the environment. Do not build yet: No version control workflow yet. ### Phase 5 · Diagrams in a repository Build: A command that renders a directory of diagram files to images. A check that fails when a committed image does not match its source. Documentation of how to embed the rendered output in your documentation. Trap: Committing a rendered image without a check means it drifts from its source silently, and readers see a diagram that no longer matches the text that supposedly produced it. A check that re-renders and compares turns that into a failed build rather than a lie in your documentation. Done when: The command renders a directory of sources to images. The check fails when a committed image differs from a fresh render of its source. Editing a source and forgetting to re-render is caught by the check. A diagram change produces a readable text diff in a pull request. Do not build yet: Sharing comes last. ### Phase 6 · Sharing without pretending to collaborate Build: Share by encoding the source into a link, or by publishing the rendered output. State that editing is single user. Keep shared links versioned so an edit after sharing does not silently change what a recipient sees. Trap: Because the source is text, it is tempting to sync it live over a socket, and a naive last-write-wins sync on a text document loses characters when two people type. If you want collaborative text editing, that is a conflict-free data type and a substantial project rather than a socket. Done when: A shared link renders the diagram for someone who has never used the application. Editing after sharing does not change what a recipient already received unless you re-share. The interface states that editing is single user. A link too long for practical use falls back to publishing the rendered output. Do not build yet: Nothing after this. ### Out of scope (and why) - High-fidelity import from and export to proprietary diagram formats, which is reverse engineering with no end. - Multiplayer cursors and comments, which for a text document means collaborative text editing rather than a shared canvas. - The presentation and organisational controls that make the hosted product a team tool. - Layout control beyond what the diagram language offers. If you need to place things exactly, a visual editor is the right tool and this is not it. ### README must contain - That the text is the source of truth and nothing is stored outside it - Which editing model was chosen, and that visual edits round trip through the text if offered - That fonts are embedded so exports match across machines - That the drift check keeps committed images matching their sources - That editing is single user by design ===== AGENTS.md ===== # Agent instructions · Mermaid Chart indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: The diagram language's own renderer, in the browser, TypeScript with Vite, A code editing component with syntax highlighting for the diagram language, Deterministic SVG and PNG rendered from the same source, Files on disk, or the browser with file open and save. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Clearing the preview whenever the source fails to parse means the diagram vanishes on every keystroke that lands mid-token, which is most of them. Keep the last successful render visible, show the error alongside it, and replace only when a new render succeeds. - Showing the renderer's raw error message is the difference between a tool people learn and one they abandon, because those messages are written for the parser rather than the author. Map the common ones to plain wording, and always show the line. - Storing a dragged position in a sidecar file that the diagram language does not know about creates a second source of truth, and the moment someone edits the text in another editor the two disagree with no way to reconcile. Round trip through the text or do not offer the gesture at all. - A diagram whose export depends on the fonts installed locally will render differently in continuous integration than on a laptop, and the difference shows up as a mysteriously changed image in a pull request. Bundle the font, embed it in the SVG, and verify by rendering somewhere with a different font set. - Committing a rendered image without a check means it drifts from its source silently, and readers see a diagram that no longer matches the text that supposedly produced it. A check that re-renders and compares turns that into a failed build rather than a lie in your documentation. ===== BUILD_PLAN.md ===== # Build plan · Mermaid Chart An editor with the diagram source on one side and a rendered view on the other, rendering as you type without losing your place, exporting deterministically so a diagram in a repository produces the same image on any machine, and writing visual adjustments back into the text so there is only ever one source. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Type and see, without losing your place The diagram updates as you type and the view does not jump around. ### Steps 1. Set up the split editor and preview Code editor with syntax highlighting on one side, rendered output on the other. Files: `src/editor.ts` ```sh npm create vite@latest . -- --template vanilla-ts ``` 2. Preserve view state across renders Pan and zoom survive a re-render. Without this, typing is unusable on anything large. Files: `src/preview.ts` 3. Keep the last good render On a parse failure, show the error beside the previous diagram rather than blanking it. Files: `src/render.ts` ### Done when - [ ] Typing does not reset pan or zoom - [ ] A syntax error keeps the last good diagram visible - [ ] The error names a matching line number - [ ] A large diagram re-renders without blocking typing ### Watch out - Clearing the preview whenever the source fails to parse means the diagram vanishes on every keystroke that lands mid-token, which is most of them. Keep the last successful render visible, show the error alongside it, and replace only when a new render succeeds. ## Phase 2 · Errors a person can act on A mistake in the source tells you where it is and what it expected. ### Steps 1. Extract line and position from the error Different diagram types report differently. Normalise into one shape. Files: `src/errors.ts` 2. Highlight in the editor The line and, where known, the column. Reading a line number and scrolling is friction. Files: `src/editor.ts` 3. Map common mistakes to plain wording Five is enough to start. Add one whenever you confuse yourself. Files: `src/error-map.ts` ### Done when - [ ] An unclosed bracket names and highlights its line - [ ] An invalid diagram type lists the valid ones - [ ] Fixing an error re-renders automatically - [ ] Five common mistakes have plainer explanations ### Watch out - Showing the renderer's raw error message is the difference between a tool people learn and one they abandon, because those messages are written for the parser rather than the author. Map the common ones to plain wording, and always show the line. ## Phase 3 · The visual editing decision Either a visual change writes back into the text, or there is no visual change. ### Steps 1. Decide and document Read-only preview is a legitimate and much simpler answer. Write down which you chose and why. Files: `docs/editing-model.md` 2. Implement edits as text transformations If you offer them. The gesture produces a source change and nothing else. Files: `src/visual-edit.ts` 3. Verify nothing lives outside the text Inspect the saved file. Any extra state is the second source of truth this phase prevents. ### Done when - [ ] Every visual edit produces a visible text change, or the preview is read-only - [ ] A visual rename survives a reload - [ ] Nothing is stored outside the source text - [ ] Undo works from the text side ### Watch out - Storing a dragged position in a sidecar file that the diagram language does not know about creates a second source of truth, and the moment someone edits the text in another editor the two disagree with no way to reconcile. Round trip through the text or do not offer the gesture at all. ## Phase 4 · Export that is the same everywhere The same source produces the same image on any machine. ### Steps 1. Bundle and embed the font Rather than relying on what is installed. This is the main source of environment drift. Files: `src/export.ts` 2. Pin layout settings in the document So the file carries everything needed to reproduce its rendering. Files: `src/document.ts` 3. Verify on a second machine Or a container with a different font set. Comparing on the machine that made it proves nothing. ### Done when - [ ] The same source exports identically on two machines - [ ] An exported SVG opens correctly elsewhere - [ ] A two times PNG has double the dimensions and more detail - [ ] Layout settings come from the document rather than the environment ### Watch out - A diagram whose export depends on the fonts installed locally will render differently in continuous integration than on a laptop, and the difference shows up as a mysteriously changed image in a pull request. Bundle the font, embed it in the SVG, and verify by rendering somewhere with a different font set. ## Phase 5 · Diagrams in a repository A diagram lives beside the code it describes and its changes are reviewable. ### Steps 1. Write the batch render command Directory in, images out, deterministic. Suitable for continuous integration. Files: `src/cli.ts` 2. Add a drift check Re-render and compare. Failing the build is what keeps documentation honest. Files: `src/check.ts` 3. Document the embedding pattern How to reference the rendered output from your documentation. Files: `README.md` ### Done when - [ ] The command renders a directory deterministically - [ ] The drift check fails on a stale committed image - [ ] A forgotten re-render is caught - [ ] A diagram change produces a readable text diff ### Watch out - Committing a rendered image without a check means it drifts from its source silently, and readers see a diagram that no longer matches the text that supposedly produced it. A check that re-renders and compares turns that into a failed build rather than a lie in your documentation. ## Not in this build - Import and export of proprietary diagram formats. - Multiplayer editing. - Presentation and organisational controls. - Exact layout control beyond the language. ## After v1, if you want it - A diagram gallery generated from the repository, which makes existing diagrams discoverable - Rendering diagrams from code comments, so an architecture diagram lives next to what it describes - A visual diff of rendered output in pull requests, alongside the text diff ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Pinned, because a renderer update changes layout and therefore every committed image. VITE_RENDERER_VERSION=pinned-in-package-json # Required. Bundled font embedded in exports, with its licence file beside it. VITE_FONT_PATH=/fonts/inter.woff2 # Required. Delay before re-rendering while typing. Longer on large diagrams. VITE_RENDER_DEBOUNCE_MS=250 # Required. Raster scale for PNG export, rendered from vector at that scale rather than upscaled. EXPORT_SCALE=2 # Required. Where the batch command looks for sources and writes images. DIAGRAMS_DIR=./docs/diagrams
You are building a production product version of Mermaid Chart. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== PRODUCT.md ===== # Mermaid Chart · product brief ## Problem Mermaid Chart's solo core is compact: build a browser-based diagram-as-code editor with drag, resize, connect, keyboard shortcuts, autosave, and SVG or PNG export. A competent builder can reach a useful personal version in one sitting, while the paid product mainly wins on interaction polish, collaboration. ## Product outcome A diagram tool whose output belongs in version control, whose images cannot silently drift from their sources, and which never accumulates state the text does not know about. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - Node.js 22 - modern browser - no account required ## Explicit non-goals for v1 - Import and export of proprietary diagram formats. - Multiplayer editing. - Presentation and organisational controls. - Exact layout control beyond the language. - high-fidelity import and export - presentation and enterprise controls - multiplayer cursors and comments - large template libraries ## Success criteria - The same source exports identically in two environments - The drift check fails on a stale committed image - No state is stored outside the source text - A syntax error keeps the last good diagram visible with a matching line number - The single-user limitation is stated in the interface ===== BRIEF.md ===== # Build brief · Mermaid Chart The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a diagram editor where the text is the source of truth and visual edits write back to it to replace Mermaid Chart. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero The text is the diagram. Everything else is a view. That is the whole reason to build this rather than a visual editor: a diagram in text lives in version control, diffs in a pull request, and can be generated by a script. The consequence is the hard part. If you let someone drag a node and store the position somewhere the text does not know about, you now have two sources of truth and they will disagree within a session. Either a visual edit writes back into the text, or there is no visual edit. Decide that in Phase 3 rather than discovering it after building both halves. ### Stack (fixed, do not substitute) - Rendering: The diagram language's own renderer, in the browser · reimplementing the layout of a diagram language is a project of its own and the renderer is the specification - Runtime: TypeScript with Vite · an editor and a preview pane, with no server needed - Editor: A code editing component with syntax highlighting for the diagram language · text editing is solved and the errors it surfaces are half the usability - Export: Deterministic SVG and PNG rendered from the same source · a diagram in a repository must produce the same image in continuous integration as on a laptop - Storage: Files on disk, or the browser with file open and save · the diagram is text and belongs in whatever holds the rest of your text ### Data model (create this before Phase 1) - The source text is the model. These are the derived structures the editor keeps. - documents(id, path, source, updated_at) - parse_result(document_id, ok, ast_json, error_line, error_message, parsed_at) - render_result(document_id, svg, width, height, node_positions_json, rendered_at) - history(document_id, at, source) · text revisions, since the text is what is worth versioning - Node positions come out of the renderer and are never stored as input. If a visual edit needs to change one, it must change the text that produced it. ### Phase 1 · Type and see, without losing your place Build: A split editor and preview. Re-render on a debounce. Preserve the preview's pan and zoom across renders so a small edit does not reset the view. Keep the last good render on screen while the text is temporarily invalid. Trap: Clearing the preview whenever the source fails to parse means the diagram vanishes on every keystroke that lands mid-token, which is most of them. Keep the last successful render visible, show the error alongside it, and replace only when a new render succeeds. Done when: Typing updates the diagram without the view resetting its pan or zoom. A syntax error keeps the last good diagram on screen with the error shown alongside. The error names a line number that matches the source. A large diagram re-renders within a debounce window without blocking typing. Do not build yet: No export, visual editing or version control. ### Phase 2 · Errors a person can act on Build: Surface parse errors with the line, a caret at the position where possible, and the text of what was expected. Highlight the offending line in the editor. Add a small set of common mistakes with a plainer explanation. Trap: Showing the renderer's raw error message is the difference between a tool people learn and one they abandon, because those messages are written for the parser rather than the author. Map the common ones to plain wording, and always show the line. Done when: An unclosed bracket produces an error naming its line and highlighted in the editor. A misspelled diagram type produces a message listing the valid types. Fixing the error re-renders without needing a manual refresh. At least five common mistakes have plainer explanations than the raw message. Do not build yet: No export or visual editing yet. ### Phase 3 · The visual editing decision Build: Pick one. If you allow visual edits, implement them as text transformations: dragging a node reorders or re-parents it in the source, and renaming edits the label in place. If you do not, make the preview explicitly read-only and say so. Trap: Storing a dragged position in a sidecar file that the diagram language does not know about creates a second source of truth, and the moment someone edits the text in another editor the two disagree with no way to reconcile. Round trip through the text or do not offer the gesture at all. Done when: Every visual edit produces a text change visible in the editor, or the preview is read-only and labelled as such. Renaming a node in the preview updates the source text and the change survives a reload. No position or styling is stored outside the source text, verified by inspecting what is saved. Undo works from the text side regardless of where the edit was made. Do not build yet: No export or version control integration yet. ### Phase 4 · Export that is the same everywhere Build: Export SVG and PNG from the same render. Embed or outline fonts. Pin any setting that affects layout. Then render the same file on a second machine and compare the output byte for byte. Trap: A diagram whose export depends on the fonts installed locally will render differently in continuous integration than on a laptop, and the difference shows up as a mysteriously changed image in a pull request. Bundle the font, embed it in the SVG, and verify by rendering somewhere with a different font set. Done when: The same source exports byte-identical SVG on two different machines. An exported SVG opens in a different application with the correct fonts. A PNG at two times scale has exactly double the dimensions and is sharper rather than merely larger. Layout-affecting settings are pinned in the document rather than taken from the environment. Do not build yet: No version control workflow yet. ### Phase 5 · Diagrams in a repository Build: A command that renders a directory of diagram files to images. A check that fails when a committed image does not match its source. Documentation of how to embed the rendered output in your documentation. Trap: Committing a rendered image without a check means it drifts from its source silently, and readers see a diagram that no longer matches the text that supposedly produced it. A check that re-renders and compares turns that into a failed build rather than a lie in your documentation. Done when: The command renders a directory of sources to images. The check fails when a committed image differs from a fresh render of its source. Editing a source and forgetting to re-render is caught by the check. A diagram change produces a readable text diff in a pull request. Do not build yet: Sharing comes last. ### Phase 6 · Sharing without pretending to collaborate Build: Share by encoding the source into a link, or by publishing the rendered output. State that editing is single user. Keep shared links versioned so an edit after sharing does not silently change what a recipient sees. Trap: Because the source is text, it is tempting to sync it live over a socket, and a naive last-write-wins sync on a text document loses characters when two people type. If you want collaborative text editing, that is a conflict-free data type and a substantial project rather than a socket. Done when: A shared link renders the diagram for someone who has never used the application. Editing after sharing does not change what a recipient already received unless you re-share. The interface states that editing is single user. A link too long for practical use falls back to publishing the rendered output. Do not build yet: Nothing after this. ### Out of scope (and why) - High-fidelity import from and export to proprietary diagram formats, which is reverse engineering with no end. - Multiplayer cursors and comments, which for a text document means collaborative text editing rather than a shared canvas. - The presentation and organisational controls that make the hosted product a team tool. - Layout control beyond what the diagram language offers. If you need to place things exactly, a visual editor is the right tool and this is not it. ### README must contain - That the text is the source of truth and nothing is stored outside it - Which editing model was chosen, and that visual edits round trip through the text if offered - That fonts are embedded so exports match across machines - That the drift check keeps committed images matching their sources - That editing is single user by design ===== ARCHITECTURE.md ===== # Architecture · Mermaid Chart ## Stack | Part | Choice | Why | | --- | --- | --- | | Rendering | The diagram language's own renderer, in the browser | reimplementing the layout of a diagram language is a project of its own and the renderer is the specification | | Runtime | TypeScript with Vite | an editor and a preview pane, with no server needed | | Editor | A code editing component with syntax highlighting for the diagram language | text editing is solved and the errors it surfaces are half the usability | | Export | Deterministic SVG and PNG rendered from the same source | a diagram in a repository must produce the same image in continuous integration as on a laptop | | Storage | Files on disk, or the browser with file open and save | the diagram is text and belongs in whatever holds the rest of your text | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Source | The diagram, as text. | Not swappable. Everything else is derived and the moment something is not, there are two sources of truth. | | Render | Turning source into an image. | The language's own renderer. A different one would be a different language. | | Errors | Making a mistake actionable. | Any mapping. Showing the line is the part that matters most. | | Export | Deterministic output across machines. | Any exporter that embeds fonts and pins layout settings. | | Check | Keeping committed images honest. | Any comparison. Failing the build rather than warning is the decision that makes it work. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `VITE_RENDERER_VERSION` · required · Pinned, because a renderer update changes layout and therefore every committed image. - `VITE_FONT_PATH` · required · Bundled font embedded in exports, with its licence file beside it. - `VITE_RENDER_DEBOUNCE_MS` · required · Delay before re-rendering while typing. Longer on large diagrams. - `EXPORT_SCALE` · required · Raster scale for PNG export, rendered from vector at that scale rather than upscaled. - `DIAGRAMS_DIR` · required · Where the batch command looks for sources and writes images. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it. ===== AGENTS.md ===== # Agent instructions · Mermaid Chart product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: The diagram language's own renderer, in the browser, TypeScript with Vite, A code editing component with syntax highlighting for the diagram language, Deterministic SVG and PNG rendered from the same source, Files on disk, or the browser with file open and save. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Clearing the preview whenever the source fails to parse means the diagram vanishes on every keystroke that lands mid-token, which is most of them. Keep the last successful render visible, show the error alongside it, and replace only when a new render succeeds. - Showing the renderer's raw error message is the difference between a tool people learn and one they abandon, because those messages are written for the parser rather than the author. Map the common ones to plain wording, and always show the line. - Storing a dragged position in a sidecar file that the diagram language does not know about creates a second source of truth, and the moment someone edits the text in another editor the two disagree with no way to reconcile. Round trip through the text or do not offer the gesture at all. - A diagram whose export depends on the fonts installed locally will render differently in continuous integration than on a laptop, and the difference shows up as a mysteriously changed image in a pull request. Bundle the font, embed it in the SVG, and verify by rendering somewhere with a different font set. - Committing a rendered image without a check means it drifts from its source silently, and readers see a diagram that no longer matches the text that supposedly produced it. A check that re-renders and compares turns that into a failed build rather than a lie in your documentation. - Because the source is text, it is tempting to sync it live over a socket, and a naive last-write-wins sync on a text document loses characters when two people type. If you want collaborative text editing, that is a conflict-free data type and a substantial project rather than a socket. ===== MILESTONES.md ===== # Delivery milestones · Mermaid Chart Estimated effort: **weekend** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Type and see, without losing your place The diagram updates as you type and the view does not jump around. ### Steps 1. Set up the split editor and preview Code editor with syntax highlighting on one side, rendered output on the other. Files: `src/editor.ts` ```sh npm create vite@latest . -- --template vanilla-ts ``` 2. Preserve view state across renders Pan and zoom survive a re-render. Without this, typing is unusable on anything large. Files: `src/preview.ts` 3. Keep the last good render On a parse failure, show the error beside the previous diagram rather than blanking it. Files: `src/render.ts` ### Done when - [ ] Typing does not reset pan or zoom - [ ] A syntax error keeps the last good diagram visible - [ ] The error names a matching line number - [ ] A large diagram re-renders without blocking typing ### Watch out - Clearing the preview whenever the source fails to parse means the diagram vanishes on every keystroke that lands mid-token, which is most of them. Keep the last successful render visible, show the error alongside it, and replace only when a new render succeeds. ## M2 · Errors a person can act on A mistake in the source tells you where it is and what it expected. ### Steps 1. Extract line and position from the error Different diagram types report differently. Normalise into one shape. Files: `src/errors.ts` 2. Highlight in the editor The line and, where known, the column. Reading a line number and scrolling is friction. Files: `src/editor.ts` 3. Map common mistakes to plain wording Five is enough to start. Add one whenever you confuse yourself. Files: `src/error-map.ts` ### Done when - [ ] An unclosed bracket names and highlights its line - [ ] An invalid diagram type lists the valid ones - [ ] Fixing an error re-renders automatically - [ ] Five common mistakes have plainer explanations ### Watch out - Showing the renderer's raw error message is the difference between a tool people learn and one they abandon, because those messages are written for the parser rather than the author. Map the common ones to plain wording, and always show the line. ## M3 · The visual editing decision Either a visual change writes back into the text, or there is no visual change. ### Steps 1. Decide and document Read-only preview is a legitimate and much simpler answer. Write down which you chose and why. Files: `docs/editing-model.md` 2. Implement edits as text transformations If you offer them. The gesture produces a source change and nothing else. Files: `src/visual-edit.ts` 3. Verify nothing lives outside the text Inspect the saved file. Any extra state is the second source of truth this phase prevents. ### Done when - [ ] Every visual edit produces a visible text change, or the preview is read-only - [ ] A visual rename survives a reload - [ ] Nothing is stored outside the source text - [ ] Undo works from the text side ### Watch out - Storing a dragged position in a sidecar file that the diagram language does not know about creates a second source of truth, and the moment someone edits the text in another editor the two disagree with no way to reconcile. Round trip through the text or do not offer the gesture at all. ## M4 · Export that is the same everywhere The same source produces the same image on any machine. ### Steps 1. Bundle and embed the font Rather than relying on what is installed. This is the main source of environment drift. Files: `src/export.ts` 2. Pin layout settings in the document So the file carries everything needed to reproduce its rendering. Files: `src/document.ts` 3. Verify on a second machine Or a container with a different font set. Comparing on the machine that made it proves nothing. ### Done when - [ ] The same source exports identically on two machines - [ ] An exported SVG opens correctly elsewhere - [ ] A two times PNG has double the dimensions and more detail - [ ] Layout settings come from the document rather than the environment ### Watch out - A diagram whose export depends on the fonts installed locally will render differently in continuous integration than on a laptop, and the difference shows up as a mysteriously changed image in a pull request. Bundle the font, embed it in the SVG, and verify by rendering somewhere with a different font set. ## M5 · Diagrams in a repository A diagram lives beside the code it describes and its changes are reviewable. ### Steps 1. Write the batch render command Directory in, images out, deterministic. Suitable for continuous integration. Files: `src/cli.ts` 2. Add a drift check Re-render and compare. Failing the build is what keeps documentation honest. Files: `src/check.ts` 3. Document the embedding pattern How to reference the rendered output from your documentation. Files: `README.md` ### Done when - [ ] The command renders a directory deterministically - [ ] The drift check fails on a stale committed image - [ ] A forgotten re-render is caught - [ ] A diagram change produces a readable text diff ### Watch out - Committing a rendered image without a check means it drifts from its source silently, and readers see a diagram that no longer matches the text that supposedly produced it. A check that re-renders and compares turns that into a failed build rather than a lie in your documentation. ## M6 · Sharing without pretending to collaborate (production only) Someone can view a diagram, and you have not implied multiplayer. ### Steps 1. Encode the source into a link Compressed. It is text, which makes this genuinely practical for most diagrams. Files: `src/share.ts` 2. Version shared links So a later edit does not change what someone already has. Files: `src/share.ts` 3. Say editing is single user In the interface. Silence reads as a promise about collaboration you did not make. Files: `README.md` ### Done when - [ ] A shared link renders for a first-time visitor - [ ] Editing after sharing does not change the shared version - [ ] The single-user limitation is stated in the interface - [ ] An overlong link falls back to publishing output ### Watch out - Because the source is text, it is tempting to sync it live over a socket, and a naive last-write-wins sync on a text document loses characters when two people type. If you want collaborative text editing, that is a conflict-free data type and a substantial project rather than a socket. ===== OPERATIONS.md ===== # Operations · Mermaid Chart ## Backup The sources are text and belong in version control with everything else, which is most of the point. Rendered images are derived and could be excluded, though committing them plus the drift check is usually the more practical choice. ## Restore Check out the repository and run the batch render. If the output matches what was committed, the restore is complete and the check has proved it. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Render time on your largest diagram and the drift check in continuous integration. A renderer upgrade changing layout will fail the check across every diagram at once, which is the correct and useful outcome. ## Incident checklist A renderer version change is the recurring event and it invalidates every committed image. Pin the version, treat an upgrade as a deliberate change with a large diff, and review the rendered differences rather than accepting them blindly. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] The same source exports identically in two environments - [ ] The drift check fails on a stale committed image - [ ] No state is stored outside the source text - [ ] A syntax error keeps the last good diagram visible with a matching line number - [ ] The single-user limitation is stated in the interface ## Launch constraint Do not market omitted Mermaid Chart capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Pinned, because a renderer update changes layout and therefore every committed image. VITE_RENDERER_VERSION=pinned-in-package-json # Required. Bundled font embedded in exports, with its licence file beside it. VITE_FONT_PATH=/fonts/inter.woff2 # Required. Delay before re-rendering while typing. Longer on large diagrams. VITE_RENDER_DEBOUNCE_MS=250 # Required. Raster scale for PNG export, rendered from vector at that scale rather than upscaled. EXPORT_SCALE=2 # Required. Where the batch command looks for sources and writes images. DIAGRAMS_DIR=./docs/diagrams
# Mermaid Chart · indie build An editor with the diagram source on one side and a rendered view on the other, rendering as you type without losing your place, exporting deterministically so a diagram in a repository produces the same image on any machine, and writing visual adjustments back into the text so there is only ever one source. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Rendering | The diagram language's own renderer, in the browser | reimplementing the layout of a diagram language is a project of its own and the renderer is the specification | | Runtime | TypeScript with Vite | an editor and a preview pane, with no server needed | | Editor | A code editing component with syntax highlighting for the diagram language | text editing is solved and the errors it surfaces are half the usability | | Export | Deterministic SVG and PNG rendered from the same source | a diagram in a repository must produce the same image in continuous integration as on a laptop | | Storage | Files on disk, or the browser with file open and save | the diagram is text and belongs in whatever holds the rest of your text | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the build and the batch render command. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **The diagram language's renderer** · free - Why: It is the specification of the language. Reimplementing its layout is a separate project and would diverge immediately. - Get it: Install the renderer as a dependency. Pin the version, since a renderer update changes layout and therefore every committed image. - Verify: A sample diagram renders to SVG - [ ] **A font you may embed** · free with an open licence - Why: Embedding is what makes exports identical across machines, and embedding is redistribution which not every licence permits. - Get it: An openly licensed family with its licence file kept alongside. - Verify: The licence permits embedding and the exported SVG carries the font - [ ] **A second machine or container with different fonts** · free - Why: Phase 4's determinism check is meaningless on the machine that produced the reference, since it has exactly the fonts that were used. - Get it: A container image with a minimal font set is the easiest way, and it is also what your continuous integration will look like. - Verify: The same source exports identically in both environments - [ ] **Real diagrams from your own documentation** · free - Why: Error handling and re-render performance both need real sources, which are longer and messier than examples and contain the syntax you actually get wrong. - Get it: Take the diagrams from a project you maintain, or write the ones you have been meaning to. - Verify: At least five real diagrams render, including one large one ## Quick start ```sh npm create vite@latest . -- --template vanilla-ts ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Import and export of proprietary diagram formats. - Multiplayer editing. - Presentation and organisational controls. - Exact layout control beyond the language. - high-fidelity import and export - presentation and enterprise controls - multiplayer cursors and comments - large template libraries If one of those is essential to you, that is the reason to keep paying for Mermaid Chart, and the README should say so rather than pretend.
# Build brief · Mermaid Chart The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a diagram editor where the text is the source of truth and visual edits write back to it to replace Mermaid Chart. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero The text is the diagram. Everything else is a view. That is the whole reason to build this rather than a visual editor: a diagram in text lives in version control, diffs in a pull request, and can be generated by a script. The consequence is the hard part. If you let someone drag a node and store the position somewhere the text does not know about, you now have two sources of truth and they will disagree within a session. Either a visual edit writes back into the text, or there is no visual edit. Decide that in Phase 3 rather than discovering it after building both halves. ### Stack (fixed, do not substitute) - Rendering: The diagram language's own renderer, in the browser · reimplementing the layout of a diagram language is a project of its own and the renderer is the specification - Runtime: TypeScript with Vite · an editor and a preview pane, with no server needed - Editor: A code editing component with syntax highlighting for the diagram language · text editing is solved and the errors it surfaces are half the usability - Export: Deterministic SVG and PNG rendered from the same source · a diagram in a repository must produce the same image in continuous integration as on a laptop - Storage: Files on disk, or the browser with file open and save · the diagram is text and belongs in whatever holds the rest of your text ### Data model (create this before Phase 1) - The source text is the model. These are the derived structures the editor keeps. - documents(id, path, source, updated_at) - parse_result(document_id, ok, ast_json, error_line, error_message, parsed_at) - render_result(document_id, svg, width, height, node_positions_json, rendered_at) - history(document_id, at, source) · text revisions, since the text is what is worth versioning - Node positions come out of the renderer and are never stored as input. If a visual edit needs to change one, it must change the text that produced it. ### Phase 1 · Type and see, without losing your place Build: A split editor and preview. Re-render on a debounce. Preserve the preview's pan and zoom across renders so a small edit does not reset the view. Keep the last good render on screen while the text is temporarily invalid. Trap: Clearing the preview whenever the source fails to parse means the diagram vanishes on every keystroke that lands mid-token, which is most of them. Keep the last successful render visible, show the error alongside it, and replace only when a new render succeeds. Done when: Typing updates the diagram without the view resetting its pan or zoom. A syntax error keeps the last good diagram on screen with the error shown alongside. The error names a line number that matches the source. A large diagram re-renders within a debounce window without blocking typing. Do not build yet: No export, visual editing or version control. ### Phase 2 · Errors a person can act on Build: Surface parse errors with the line, a caret at the position where possible, and the text of what was expected. Highlight the offending line in the editor. Add a small set of common mistakes with a plainer explanation. Trap: Showing the renderer's raw error message is the difference between a tool people learn and one they abandon, because those messages are written for the parser rather than the author. Map the common ones to plain wording, and always show the line. Done when: An unclosed bracket produces an error naming its line and highlighted in the editor. A misspelled diagram type produces a message listing the valid types. Fixing the error re-renders without needing a manual refresh. At least five common mistakes have plainer explanations than the raw message. Do not build yet: No export or visual editing yet. ### Phase 3 · The visual editing decision Build: Pick one. If you allow visual edits, implement them as text transformations: dragging a node reorders or re-parents it in the source, and renaming edits the label in place. If you do not, make the preview explicitly read-only and say so. Trap: Storing a dragged position in a sidecar file that the diagram language does not know about creates a second source of truth, and the moment someone edits the text in another editor the two disagree with no way to reconcile. Round trip through the text or do not offer the gesture at all. Done when: Every visual edit produces a text change visible in the editor, or the preview is read-only and labelled as such. Renaming a node in the preview updates the source text and the change survives a reload. No position or styling is stored outside the source text, verified by inspecting what is saved. Undo works from the text side regardless of where the edit was made. Do not build yet: No export or version control integration yet. ### Phase 4 · Export that is the same everywhere Build: Export SVG and PNG from the same render. Embed or outline fonts. Pin any setting that affects layout. Then render the same file on a second machine and compare the output byte for byte. Trap: A diagram whose export depends on the fonts installed locally will render differently in continuous integration than on a laptop, and the difference shows up as a mysteriously changed image in a pull request. Bundle the font, embed it in the SVG, and verify by rendering somewhere with a different font set. Done when: The same source exports byte-identical SVG on two different machines. An exported SVG opens in a different application with the correct fonts. A PNG at two times scale has exactly double the dimensions and is sharper rather than merely larger. Layout-affecting settings are pinned in the document rather than taken from the environment. Do not build yet: No version control workflow yet. ### Phase 5 · Diagrams in a repository Build: A command that renders a directory of diagram files to images. A check that fails when a committed image does not match its source. Documentation of how to embed the rendered output in your documentation. Trap: Committing a rendered image without a check means it drifts from its source silently, and readers see a diagram that no longer matches the text that supposedly produced it. A check that re-renders and compares turns that into a failed build rather than a lie in your documentation. Done when: The command renders a directory of sources to images. The check fails when a committed image differs from a fresh render of its source. Editing a source and forgetting to re-render is caught by the check. A diagram change produces a readable text diff in a pull request. Do not build yet: Sharing comes last. ### Phase 6 · Sharing without pretending to collaborate Build: Share by encoding the source into a link, or by publishing the rendered output. State that editing is single user. Keep shared links versioned so an edit after sharing does not silently change what a recipient sees. Trap: Because the source is text, it is tempting to sync it live over a socket, and a naive last-write-wins sync on a text document loses characters when two people type. If you want collaborative text editing, that is a conflict-free data type and a substantial project rather than a socket. Done when: A shared link renders the diagram for someone who has never used the application. Editing after sharing does not change what a recipient already received unless you re-share. The interface states that editing is single user. A link too long for practical use falls back to publishing the rendered output. Do not build yet: Nothing after this. ### Out of scope (and why) - High-fidelity import from and export to proprietary diagram formats, which is reverse engineering with no end. - Multiplayer cursors and comments, which for a text document means collaborative text editing rather than a shared canvas. - The presentation and organisational controls that make the hosted product a team tool. - Layout control beyond what the diagram language offers. If you need to place things exactly, a visual editor is the right tool and this is not it. ### README must contain - That the text is the source of truth and nothing is stored outside it - Which editing model was chosen, and that visual edits round trip through the text if offered - That fonts are embedded so exports match across machines - That the drift check keeps committed images matching their sources - That editing is single user by design
# Agent instructions · Mermaid Chart indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: The diagram language's own renderer, in the browser, TypeScript with Vite, A code editing component with syntax highlighting for the diagram language, Deterministic SVG and PNG rendered from the same source, Files on disk, or the browser with file open and save. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Clearing the preview whenever the source fails to parse means the diagram vanishes on every keystroke that lands mid-token, which is most of them. Keep the last successful render visible, show the error alongside it, and replace only when a new render succeeds. - Showing the renderer's raw error message is the difference between a tool people learn and one they abandon, because those messages are written for the parser rather than the author. Map the common ones to plain wording, and always show the line. - Storing a dragged position in a sidecar file that the diagram language does not know about creates a second source of truth, and the moment someone edits the text in another editor the two disagree with no way to reconcile. Round trip through the text or do not offer the gesture at all. - A diagram whose export depends on the fonts installed locally will render differently in continuous integration than on a laptop, and the difference shows up as a mysteriously changed image in a pull request. Bundle the font, embed it in the SVG, and verify by rendering somewhere with a different font set. - Committing a rendered image without a check means it drifts from its source silently, and readers see a diagram that no longer matches the text that supposedly produced it. A check that re-renders and compares turns that into a failed build rather than a lie in your documentation.
# Build plan · Mermaid Chart An editor with the diagram source on one side and a rendered view on the other, rendering as you type without losing your place, exporting deterministically so a diagram in a repository produces the same image on any machine, and writing visual adjustments back into the text so there is only ever one source. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Type and see, without losing your place The diagram updates as you type and the view does not jump around. ### Steps 1. Set up the split editor and preview Code editor with syntax highlighting on one side, rendered output on the other. Files: `src/editor.ts` ```sh npm create vite@latest . -- --template vanilla-ts ``` 2. Preserve view state across renders Pan and zoom survive a re-render. Without this, typing is unusable on anything large. Files: `src/preview.ts` 3. Keep the last good render On a parse failure, show the error beside the previous diagram rather than blanking it. Files: `src/render.ts` ### Done when - [ ] Typing does not reset pan or zoom - [ ] A syntax error keeps the last good diagram visible - [ ] The error names a matching line number - [ ] A large diagram re-renders without blocking typing ### Watch out - Clearing the preview whenever the source fails to parse means the diagram vanishes on every keystroke that lands mid-token, which is most of them. Keep the last successful render visible, show the error alongside it, and replace only when a new render succeeds. ## Phase 2 · Errors a person can act on A mistake in the source tells you where it is and what it expected. ### Steps 1. Extract line and position from the error Different diagram types report differently. Normalise into one shape. Files: `src/errors.ts` 2. Highlight in the editor The line and, where known, the column. Reading a line number and scrolling is friction. Files: `src/editor.ts` 3. Map common mistakes to plain wording Five is enough to start. Add one whenever you confuse yourself. Files: `src/error-map.ts` ### Done when - [ ] An unclosed bracket names and highlights its line - [ ] An invalid diagram type lists the valid ones - [ ] Fixing an error re-renders automatically - [ ] Five common mistakes have plainer explanations ### Watch out - Showing the renderer's raw error message is the difference between a tool people learn and one they abandon, because those messages are written for the parser rather than the author. Map the common ones to plain wording, and always show the line. ## Phase 3 · The visual editing decision Either a visual change writes back into the text, or there is no visual change. ### Steps 1. Decide and document Read-only preview is a legitimate and much simpler answer. Write down which you chose and why. Files: `docs/editing-model.md` 2. Implement edits as text transformations If you offer them. The gesture produces a source change and nothing else. Files: `src/visual-edit.ts` 3. Verify nothing lives outside the text Inspect the saved file. Any extra state is the second source of truth this phase prevents. ### Done when - [ ] Every visual edit produces a visible text change, or the preview is read-only - [ ] A visual rename survives a reload - [ ] Nothing is stored outside the source text - [ ] Undo works from the text side ### Watch out - Storing a dragged position in a sidecar file that the diagram language does not know about creates a second source of truth, and the moment someone edits the text in another editor the two disagree with no way to reconcile. Round trip through the text or do not offer the gesture at all. ## Phase 4 · Export that is the same everywhere The same source produces the same image on any machine. ### Steps 1. Bundle and embed the font Rather than relying on what is installed. This is the main source of environment drift. Files: `src/export.ts` 2. Pin layout settings in the document So the file carries everything needed to reproduce its rendering. Files: `src/document.ts` 3. Verify on a second machine Or a container with a different font set. Comparing on the machine that made it proves nothing. ### Done when - [ ] The same source exports identically on two machines - [ ] An exported SVG opens correctly elsewhere - [ ] A two times PNG has double the dimensions and more detail - [ ] Layout settings come from the document rather than the environment ### Watch out - A diagram whose export depends on the fonts installed locally will render differently in continuous integration than on a laptop, and the difference shows up as a mysteriously changed image in a pull request. Bundle the font, embed it in the SVG, and verify by rendering somewhere with a different font set. ## Phase 5 · Diagrams in a repository A diagram lives beside the code it describes and its changes are reviewable. ### Steps 1. Write the batch render command Directory in, images out, deterministic. Suitable for continuous integration. Files: `src/cli.ts` 2. Add a drift check Re-render and compare. Failing the build is what keeps documentation honest. Files: `src/check.ts` 3. Document the embedding pattern How to reference the rendered output from your documentation. Files: `README.md` ### Done when - [ ] The command renders a directory deterministically - [ ] The drift check fails on a stale committed image - [ ] A forgotten re-render is caught - [ ] A diagram change produces a readable text diff ### Watch out - Committing a rendered image without a check means it drifts from its source silently, and readers see a diagram that no longer matches the text that supposedly produced it. A check that re-renders and compares turns that into a failed build rather than a lie in your documentation. ## Not in this build - Import and export of proprietary diagram formats. - Multiplayer editing. - Presentation and organisational controls. - Exact layout control beyond the language. ## After v1, if you want it - A diagram gallery generated from the repository, which makes existing diagrams discoverable - Rendering diagrams from code comments, so an architecture diagram lives next to what it describes - A visual diff of rendered output in pull requests, alongside the text diff
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Pinned, because a renderer update changes layout and therefore every committed image. VITE_RENDERER_VERSION=pinned-in-package-json # Required. Bundled font embedded in exports, with its licence file beside it. VITE_FONT_PATH=/fonts/inter.woff2 # Required. Delay before re-rendering while typing. Longer on large diagrams. VITE_RENDER_DEBOUNCE_MS=250 # Required. Raster scale for PNG export, rendered from vector at that scale rather than upscaled. EXPORT_SCALE=2 # Required. Where the batch command looks for sources and writes images. DIAGRAMS_DIR=./docs/diagrams
# Mermaid Chart · product brief ## Problem Mermaid Chart's solo core is compact: build a browser-based diagram-as-code editor with drag, resize, connect, keyboard shortcuts, autosave, and SVG or PNG export. A competent builder can reach a useful personal version in one sitting, while the paid product mainly wins on interaction polish, collaboration. ## Product outcome A diagram tool whose output belongs in version control, whose images cannot silently drift from their sources, and which never accumulates state the text does not know about. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - Node.js 22 - modern browser - no account required ## Explicit non-goals for v1 - Import and export of proprietary diagram formats. - Multiplayer editing. - Presentation and organisational controls. - Exact layout control beyond the language. - high-fidelity import and export - presentation and enterprise controls - multiplayer cursors and comments - large template libraries ## Success criteria - The same source exports identically in two environments - The drift check fails on a stale committed image - No state is stored outside the source text - A syntax error keeps the last good diagram visible with a matching line number - The single-user limitation is stated in the interface
# Build brief · Mermaid Chart The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a diagram editor where the text is the source of truth and visual edits write back to it to replace Mermaid Chart. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero The text is the diagram. Everything else is a view. That is the whole reason to build this rather than a visual editor: a diagram in text lives in version control, diffs in a pull request, and can be generated by a script. The consequence is the hard part. If you let someone drag a node and store the position somewhere the text does not know about, you now have two sources of truth and they will disagree within a session. Either a visual edit writes back into the text, or there is no visual edit. Decide that in Phase 3 rather than discovering it after building both halves. ### Stack (fixed, do not substitute) - Rendering: The diagram language's own renderer, in the browser · reimplementing the layout of a diagram language is a project of its own and the renderer is the specification - Runtime: TypeScript with Vite · an editor and a preview pane, with no server needed - Editor: A code editing component with syntax highlighting for the diagram language · text editing is solved and the errors it surfaces are half the usability - Export: Deterministic SVG and PNG rendered from the same source · a diagram in a repository must produce the same image in continuous integration as on a laptop - Storage: Files on disk, or the browser with file open and save · the diagram is text and belongs in whatever holds the rest of your text ### Data model (create this before Phase 1) - The source text is the model. These are the derived structures the editor keeps. - documents(id, path, source, updated_at) - parse_result(document_id, ok, ast_json, error_line, error_message, parsed_at) - render_result(document_id, svg, width, height, node_positions_json, rendered_at) - history(document_id, at, source) · text revisions, since the text is what is worth versioning - Node positions come out of the renderer and are never stored as input. If a visual edit needs to change one, it must change the text that produced it. ### Phase 1 · Type and see, without losing your place Build: A split editor and preview. Re-render on a debounce. Preserve the preview's pan and zoom across renders so a small edit does not reset the view. Keep the last good render on screen while the text is temporarily invalid. Trap: Clearing the preview whenever the source fails to parse means the diagram vanishes on every keystroke that lands mid-token, which is most of them. Keep the last successful render visible, show the error alongside it, and replace only when a new render succeeds. Done when: Typing updates the diagram without the view resetting its pan or zoom. A syntax error keeps the last good diagram on screen with the error shown alongside. The error names a line number that matches the source. A large diagram re-renders within a debounce window without blocking typing. Do not build yet: No export, visual editing or version control. ### Phase 2 · Errors a person can act on Build: Surface parse errors with the line, a caret at the position where possible, and the text of what was expected. Highlight the offending line in the editor. Add a small set of common mistakes with a plainer explanation. Trap: Showing the renderer's raw error message is the difference between a tool people learn and one they abandon, because those messages are written for the parser rather than the author. Map the common ones to plain wording, and always show the line. Done when: An unclosed bracket produces an error naming its line and highlighted in the editor. A misspelled diagram type produces a message listing the valid types. Fixing the error re-renders without needing a manual refresh. At least five common mistakes have plainer explanations than the raw message. Do not build yet: No export or visual editing yet. ### Phase 3 · The visual editing decision Build: Pick one. If you allow visual edits, implement them as text transformations: dragging a node reorders or re-parents it in the source, and renaming edits the label in place. If you do not, make the preview explicitly read-only and say so. Trap: Storing a dragged position in a sidecar file that the diagram language does not know about creates a second source of truth, and the moment someone edits the text in another editor the two disagree with no way to reconcile. Round trip through the text or do not offer the gesture at all. Done when: Every visual edit produces a text change visible in the editor, or the preview is read-only and labelled as such. Renaming a node in the preview updates the source text and the change survives a reload. No position or styling is stored outside the source text, verified by inspecting what is saved. Undo works from the text side regardless of where the edit was made. Do not build yet: No export or version control integration yet. ### Phase 4 · Export that is the same everywhere Build: Export SVG and PNG from the same render. Embed or outline fonts. Pin any setting that affects layout. Then render the same file on a second machine and compare the output byte for byte. Trap: A diagram whose export depends on the fonts installed locally will render differently in continuous integration than on a laptop, and the difference shows up as a mysteriously changed image in a pull request. Bundle the font, embed it in the SVG, and verify by rendering somewhere with a different font set. Done when: The same source exports byte-identical SVG on two different machines. An exported SVG opens in a different application with the correct fonts. A PNG at two times scale has exactly double the dimensions and is sharper rather than merely larger. Layout-affecting settings are pinned in the document rather than taken from the environment. Do not build yet: No version control workflow yet. ### Phase 5 · Diagrams in a repository Build: A command that renders a directory of diagram files to images. A check that fails when a committed image does not match its source. Documentation of how to embed the rendered output in your documentation. Trap: Committing a rendered image without a check means it drifts from its source silently, and readers see a diagram that no longer matches the text that supposedly produced it. A check that re-renders and compares turns that into a failed build rather than a lie in your documentation. Done when: The command renders a directory of sources to images. The check fails when a committed image differs from a fresh render of its source. Editing a source and forgetting to re-render is caught by the check. A diagram change produces a readable text diff in a pull request. Do not build yet: Sharing comes last. ### Phase 6 · Sharing without pretending to collaborate Build: Share by encoding the source into a link, or by publishing the rendered output. State that editing is single user. Keep shared links versioned so an edit after sharing does not silently change what a recipient sees. Trap: Because the source is text, it is tempting to sync it live over a socket, and a naive last-write-wins sync on a text document loses characters when two people type. If you want collaborative text editing, that is a conflict-free data type and a substantial project rather than a socket. Done when: A shared link renders the diagram for someone who has never used the application. Editing after sharing does not change what a recipient already received unless you re-share. The interface states that editing is single user. A link too long for practical use falls back to publishing the rendered output. Do not build yet: Nothing after this. ### Out of scope (and why) - High-fidelity import from and export to proprietary diagram formats, which is reverse engineering with no end. - Multiplayer cursors and comments, which for a text document means collaborative text editing rather than a shared canvas. - The presentation and organisational controls that make the hosted product a team tool. - Layout control beyond what the diagram language offers. If you need to place things exactly, a visual editor is the right tool and this is not it. ### README must contain - That the text is the source of truth and nothing is stored outside it - Which editing model was chosen, and that visual edits round trip through the text if offered - That fonts are embedded so exports match across machines - That the drift check keeps committed images matching their sources - That editing is single user by design
# Architecture · Mermaid Chart ## Stack | Part | Choice | Why | | --- | --- | --- | | Rendering | The diagram language's own renderer, in the browser | reimplementing the layout of a diagram language is a project of its own and the renderer is the specification | | Runtime | TypeScript with Vite | an editor and a preview pane, with no server needed | | Editor | A code editing component with syntax highlighting for the diagram language | text editing is solved and the errors it surfaces are half the usability | | Export | Deterministic SVG and PNG rendered from the same source | a diagram in a repository must produce the same image in continuous integration as on a laptop | | Storage | Files on disk, or the browser with file open and save | the diagram is text and belongs in whatever holds the rest of your text | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Source | The diagram, as text. | Not swappable. Everything else is derived and the moment something is not, there are two sources of truth. | | Render | Turning source into an image. | The language's own renderer. A different one would be a different language. | | Errors | Making a mistake actionable. | Any mapping. Showing the line is the part that matters most. | | Export | Deterministic output across machines. | Any exporter that embeds fonts and pins layout settings. | | Check | Keeping committed images honest. | Any comparison. Failing the build rather than warning is the decision that makes it work. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `VITE_RENDERER_VERSION` · required · Pinned, because a renderer update changes layout and therefore every committed image. - `VITE_FONT_PATH` · required · Bundled font embedded in exports, with its licence file beside it. - `VITE_RENDER_DEBOUNCE_MS` · required · Delay before re-rendering while typing. Longer on large diagrams. - `EXPORT_SCALE` · required · Raster scale for PNG export, rendered from vector at that scale rather than upscaled. - `DIAGRAMS_DIR` · required · Where the batch command looks for sources and writes images. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it.
# Agent instructions · Mermaid Chart product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: The diagram language's own renderer, in the browser, TypeScript with Vite, A code editing component with syntax highlighting for the diagram language, Deterministic SVG and PNG rendered from the same source, Files on disk, or the browser with file open and save. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Clearing the preview whenever the source fails to parse means the diagram vanishes on every keystroke that lands mid-token, which is most of them. Keep the last successful render visible, show the error alongside it, and replace only when a new render succeeds. - Showing the renderer's raw error message is the difference between a tool people learn and one they abandon, because those messages are written for the parser rather than the author. Map the common ones to plain wording, and always show the line. - Storing a dragged position in a sidecar file that the diagram language does not know about creates a second source of truth, and the moment someone edits the text in another editor the two disagree with no way to reconcile. Round trip through the text or do not offer the gesture at all. - A diagram whose export depends on the fonts installed locally will render differently in continuous integration than on a laptop, and the difference shows up as a mysteriously changed image in a pull request. Bundle the font, embed it in the SVG, and verify by rendering somewhere with a different font set. - Committing a rendered image without a check means it drifts from its source silently, and readers see a diagram that no longer matches the text that supposedly produced it. A check that re-renders and compares turns that into a failed build rather than a lie in your documentation. - Because the source is text, it is tempting to sync it live over a socket, and a naive last-write-wins sync on a text document loses characters when two people type. If you want collaborative text editing, that is a conflict-free data type and a substantial project rather than a socket.
# Delivery milestones · Mermaid Chart Estimated effort: **weekend** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Type and see, without losing your place The diagram updates as you type and the view does not jump around. ### Steps 1. Set up the split editor and preview Code editor with syntax highlighting on one side, rendered output on the other. Files: `src/editor.ts` ```sh npm create vite@latest . -- --template vanilla-ts ``` 2. Preserve view state across renders Pan and zoom survive a re-render. Without this, typing is unusable on anything large. Files: `src/preview.ts` 3. Keep the last good render On a parse failure, show the error beside the previous diagram rather than blanking it. Files: `src/render.ts` ### Done when - [ ] Typing does not reset pan or zoom - [ ] A syntax error keeps the last good diagram visible - [ ] The error names a matching line number - [ ] A large diagram re-renders without blocking typing ### Watch out - Clearing the preview whenever the source fails to parse means the diagram vanishes on every keystroke that lands mid-token, which is most of them. Keep the last successful render visible, show the error alongside it, and replace only when a new render succeeds. ## M2 · Errors a person can act on A mistake in the source tells you where it is and what it expected. ### Steps 1. Extract line and position from the error Different diagram types report differently. Normalise into one shape. Files: `src/errors.ts` 2. Highlight in the editor The line and, where known, the column. Reading a line number and scrolling is friction. Files: `src/editor.ts` 3. Map common mistakes to plain wording Five is enough to start. Add one whenever you confuse yourself. Files: `src/error-map.ts` ### Done when - [ ] An unclosed bracket names and highlights its line - [ ] An invalid diagram type lists the valid ones - [ ] Fixing an error re-renders automatically - [ ] Five common mistakes have plainer explanations ### Watch out - Showing the renderer's raw error message is the difference between a tool people learn and one they abandon, because those messages are written for the parser rather than the author. Map the common ones to plain wording, and always show the line. ## M3 · The visual editing decision Either a visual change writes back into the text, or there is no visual change. ### Steps 1. Decide and document Read-only preview is a legitimate and much simpler answer. Write down which you chose and why. Files: `docs/editing-model.md` 2. Implement edits as text transformations If you offer them. The gesture produces a source change and nothing else. Files: `src/visual-edit.ts` 3. Verify nothing lives outside the text Inspect the saved file. Any extra state is the second source of truth this phase prevents. ### Done when - [ ] Every visual edit produces a visible text change, or the preview is read-only - [ ] A visual rename survives a reload - [ ] Nothing is stored outside the source text - [ ] Undo works from the text side ### Watch out - Storing a dragged position in a sidecar file that the diagram language does not know about creates a second source of truth, and the moment someone edits the text in another editor the two disagree with no way to reconcile. Round trip through the text or do not offer the gesture at all. ## M4 · Export that is the same everywhere The same source produces the same image on any machine. ### Steps 1. Bundle and embed the font Rather than relying on what is installed. This is the main source of environment drift. Files: `src/export.ts` 2. Pin layout settings in the document So the file carries everything needed to reproduce its rendering. Files: `src/document.ts` 3. Verify on a second machine Or a container with a different font set. Comparing on the machine that made it proves nothing. ### Done when - [ ] The same source exports identically on two machines - [ ] An exported SVG opens correctly elsewhere - [ ] A two times PNG has double the dimensions and more detail - [ ] Layout settings come from the document rather than the environment ### Watch out - A diagram whose export depends on the fonts installed locally will render differently in continuous integration than on a laptop, and the difference shows up as a mysteriously changed image in a pull request. Bundle the font, embed it in the SVG, and verify by rendering somewhere with a different font set. ## M5 · Diagrams in a repository A diagram lives beside the code it describes and its changes are reviewable. ### Steps 1. Write the batch render command Directory in, images out, deterministic. Suitable for continuous integration. Files: `src/cli.ts` 2. Add a drift check Re-render and compare. Failing the build is what keeps documentation honest. Files: `src/check.ts` 3. Document the embedding pattern How to reference the rendered output from your documentation. Files: `README.md` ### Done when - [ ] The command renders a directory deterministically - [ ] The drift check fails on a stale committed image - [ ] A forgotten re-render is caught - [ ] A diagram change produces a readable text diff ### Watch out - Committing a rendered image without a check means it drifts from its source silently, and readers see a diagram that no longer matches the text that supposedly produced it. A check that re-renders and compares turns that into a failed build rather than a lie in your documentation. ## M6 · Sharing without pretending to collaborate (production only) Someone can view a diagram, and you have not implied multiplayer. ### Steps 1. Encode the source into a link Compressed. It is text, which makes this genuinely practical for most diagrams. Files: `src/share.ts` 2. Version shared links So a later edit does not change what someone already has. Files: `src/share.ts` 3. Say editing is single user In the interface. Silence reads as a promise about collaboration you did not make. Files: `README.md` ### Done when - [ ] A shared link renders for a first-time visitor - [ ] Editing after sharing does not change the shared version - [ ] The single-user limitation is stated in the interface - [ ] An overlong link falls back to publishing output ### Watch out - Because the source is text, it is tempting to sync it live over a socket, and a naive last-write-wins sync on a text document loses characters when two people type. If you want collaborative text editing, that is a conflict-free data type and a substantial project rather than a socket.
# Operations · Mermaid Chart ## Backup The sources are text and belong in version control with everything else, which is most of the point. Rendered images are derived and could be excluded, though committing them plus the drift check is usually the more practical choice. ## Restore Check out the repository and run the batch render. If the output matches what was committed, the restore is complete and the check has proved it. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Render time on your largest diagram and the drift check in continuous integration. A renderer upgrade changing layout will fail the check across every diagram at once, which is the correct and useful outcome. ## Incident checklist A renderer version change is the recurring event and it invalidates every committed image. Pin the version, treat an upgrade as a deliberate change with a large diff, and review the rendered differences rather than accepting them blindly. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] The same source exports identically in two environments - [ ] The drift check fails on a stale committed image - [ ] No state is stored outside the source text - [ ] A syntax error keeps the last good diagram visible with a matching line number - [ ] The single-user limitation is stated in the interface ## Launch constraint Do not market omitted Mermaid Chart capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered.
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Pinned, because a renderer update changes layout and therefore every committed image. VITE_RENDERER_VERSION=pinned-in-package-json # Required. Bundled font embedded in exports, with its licence file beside it. VITE_FONT_PATH=/fonts/inter.woff2 # Required. Delay before re-rendering while typing. Longer on large diagrams. VITE_RENDER_DEBOUNCE_MS=250 # Required. Raster scale for PNG export, rendered from vector at that scale rather than upscaled. EXPORT_SCALE=2 # Required. Where the batch command looks for sources and writes images. DIAGRAMS_DIR=./docs/diagrams
$ choose a build depth, inspect the files, then open the complete pack in your agent
Mermaid Chart: The shapes are easy; customers pay for low-friction editing, reliable exports, collaboration, and a canvas that never fights them.
xhigh-fidelity import and export
xpresentation and enterprise controls
xmultiplayer cursors and comments
xlarge template libraries
Mermaid Chart pricing
| plan | monthly | annual (per mo) | what you get |
|---|---|---|---|
| basic | $0/user | $0/user | Up to 3 diagrams; 1 team and 1 project; limited AI usage (credit cap not publicly quantified) |
| plus | — | $10/user | Unlimited diagrams and diagram size; 300 AI credits/year; view-and-comment collaboration |
| premium | — | $20/user | Unlimited diagrams; 2,000 AI credits/year; unlimited viewer seats, teams and projects; co-editing and SSO |
| enterprise | custom | — | Premium features; custom contracts, support and enterprise controls; numeric caps custom |
free tierUp to 3 hosted diagrams; 1 team and 1 project; limited AI usage (no numeric AI-credit cap published)
billingpaid self-serve plans are annual only; no month-to-month plan
hidden costsPaid prices are per user. Included AI credits reset on the plan cadence; Enterprise pricing and any extra-credit pricing are not published.
verified 2026-08-12 · source ↗
Vibecode Mermaid Chart
Yes. A competent AI coding agent (Claude Code, Codex, Cursor) can build a usable personal Mermaid Chart replacement in one session with the prompt on this page. It runs on your own machine or server with no subscription.
How much does Mermaid Chart cost?
Mermaid Chart's pricing is usage-based or varies by plan · The official pricing URL is recorded, but a stable current amount was not safely recoverable in this pass. Do not publish a number until rechecked..
What do I lose by replacing Mermaid Chart?
Honestly: high-fidelity import and export; presentation and enterprise controls; multiplayer cursors and comments; large template libraries. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to Mermaid Chart?
Yes: Mermaid (Text-to-diagram renderer with broad diagram syntax.). Using prior art is also vibecoding; the prompt is for when you want it exactly your way.