# Architecture

This page uses one overview diagram to show NanoMD's three components, the conversion pipeline they share, and how the source is layered.

## System Overview

```mermaid
graph LR
    User[User input] --> Editor[MDEditor]
    Editor -->|"init() triggers re-render"| Viewer[MDViewer]
    Viewer -->|"reads each editor row"| Editor
    Viewer -->|Markdown| Trans["transToHTML()"]
    Parser[MDParser] -->|Markdown| Trans
    Editor -->|"download('html')"| Trans
    Trans -->|HTML string| Viewer
    Trans -->|HTML string| Parser
    Trans --> Funcs["set* transform functions<br/>src/function/"]
```

None of the three components parses Markdown itself; all of them call the same `transToHTML()`. Only the arguments differ: `MDViewer` passes the hashtag path and target and always uses the extended syntax; `MDParser` passes its `standard` flag; `MDEditor` calls it only when exporting HTML.

## Layers

| Layer | Location | Responsibility |
|---|---|---|
| Public classes | `src/model/editor.js`, `viewer.js`, `parser.js` | Public API; attached to `window.MDEditor` / `MDViewer` / `MDParser` |
| Editor sub-modules | `src/model/editorCaret.js`, `editorSelection.js`, `editorHistory.js`, `editorKeydown.js`, `editorTab.js` | Caret, selection range, undo stack, shortcut mapping, toolbar |
| Virtual DOM | `src/model/vDOM.js` | Old/new node tree comparison; the viewer currently replaces content with `replaceChildren()`, so the diff is not active |
| Transform functions | `src/function/set*.js`, `transToHTML.js` | One function per syntax, run in a fixed order |
| Shared constants | `src/data.js` | Regexes, string constants, CDN resource injection, `isDarkMode` |
| Styles | `src/sass/` → `dist/NanoMD.css`, `dist/NanoMD-output.css` | Editor/viewer styles and the stylesheet for exported HTML |

## Cross-cutting Principles

| Principle | How it shows |
|---|---|
| No framework | Native DOM APIs only; `createElement()` is an in-house lightweight builder |
| Single conversion entry | All HTML comes from `transToHTML()`, so syntax behaves the same in all three components (except for the `standard` flag) |
| Placeholder isolation | Converted fragments become `{{tag-uuid}}` so later steps do not re-parse them; they are restored once at the end |
| Global build | terser concatenates `src/**/*.js` into one script, so functions and constants share a single script scope |

## Further Reading

- [Parsing Pipeline](/parsing-pipeline): the step order inside `transToHTML()`
- [Rendering and Sync](/rendering-and-sync): when and how the viewer re-renders
- [Full architecture document](https://github.com/pardnchiu/NanoMD/blob/main/doc/architecture.md): per-module diagrams, event sequence, vDOM state machine
