Skip to content

Commit cc893b1

Browse files
committed
Add ROADMAP.md for post-2.3.1 backlog
Documents the four candidate features reviewed after 2.3.1 shipped: 1. ?exportScale=N URL param (S, v2.4) - restore 1x/2x escape hatch without reintroducing a UI footgun 2. Color-blind-safe palette via [data-palette="cb"] (S, v2.4) - third CSS-variable set using Okabe-Ito colors 3. gzip + base64 share links (M, v2.5) - compress entire HTML into URL hash via CompressionStream, decoder on a hosted page 4. diagram.yaml intermediate format (L, v3.0) - structured spec + renderer, unlocks diffing / auto-layout / Mermaid import / C4 Each entry has: what + why, implementation sketch, tradeoffs, and explicit done-when criteria. Recommended order + a "declined or deferred" section at the bottom for things that came up in reviews but aren't going to be built.
1 parent b4bb309 commit cc893b1

1 file changed

Lines changed: 285 additions & 0 deletions

File tree

ROADMAP.md

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
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 &middot; **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

Comments
 (0)