Replace ASCII diagrams with Graphviz DOT in architecture and ticket docs.
This commit is contained in:
parent
b63277e4d1
commit
75bec564b2
2 changed files with 203 additions and 110 deletions
|
|
@ -3,132 +3,211 @@
|
|||
Document-first platform: every namespace is a book, every thread is a chapter,
|
||||
every reply is a section. Pandoc renders 67 output formats.
|
||||
|
||||
## Data Flow
|
||||
## Data Flow — Write Path
|
||||
|
||||
```dot
|
||||
digraph write_path {
|
||||
rankdir=LR
|
||||
node [shape=box, style=rounded, fontname="sans-serif"]
|
||||
edge [fontname="sans-serif", fontsize=10]
|
||||
|
||||
input [label="User Input\n(md, html, rst,\nmediawiki, latex,\ntextile, org, ...)", shape=note]
|
||||
source_fmt [label="source_format\ndetection", shape=diamond]
|
||||
pandoc_html [label="pandoc\nconvert to html5"]
|
||||
pandoc_roundtrip [label="pandoc\nhtml → markdown\n(round-trip clean)"]
|
||||
bleach [label="bleach\nsanitize"]
|
||||
data [label="Node.data\n(raw source)", shape=cylinder]
|
||||
data_html [label="Node.data_html\n(rendered)", shape=cylinder]
|
||||
source_col [label="Node.source_format", shape=cylinder]
|
||||
|
||||
input -> source_fmt
|
||||
source_fmt -> pandoc_html [label="rst, mediawiki,\nlatex, etc."]
|
||||
source_fmt -> pandoc_roundtrip [label="html input"]
|
||||
source_fmt -> data [label="markdown\n(direct)"]
|
||||
pandoc_roundtrip -> data [label="cleaned md"]
|
||||
pandoc_html -> bleach
|
||||
bleach -> data_html
|
||||
input -> source_col [style=dashed, label="store format"]
|
||||
data -> data_html [label="markdown_to_html\n(for md input)", style=dashed]
|
||||
}
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Write Path │
|
||||
│ │
|
||||
User Input │ source_format pandoc bleach │
|
||||
(md/html/rst/ │ ─────────────► convert to ──► sanitize │
|
||||
mediawiki/latex/ │ html5 pipeline │
|
||||
textile/org/...) │ │ │ │
|
||||
│ │ ▼ │
|
||||
│ │ data_html │
|
||||
│ │ (rendered) │
|
||||
│ │ │
|
||||
│ html input: │ │
|
||||
│ pandoc html→md │ │
|
||||
│ (round-trip clean) │ │
|
||||
│ │ │ │
|
||||
│ ▼ │ │
|
||||
│ data (raw) │ │
|
||||
│ + source_format │ │
|
||||
└─────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Read / Export Path │
|
||||
│ │
|
||||
Node tree │ tree-to-markdown pandoc │
|
||||
(adjacency list) ────► │ renderer ──► convert to ────► output
|
||||
│ target format │
|
||||
│ │
|
||||
│ Hierarchy: │
|
||||
│ # Thread Title (h1) │
|
||||
│ ## Author Name (h2, per reply) │
|
||||
│ ### Author Name (h3, nested reply) │
|
||||
│ ...depth maps to heading level │
|
||||
└─────────────────────────────────────────────┘
|
||||
## Data Flow — Read / Export Path
|
||||
|
||||
```dot
|
||||
digraph read_path {
|
||||
rankdir=LR
|
||||
node [shape=box, style=rounded, fontname="sans-serif"]
|
||||
edge [fontname="sans-serif", fontsize=10]
|
||||
|
||||
tree [label="Node tree\n(adjacency list)", shape=cylinder]
|
||||
renderer [label="tree-to-markdown\nrenderer"]
|
||||
pandoc [label="pandoc\nconvert to\ntarget format"]
|
||||
output [label="Output\n(pdf, epub, docx,\nhtml, rst, ...)", shape=note]
|
||||
|
||||
tree -> renderer
|
||||
renderer -> pandoc [label="markdown\ndocument"]
|
||||
pandoc -> output
|
||||
|
||||
subgraph cluster_heading_map {
|
||||
label="Heading depth mapping"
|
||||
style=dashed
|
||||
fontname="sans-serif"
|
||||
h1 [label="# Thread Title (h1)", shape=plaintext]
|
||||
h2 [label="## Author Name (h2, reply)", shape=plaintext]
|
||||
h3 [label="### Author Name (h3, nested)", shape=plaintext]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Export Hierarchy
|
||||
|
||||
```
|
||||
Namespace (book)
|
||||
GET /api/v1/export/namespace/{name}.{fmt}
|
||||
│
|
||||
├── Root Node A (chapter) ← default export
|
||||
│ GET /api/v1/export/threads/{id}.{fmt}
|
||||
│ │
|
||||
│ ├── Reply 1 (section) ← on-demand
|
||||
│ │ GET /api/v1/export/nodes/{id}.{fmt}
|
||||
│ │ │
|
||||
│ │ └── Reply 1.1 (subsection) ← on-demand
|
||||
│ │
|
||||
│ └── Reply 2 (section) ← on-demand
|
||||
│
|
||||
└── Root Node B (chapter) ← default export
|
||||
GET /api/v1/export/threads/{id}.{fmt}
|
||||
```dot
|
||||
digraph export_hierarchy {
|
||||
rankdir=TB
|
||||
node [shape=box, style=rounded, fontname="sans-serif"]
|
||||
edge [fontname="sans-serif", fontsize=10]
|
||||
|
||||
ns [label="Namespace (book)\n/export/namespace/{name}.{fmt}"]
|
||||
rootA [label="Root Node A (chapter)\n/export/threads/{id}.{fmt}"]
|
||||
rootB [label="Root Node B (chapter)\n/export/threads/{id}.{fmt}"]
|
||||
reply1 [label="Reply 1 (section)\n/export/nodes/{id}.{fmt}"]
|
||||
reply2 [label="Reply 2 (section)\n/export/nodes/{id}.{fmt}"]
|
||||
reply1_1 [label="Reply 1.1 (subsection)\n/export/nodes/{id}.{fmt}"]
|
||||
|
||||
ns -> rootA [label="default"]
|
||||
ns -> rootB [label="default"]
|
||||
rootA -> reply1 [label="on-demand"]
|
||||
rootA -> reply2 [label="on-demand"]
|
||||
reply1 -> reply1_1 [label="on-demand"]
|
||||
}
|
||||
```
|
||||
|
||||
**67 output formats** including: markdown, html5, pdf, epub, docx, odt, rst,
|
||||
67 output formats including: markdown, html5, pdf, epub, docx, odt, rst,
|
||||
latex, mediawiki, man, plain, rtf, asciidoc, textile, org, json, and more.
|
||||
|
||||
## Wiki Mode
|
||||
|
||||
```
|
||||
┌──────────────┐ wiki_edit() ┌──────────────┐
|
||||
│ │ ──────────────────► │ rb_revision │
|
||||
│ rb_node │ │ │
|
||||
│ │ 1. snapshot current │ id │
|
||||
│ id │ data → revision │ node_id (FK) │
|
||||
│ data │ 2. increment rev# │ user_id (FK) │
|
||||
│ data_html │ 3. overwrite node │ data │
|
||||
│ source_fmt │ with new content │ source_format │
|
||||
│ │ │ revision_number│
|
||||
└──────────────┘ │ created │
|
||||
│ └──────────────┘
|
||||
│
|
||||
▼
|
||||
can_wiki_edit(node, user)?
|
||||
├── user.authenticated? → required
|
||||
├── namespace.can_alter_node()? → owner/moderator always yes
|
||||
└── namespace.wiki && node.is_root? → wiki mode for root nodes
|
||||
```dot
|
||||
digraph wiki_mode {
|
||||
rankdir=TB
|
||||
node [shape=record, fontname="sans-serif"]
|
||||
edge [fontname="sans-serif", fontsize=10]
|
||||
|
||||
rb_node [label="{rb_node|id\ldata\ldata_html\lsource_format\l}"]
|
||||
rb_revision [label="{rb_revision|id\lnode_id (FK)\luser_id (FK)\ldata\lsource_format\lrevision_number\lcreated\l}"]
|
||||
wiki_edit [label="wiki_edit()", shape=ellipse]
|
||||
check [label="can_wiki_edit(node, user)?", shape=diamond]
|
||||
|
||||
rb_node -> wiki_edit [label="called on node"]
|
||||
wiki_edit -> rb_revision [label="1. snapshot\ncurrent data"]
|
||||
wiki_edit -> rb_node [label="3. overwrite\nwith new content", style=dashed]
|
||||
|
||||
check -> wiki_edit [label="allowed"]
|
||||
|
||||
subgraph cluster_perms {
|
||||
label="Permission checks"
|
||||
style=dashed
|
||||
fontname="sans-serif"
|
||||
node [shape=plaintext]
|
||||
p1 [label="user.authenticated → required"]
|
||||
p2 [label="namespace.can_alter_node() → owner/moderator always yes"]
|
||||
p3 [label="namespace.wiki && node.is_root → wiki mode for root nodes"]
|
||||
}
|
||||
|
||||
check -> p1 [style=invis]
|
||||
}
|
||||
```
|
||||
|
||||
## Auto-Generated Themes
|
||||
|
||||
```
|
||||
namespace_name
|
||||
│
|
||||
▼
|
||||
SHA-256 hash
|
||||
│
|
||||
├── bits[0:8] → hue (0-360)
|
||||
├── bits[8:40] → 8 seed values
|
||||
│
|
||||
▼
|
||||
HSL color palette
|
||||
│
|
||||
├── Light mode (:root, .theme-light)
|
||||
│ --rb-bg, --rb-text, --rb-link, --rb-accent, --rb-border, ...
|
||||
│
|
||||
└── Dark mode (@media prefers-color-scheme: dark, .theme-dark)
|
||||
--rb-bg, --rb-text, --rb-link, --rb-accent, --rb-border, ...
|
||||
```dot
|
||||
digraph themes {
|
||||
rankdir=TB
|
||||
node [shape=box, style=rounded, fontname="sans-serif"]
|
||||
edge [fontname="sans-serif", fontsize=10]
|
||||
|
||||
Deterministic: same namespace → same theme, always.
|
||||
Served at: GET /api/v1/themes/{namespace}/css (1-day cache)
|
||||
Preview: GET /api/v1/themes/{namespace}/preview (JSON palette)
|
||||
name [label="namespace_name", shape=plaintext]
|
||||
sha [label="SHA-256 hash"]
|
||||
hue [label="hue (0-360)\nbits[0:8]"]
|
||||
seeds [label="8 seed values\nbits[8:40]"]
|
||||
palette [label="HSL color palette"]
|
||||
|
||||
light [label="Light mode\n:root, .theme-light\n--rb-bg, --rb-text,\n--rb-link, --rb-accent,\n--rb-border, ...", shape=note]
|
||||
dark [label="Dark mode\n@media prefers-color-scheme: dark\n.theme-dark\n--rb-bg, --rb-text,\n--rb-link, --rb-accent,\n--rb-border, ...", shape=note]
|
||||
|
||||
css [label="GET /api/v1/themes/{ns}/css\n(1-day cache)", shape=component]
|
||||
preview [label="GET /api/v1/themes/{ns}/preview\n(JSON palette)", shape=component]
|
||||
|
||||
name -> sha
|
||||
sha -> hue
|
||||
sha -> seeds
|
||||
hue -> palette
|
||||
seeds -> palette
|
||||
palette -> light
|
||||
palette -> dark
|
||||
light -> css
|
||||
dark -> css
|
||||
palette -> preview
|
||||
}
|
||||
```
|
||||
|
||||
Deterministic: same namespace name always produces the same theme.
|
||||
|
||||
## Module Map
|
||||
|
||||
```
|
||||
remarkbox/
|
||||
├── lib/
|
||||
│ ├── pandoc.py ← subprocess wrapper, tree renderer
|
||||
│ └── theme_generator.py ← deterministic CSS from namespace name
|
||||
├── api/
|
||||
│ ├── export.py ← /export/ endpoints (namespace, thread, node)
|
||||
│ ├── wiki.py ← /wiki-edit, /revisions endpoints
|
||||
│ ├── themes.py ← /themes/ endpoints (css, preview)
|
||||
│ ├── views.py ← modified: source_format on create/reply/edit
|
||||
│ └── serializers.py ← modified: source_format in node JSON
|
||||
├── models/
|
||||
│ ├── node.py ← modified: source_format column, set_data(), wiki_edit()
|
||||
│ ├── namespace.py ← modified: can_wiki_edit()
|
||||
│ └── revision.py ← new: Revision model
|
||||
└── scripts/alembic/versions/
|
||||
├── d47dc908d2ea_... ← add source_format to rb_node
|
||||
└── 8e3c406e4049_... ← create rb_revision table
|
||||
```dot
|
||||
digraph modules {
|
||||
rankdir=TB
|
||||
node [shape=box, fontname="monospace", fontsize=10]
|
||||
edge [fontname="sans-serif", fontsize=9]
|
||||
compound=true
|
||||
|
||||
subgraph cluster_lib {
|
||||
label="remarkbox/lib/"
|
||||
style=rounded
|
||||
fontname="sans-serif"
|
||||
pandoc_py [label="pandoc.py\nsubprocess wrapper\ntree renderer"]
|
||||
theme_gen [label="theme_generator.py\ndeterministic CSS"]
|
||||
}
|
||||
|
||||
subgraph cluster_api {
|
||||
label="remarkbox/api/"
|
||||
style=rounded
|
||||
fontname="sans-serif"
|
||||
export_py [label="export.py\n/export/ endpoints"]
|
||||
wiki_py [label="wiki.py\n/wiki-edit, /revisions"]
|
||||
themes_py [label="themes.py\n/themes/ endpoints"]
|
||||
views_py [label="views.py\nsource_format on\ncreate/reply/edit"]
|
||||
serial_py [label="serializers.py\nsource_format in JSON"]
|
||||
}
|
||||
|
||||
subgraph cluster_models {
|
||||
label="remarkbox/models/"
|
||||
style=rounded
|
||||
fontname="sans-serif"
|
||||
node_py [label="node.py\nsource_format column\nset_data(), wiki_edit()"]
|
||||
ns_py [label="namespace.py\ncan_wiki_edit()"]
|
||||
rev_py [label="revision.py\nRevision model"]
|
||||
}
|
||||
|
||||
subgraph cluster_migrations {
|
||||
label="remarkbox/scripts/alembic/versions/"
|
||||
style=rounded
|
||||
fontname="sans-serif"
|
||||
mig1 [label="d47dc908d2ea\nadd source_format"]
|
||||
mig2 [label="8e3c406e4049\ncreate rb_revision"]
|
||||
}
|
||||
|
||||
export_py -> pandoc_py
|
||||
themes_py -> theme_gen
|
||||
wiki_py -> rev_py
|
||||
wiki_py -> node_py
|
||||
views_py -> node_py
|
||||
export_py -> node_py
|
||||
export_py -> ns_py
|
||||
node_py -> pandoc_py [style=dashed, label="non-md formats"]
|
||||
mig1 -> node_py [style=dotted]
|
||||
mig2 -> rev_py [style=dotted]
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -13,10 +13,24 @@ Wiki mode lets anyone edit root topics with revision tracking.
|
|||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Namespace (book) → pandoc → all formats (default)
|
||||
└── Root Node (chapter) → pandoc → all formats (default)
|
||||
└── Reply (section) → pandoc → on-demand only
|
||||
```dot
|
||||
digraph export_hierarchy {
|
||||
rankdir=LR
|
||||
node [shape=box, style=rounded, fontname="sans-serif"]
|
||||
|
||||
namespace [label="Namespace\n(book)"]
|
||||
root [label="Root Node\n(chapter)"]
|
||||
reply [label="Reply\n(section)"]
|
||||
pandoc [label="pandoc", shape=ellipse]
|
||||
formats [label="67 output\nformats", shape=note]
|
||||
|
||||
namespace -> root [label="contains"]
|
||||
root -> reply [label="contains"]
|
||||
namespace -> pandoc [label="default"]
|
||||
root -> pandoc [label="default"]
|
||||
reply -> pandoc [label="on-demand"]
|
||||
pandoc -> formats
|
||||
}
|
||||
```
|
||||
|
||||
### Data model changes
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue