|
| 1 | +# Archify Roadmap |
| 2 | + |
| 3 | +Features planned but not yet shipped, in recommended implementation order. |
| 4 | + |
| 5 | +Effort legend: **S** = afternoon · **M** = ~a day · **L** = multi-day. |
| 6 | +Target versions are suggestions — bundle them however makes sense. |
| 7 | + |
| 8 | +--- |
| 9 | + |
| 10 | +## 1. `?exportScale=N` URL parameter |
| 11 | + |
| 12 | +**Effort:** S · **Target:** v2.4.0 |
| 13 | + |
| 14 | +### What |
| 15 | + |
| 16 | +Re-expose 1× / 2× / 3× / 4× raster scale control — via URL parameter only, no UI selector. Default stays 4× for any user who doesn't care. |
| 17 | + |
| 18 | +### Why |
| 19 | + |
| 20 | +v2.3.0 removed the scale selector because "2× default + bitmap upsampling" produced soft output and the UI actively encouraged picking the softest option. The native-rasterization fix from 2.3 stays; this just restores an escape hatch for: |
| 21 | + |
| 22 | +- Sharing compact PNGs in Slack / Notion / Discord where a 4000×2720 file is overkill |
| 23 | +- Headless batch scripts wanting a specific output size |
| 24 | +- Embedding in pipelines that re-compress anyway (no point in paying the 4× pixel cost upstream) |
| 25 | + |
| 26 | +No UI surface means no regression back to the "soft by default" footgun. |
| 27 | + |
| 28 | +### How |
| 29 | + |
| 30 | +```js |
| 31 | +function resolveRasterScale() { |
| 32 | + try { |
| 33 | + var v = parseInt(new URLSearchParams(location.search).get('exportScale'), 10); |
| 34 | + if (v === 1 || v === 2 || v === 3 || v === 4) return v; |
| 35 | + } catch (_) {} |
| 36 | + return 4; |
| 37 | +} |
| 38 | +var RASTER_SCALE = resolveRasterScale(); |
| 39 | +``` |
| 40 | + |
| 41 | +Also: |
| 42 | +- Let the existing canvas-size clamp (16 Mpx) continue to downshift automatically if `scale × viewBox` would exceed limits. |
| 43 | +- Update the "Copied" toast to append `(Nx)` when `N !== 4`, so the user sees they got non-default output. |
| 44 | +- Document in README's **URL parameters** section. |
| 45 | + |
| 46 | +### Done when |
| 47 | + |
| 48 | +- `?exportScale=2` produces a 2000×1360 PNG for the default viewBox. |
| 49 | +- No parameter → output is byte-for-byte identical to v2.3.1. |
| 50 | +- Canvas clamp still kicks in on oversized viewBoxes. |
| 51 | +- README and CHANGELOG updated. |
| 52 | + |
| 53 | +--- |
| 54 | + |
| 55 | +## 2. Color-blind-safe palette |
| 56 | + |
| 57 | +**Effort:** S · **Target:** v2.4.0 (bundle with #1) |
| 58 | + |
| 59 | +### What |
| 60 | + |
| 61 | +A third CSS-variable set `[data-palette="cb"]` orthogonal to the existing `[data-theme]` axis. Uses the Okabe-Ito palette (seven colors designed to be distinguishable under deuteranopia, protanopia, and tritanopia). |
| 62 | + |
| 63 | +### Why |
| 64 | + |
| 65 | +Archify's default palette uses emerald (backend) + rose (security) + violet (database) — emerald and rose are hard to distinguish under red-green deficiency, which affects ~8% of males. For inclusive team docs, architecture reviews, and conference slides, an accessible option matters. |
| 66 | + |
| 67 | +### How |
| 68 | + |
| 69 | +Add a palette axis alongside the theme axis. Existing CSS structure already has every color as a variable, so this is just one more block: |
| 70 | + |
| 71 | +```css |
| 72 | +[data-palette="cb"] { |
| 73 | + --frontend-stroke: #56B4E9; /* sky blue */ |
| 74 | + --backend-stroke: #009E73; /* bluish green */ |
| 75 | + --database-stroke: #CC79A7; /* reddish purple */ |
| 76 | + --cloud-stroke: #E69F00; /* orange */ |
| 77 | + --security-stroke: #D55E00; /* vermillion */ |
| 78 | + --messagebus-stroke: #F0E442; /* yellow */ |
| 79 | + --external-stroke: #0072B2; /* blue */ |
| 80 | + /* matching rgba fills for each */ |
| 81 | +} |
| 82 | +``` |
| 83 | + |
| 84 | +Toggle surfaces: |
| 85 | +- URL parameter `?palette=cb` (mirrors the existing `?theme=` pattern). |
| 86 | +- Optionally: an accessibility button in the toolbar next to the theme toggle. Button label can be a small `◐` glyph with `aria-label="Color palette"`. |
| 87 | + |
| 88 | +Persistence: `localStorage['archify-palette']`, URL param wins. |
| 89 | + |
| 90 | +### Done when |
| 91 | + |
| 92 | +- Dark + default, Dark + cb, Light + default, Light + cb all render cleanly. |
| 93 | +- URL override works alongside localStorage persistence. |
| 94 | +- README documents `?palette=cb`. |
| 95 | +- SKILL.md notes that palette is orthogonal to theme. |
| 96 | +- Legend + summary-card dots re-map correctly under the cb palette. |
| 97 | + |
| 98 | +--- |
| 99 | + |
| 100 | +## 3. gzip + base64 share links |
| 101 | + |
| 102 | +**Effort:** M · **Target:** v2.5.0 |
| 103 | + |
| 104 | +### What |
| 105 | + |
| 106 | +"Share" button in the toolbar that compresses the entire generated HTML into the URL fragment, producing a self-contained link. Opening the link reconstitutes the diagram in-browser. No server, no attachment. |
| 107 | + |
| 108 | +### Why |
| 109 | + |
| 110 | +Today, sharing a diagram means attaching an HTML file. A URL is strictly better for: |
| 111 | +- Slack / Discord / Teams (shows as link unfurl inline) |
| 112 | +- GitHub / GitLab issue + PR comments |
| 113 | +- Jira / Linear / Notion pages |
| 114 | +- Email + RFC drafts |
| 115 | +- Browser bookmarks + history |
| 116 | + |
| 117 | +### How |
| 118 | + |
| 119 | +Two halves: |
| 120 | + |
| 121 | +**Producer (in the generated HTML):** |
| 122 | + |
| 123 | +```js |
| 124 | +async function createShareLink() { |
| 125 | + const html = '<!DOCTYPE html>\n' + document.documentElement.outerHTML; |
| 126 | + const stream = new Blob([html]).stream().pipeThrough(new CompressionStream('gzip')); |
| 127 | + const bytes = new Uint8Array(await new Response(stream).arrayBuffer()); |
| 128 | + const b64 = btoa(String.fromCharCode.apply(null, bytes)); |
| 129 | + return SHARE_BASE_URL + '#data=' + b64; |
| 130 | +} |
| 131 | +``` |
| 132 | + |
| 133 | +**Consumer (hosted decoder page):** |
| 134 | + |
| 135 | +A static page at `archify.pages.dev/` (Cloudflare Pages, free tier) that: |
| 136 | +1. Reads `location.hash` |
| 137 | +2. Base64-decodes, runs it through `DecompressionStream('gzip')` |
| 138 | +3. `document.open(); document.write(html); document.close();` |
| 139 | + |
| 140 | +Use `CompressionStream` / `DecompressionStream` (native, no pako dep needed — supported across Chrome 80+, Firefox 113+, Safari 16.4+). |
| 141 | + |
| 142 | +### Tradeoffs |
| 143 | + |
| 144 | +- **URL length:** Chrome handles ~32 KB fragments reliably. A typical archify diagram compresses to 5–15 KB. Very large diagrams (30+ components) may bump into the limit — fall back to a "Diagram too large to share as link" message with a download button. |
| 145 | +- **Security:** The decoder `document.write()`s arbitrary HTML. The decoder page must live on a dedicated hostname that never holds user credentials, so XSS-via-share-link can't exfiltrate anything interesting. Never run the decoder on the same origin as an account system. |
| 146 | +- **Fragment vs query param:** `#data=` stays client-side (never hits server logs). Prefer it over `?data=`. |
| 147 | + |
| 148 | +### Done when |
| 149 | + |
| 150 | +- "Share" button produces a URL < 30 KB for a typical 8-component diagram. |
| 151 | +- Opening the link in a fresh tab reconstitutes the exact same page (theme, palette, everything). |
| 152 | +- Link works after pasting into Slack / GitHub (tested in a throwaway thread). |
| 153 | +- Decoder page is deployed and versioned (e.g., `archify.pages.dev/v1/`). |
| 154 | +- Fallback flow triggers cleanly when payload exceeds the threshold. |
| 155 | + |
| 156 | +### Open questions |
| 157 | + |
| 158 | +- Should the decoder sanitize anything? The HTML is self-written by Archify, but a malicious actor could craft a payload. Mitigation: only run decoder on dedicated hostname, set strict CSP on the decoder page. |
| 159 | +- Versioning: if the template changes, do older share links still work? Yes — the link contains the entire old template inline, so it's self-contained. Decoder just `document.write()`s whatever's in the blob. |
| 160 | + |
| 161 | +--- |
| 162 | + |
| 163 | +## 4. `diagram.yaml` intermediate format |
| 164 | + |
| 165 | +**Effort:** L · **Target:** v3.0.0 (architectural break, worth its own major version) |
| 166 | + |
| 167 | +### What |
| 168 | + |
| 169 | +Claude writes a structured YAML spec describing the diagram (components, connections, groups, positions, metadata) instead of one-shot HTML. A renderer (JS module) turns the YAML into the existing HTML template. |
| 170 | + |
| 171 | +### Why |
| 172 | + |
| 173 | +Today, every iteration ("move Redis to the left") triggers Claude to regenerate the entire HTML, which frequently shifts unrelated coordinates. A structured intermediate format unlocks: |
| 174 | + |
| 175 | +| Capability | How it uses the YAML | |
| 176 | +|---|---| |
| 177 | +| Stable iterative edits | Modify one field; renderer preserves everything else | |
| 178 | +| Version diffing | `git diff` on YAML is human-readable | |
| 179 | +| Theme / palette re-render | One YAML × N themes = N HTMLs without re-prompting Claude | |
| 180 | +| Mermaid / PlantUML import | Parser: source → YAML → HTML | |
| 181 | +| Auto-layout via Dagre / ELK | Read YAML, recompute `pos`, re-render | |
| 182 | +| C4 layer navigation | Add `layer: context / container / component` field | |
| 183 | +| CI architecture diff bot | PR bot renders before.yaml vs after.yaml side-by-side as PNGs | |
| 184 | + |
| 185 | +### Schema sketch |
| 186 | + |
| 187 | +```yaml |
| 188 | +meta: |
| 189 | + title: agmon |
| 190 | + subtitle: AI agent observability |
| 191 | + theme: dark # dark | light |
| 192 | + palette: default # default | cb |
| 193 | + viewBox: [1000, 680] |
| 194 | + |
| 195 | +components: |
| 196 | + claude_code: |
| 197 | + type: external # frontend | backend | database | cloud | security | messagebus | external |
| 198 | + label: Claude Code |
| 199 | + sublabel: AI coding agent |
| 200 | + pos: [40, 80] |
| 201 | + size: [140, 60] |
| 202 | + |
| 203 | + agmon_emit: |
| 204 | + type: backend |
| 205 | + label: agmon emit |
| 206 | + sublabel: hook stdin bridge |
| 207 | + pos: [410, 80] |
| 208 | + size: [150, 60] |
| 209 | + |
| 210 | +connections: |
| 211 | + - from: claude_code |
| 212 | + to: agmon_emit |
| 213 | + label: "hooks (stdin)" |
| 214 | + variant: default # default | emphasis | security | dashed |
| 215 | + |
| 216 | +groups: |
| 217 | + - type: security-group |
| 218 | + label: "sg-name :port" |
| 219 | + contains: [load_balancer] |
| 220 | + |
| 221 | +cards: |
| 222 | + - title: Security |
| 223 | + color: rose |
| 224 | + items: |
| 225 | + - OAuth 2.0 via external provider |
| 226 | + - TLS everywhere |
| 227 | + |
| 228 | +footer: "T toggle theme · E open Export" |
| 229 | +``` |
| 230 | +
|
| 231 | +### How (phased) |
| 232 | +
|
| 233 | +**Phase A — Renderer only (no LLM changes).** `render.js` (plain JS, runs in browser or Node) reads a YAML file → outputs the existing HTML. Power users can hand-write YAML. Ships as a library + CLI; template still works standalone. Target: v2.6. |
| 234 | + |
| 235 | +**Phase B — SKILL.md rewrite.** Claude produces YAML + a one-line "render this" note first, and the HTML second. Users iterate by asking Claude to modify the YAML; the renderer regenerates the HTML. Target: v3.0. |
| 236 | + |
| 237 | +**Phase C (optional) — Expose the renderer as:** |
| 238 | +- CLI: `npx archify render diagram.yaml > diagram.html` |
| 239 | +- Cloudflare Worker: POST YAML → HTML |
| 240 | +- VS Code extension: side-by-side YAML → preview |
| 241 | + |
| 242 | +### Tradeoffs |
| 243 | + |
| 244 | +- **Pro:** Unlocks diffing, auto-layout, Mermaid import, C4 layers, CI integration — every backlog feature becomes easier. |
| 245 | +- **Pro:** Claude output becomes more deterministic and reviewable. |
| 246 | +- **Con:** Two artifacts per generation (YAML + HTML) is slightly more complex for first-time users. Mitigate by generating HTML inline in the chat as usual; the YAML is an artifact, not a required read. |
| 247 | +- **Con:** Breaks the "generate → open → done" single-artifact promise — there's now a source-of-truth question. We'd need to be clear the YAML is canonical and the HTML is regenerable. |
| 248 | +- **Con:** Renderer becomes the canonical implementation path. The current template.html stays, but it becomes a target rather than the source. |
| 249 | + |
| 250 | +### Done when |
| 251 | + |
| 252 | +- `render.js` can reproduce today's `examples/web-app.html` from a hand-written YAML. |
| 253 | +- YAML schema documented in `docs/yaml-schema.md`. |
| 254 | +- Claude, prompted via the updated SKILL.md, produces valid YAML that renders correctly. |
| 255 | +- Re-theming (dark → light, default → cb palette) is a YAML edit + re-render, no re-prompt. |
| 256 | +- `git diff` between two diagram YAMLs is actually readable. |
| 257 | +- Backward-compat: hand-written or 2.x-generated HTML still works in a browser; it's just no longer the primary output format. |
| 258 | + |
| 259 | +--- |
| 260 | + |
| 261 | +## Recommended order |
| 262 | + |
| 263 | +1. **`?exportScale=N`** (S) — fills a gap left by v2.3 at near-zero effort. |
| 264 | +2. **Color-blind palette** (S) — CSS-variable system is already in place; high ratio of user impact to effort. |
| 265 | +3. **gzip + base64 share** (M) — meaningful UX win for every sharing interaction. |
| 266 | +4. **`diagram.yaml`** (L) — architectural; bundle under v3.0. |
| 267 | + |
| 268 | +Features 1 and 2 are good candidates to ship together as v2.4.0. |
| 269 | +Feature 3 is v2.5.0. |
| 270 | +Feature 4 is v3.0.0. |
| 271 | + |
| 272 | +--- |
| 273 | + |
| 274 | +## Not planned (declined or deferred) |
| 275 | + |
| 276 | +| Idea | Why not | |
| 277 | +|---|---| |
| 278 | +| Zoom & pan | Browser native pinch / Cmd-scroll already works; minimal gain for significant code. | |
| 279 | +| Auto-layout (Dagre / ELK) | Conflicts with hand-placed aesthetic; blocked on `diagram.yaml`. | |
| 280 | +| Annotation / overlay mode | Pushes toward an editor, not a viewer. | |
| 281 | +| C4 layer tabs | Blocked on `diagram.yaml`. | |
| 282 | +| Diagram diff UI | Better as a companion CLI than template bloat. | |
| 283 | +| Mermaid import | Can be addressed via SKILL.md prompt engineering first; full parser after `diagram.yaml`. | |
| 284 | +| PDF export button | `Cmd+P → Save as PDF` already works, print stylesheet handles the rest. | |
| 285 | +| Font-embedding in exports | `local()` fallback (2.2) covers the common case; full embed would add ~150 KB per generated file. | |
0 commit comments