Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .agents/skills/renderizr/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024-2026 Formula.Monks

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
102 changes: 102 additions & 0 deletions .agents/skills/renderizr/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
name: renderizr
description: Renders a Structurizr workspace as a Claude artifact or a static site, using the Renderizr CLI. Use when the user wants to see, share, publish or hand over a C4 architecture model — "show me the architecture", "turn this workspace into an artifact", "publish these diagrams" — or mentions Renderizr, workspace.json or artifact.html.
license: MIT
compatibility: Requires Node 20 or newer. Nothing else — no JVM, no Docker, no Graphviz, no PlantUML. Renderizr itself is run with `npx` and needs no installation.
allowed-tools: Bash(npx:*), Bash(node:*)
metadata:
author: Andrés Zorro <andres.zorro@monks.com>
version: 1.0.0
---

# Renderizr — a Structurizr workspace as one shareable file

This skill renders a [Structurizr workspace](https://docs.structurizr.com/workspaces) — its views, documentation and decision log — into a browsable static site, or into a single self-contained HTML file that can be uploaded as a Claude artifact and opened by anyone, with no server and no network.

Diagrams are drawn by Structurizr's own renderer rather than re-implemented, so they pan, zoom and play back dynamic views exactly as they do in Structurizr.

## When to use this skill

- The user wants to **see** an architecture model rather than read its source.
- The user wants to **share** a model with people who have no Structurizr account, no server and no copy of the DSL.
- The user asks for an **artifact**, a **preview**, or "publish the diagrams".
- The user is working in a repository that has an `./architecture` folder — often one created by [Scaffoldizr](https://formulamonks.github.io/scaffoldizr/) — and wants output from it.

Do **not** use this skill to author or edit a model. Renderizr renders; it does not parse DSL and never writes to the workspace. Editing the model is Scaffoldizr's job.

## The one command

```bash
npx github:FormulaMonks/renderizr <workspace.json|url> --single-file --out <dir>
```

That writes two files into `<dir>`:

| File | What it is | Use it for |
|---|---|---|
| `artifact.html` | The page **without** its own `<html>`/`<head>`/`<body>` scaffolding | **Uploading as a Claude artifact** — the host supplies the document |
| `index.html` | The same page as a complete standalone document | Opening from disk, emailing, dropping in a bucket |

Both inline every stylesheet, script, font, icon and the workspace itself. Neither makes a single network request.

**For a Claude artifact, use `artifact.html`.** Handing over `index.html` instead produces a document nested inside a document.

Drop `--single-file` to get a directory — `index.html` plus `assets/` — for hosting on a static server or GitHub Pages.

## Getting a workspace to render

Renderizr takes **JSON**, either a local path or a URL. It does not parse DSL.

- **A `workspace.json` already on disk** — usually `./architecture/workspace.json`. Use it directly.
- **Only a `workspace.dsl`** — export it first with [structurizr-cli](https://docs.structurizr.com/cli), which exports a DSL workspace to JSON. In a Scaffoldizr repository, `./architecture/scripts/export.sh` (or `export.ps1`) does this for you.
- **A URL** — passed straight through, e.g. the Big Bank plc example:

```bash
npx github:FormulaMonks/renderizr \
https://raw.githubusercontent.com/structurizr/ui/main/examples/big-bank-plc.json \
--single-file --out /tmp/big-bank
```

`workspace.json` is a **compiled output** in a Scaffoldizr repository. Render it, but never edit it — it is overwritten on the next export.

## Recommended flow

1. **Find the workspace.** Look for `./architecture/workspace.json`. If only `workspace.dsl` exists, export it first and say so; do not silently render a stale JSON.
2. **Render it**, into a temporary directory rather than the repository, unless the user asked for the output to be kept:

```bash
npx github:FormulaMonks/renderizr ./architecture/workspace.json --single-file --out /tmp/renderizr-out
```

3. **Check it is genuinely self-contained** before handing it over — see [verifying](./references/verifying.md). One command, and it is the difference between an artifact that opens and one that renders blank for the recipient.
4. **Hand over `artifact.html`.** Say which file it is and roughly how big; a real model lands around 1 MB.

## Flags

Full reference in [flags](./references/flags.md). The ones that matter most:

| Flag | Effect |
|---|---|
| `--single-file` | One self-contained document, plus `artifact.html`. **Use for artifacts.** |
| `-o, --out <dir>` | Output directory (default `structurizr-output`) |
| `--base <path>` | Base public path for the multi-file build, e.g. `/repo-name/` for project Pages |
| `--logo <path\|url>` | Image top-left in the header, embedded as a data URI |
| `--font <family>` | A Google Web Font, fetched at build time and embedded as woff2 |

`--font` and a remote `--logo` are the only things that need network access during a build. Without them a render is fully offline.

## Things that will bite you

Each of these has been verified against the tool, not inferred:

- **Node 20 is a hard floor.** `npx` runs against whatever Node is first on `PATH`, which is often not the one the shell reports. Renderizr checks and exits with a clear message rather than failing deep inside the build.
- **`artifact.html` and `index.html` are not interchangeable.** See the table above.
- **The output directory is emptied** before writing. Never point `--out` at a directory holding anything you want to keep.
- **`https://` in the output is not a leak.** A rendered page contains ordinary hyperlinks to `structurizr.com`, `c4model.com` and the like. Self-containment is about *asset* references — `<script src>`, `<link href>`, `<img src>` — of which there are none. Check the right thing; see [verifying](./references/verifying.md).
- **Working inside a clone of the Renderizr repository is different.** `pnpm build <workspace> [flags]` — and specifically *not* `pnpm build -- <workspace> --flag`, which makes the flag arrive as a second workspace. `pnpm dev` is the opposite and does want the `--`. This only applies inside the repository; `npx` users are unaffected.

## References

- [flags](./references/flags.md) — every CLI flag, what it does, and what it costs
- [artifacts](./references/artifacts.md) — the Claude artifact path in detail, including size and what to hand over
- [verifying](./references/verifying.md) — proving an artifact is self-contained before you hand it over
59 changes: 59 additions & 0 deletions .agents/skills/renderizr/references/artifacts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Rendering a workspace as a Claude artifact

`--single-file` exists for exactly this. It writes **two** files, and the difference between them is the whole point.

```bash
npx github:FormulaMonks/renderizr ./architecture/workspace.json --single-file --out /tmp/arch
```

```
/tmp/arch/
├── artifact.html ← upload this one
└── index.html
```

## Which file to hand over

| | `artifact.html` | `index.html` |
|---|---|---|
| Document scaffolding | none — no `<html>`, `<head>` or `<body>` | a complete document |
| Intended host | somewhere that supplies its own document, i.e. **a Claude artifact** | a browser opening a file directly |
| Opening it from disk | works, browsers are forgiving | works |

**Upload `artifact.html`.** A Claude artifact is wrapped in a document skeleton at publish time, so handing over `index.html` nests a document inside a document. It usually still renders, which is what makes the mistake easy to miss and worth getting right the first time.

Use `index.html` when the user wants a file to email, drop in a bucket, or open by double-clicking.

## What is inside

Everything. Stylesheets, scripts, fonts, icons, the Structurizr renderer and the workspace JSON are all inlined. The page makes **no network requests at all** — it works from `file://`, inside a sandboxed frame, with the network unplugged.

That is a checkable claim, not a promise. See [verifying](./verifying.md).

## Size

A real model lands around **1 MB**, roughly 320 KB gzipped. Measured:

| Workspace | `artifact.html` | gzipped |
|---|---|---|
| Big Bank plc (the Structurizr example) | 1.07 MB | ~327 KB |
| A small fixture workspace | 1.02 MB | ~320 KB |

Most of that is the Structurizr renderer itself, so size is close to constant — a bigger model is not a proportionally bigger file. The floor is about a megabyte no matter how small the workspace.

Claude artifacts allow up to 16 MB, so an ordinary workspace is nowhere near the limit. If a render ever approaches it, the cause is embedded imagery in the workspace or its documentation, not the number of elements.

Adding `--font` costs another 45–70 KB gzipped, and `--font-italic` roughly doubles that.

## What the reader gets

- Every view in the workspace, listed down the side, each with its own key.
- Diagrams drawn by Structurizr's own renderer: pan, zoom, dynamic-view playback, and the description and technology labels toggling.
- Workspace documentation as pages, with a table of contents and heading anchors.
- The decision log, with status pills, supersessions and decisions grouped by year.
- Light and dark, following the reader's system setting.
- Hash-based routing, so a link to a particular view, document or decision survives a reload and works over `file://`.

## What it is not

Renderizr renders a workspace; it does not edit one. There is no authoring UI, and nothing written back to `workspace.json`. Diagram layout comes from the workspace — if a diagram is laid out badly, fix it in the model, then re-render.
47 changes: 47 additions & 0 deletions .agents/skills/renderizr/references/flags.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Flags

The authoritative list is `npx github:FormulaMonks/renderizr --help`, which is generated from the parser itself. This page says what each flag is *for* and what it costs.

```
renderizr <workspace.json|url> [options]
```

The workspace is positional and required: a local path or a URL to a Structurizr workspace **in JSON**. Renderizr does not parse DSL — see the SKILL for how to export one.

## Output

| Flag | Default | What it does |
|---|---|---|
| `-o, --out <dir>` | `structurizr-output` | Where the build is written, relative to the current directory. **The directory is emptied first.** |
| `--single-file` | off | Inlines every asset and the workspace into one document, and emits `artifact.html` alongside it. See [artifacts](./artifacts.md). |
| `--base <path>` | `""` | Base public path for the **multi-file** build. Rewrites `./assets/…` to `<base>/assets/…`. Needed when the site is served from a subdirectory, e.g. `--base /renderizr/` for a GitHub project Pages site. Has no meaning with `--single-file`, where there are no separate assets. |
| `-h, --help` | | Print the usage and exit |

## Branding

| Flag | Default | What it does |
|---|---|---|
| `--logo <path\|url>` | none | Image shown top-left in the header, embedded as a data URI. A local path is read from disk; a URL is fetched at build time. |
| `--logo-alt <text>` | `""` | Alt text for the logo. Set it — the logo is otherwise an unlabeled image. |
| `--logo-href <url>` | none | Wraps the logo in a link, usually back to the team or product homepage. |

## Typography

| Flag | Default | What it does |
|---|---|---|
| `--font <family>` | system fonts | A Google Web Font family, e.g. `Inter` or `Source Sans 3`. Fetched at build time and embedded as woff2 data URIs. Costs roughly 45–70 KB gzipped. |
| `--font-weights <list>` | `400,700` | Comma-separated weights. A variable font covering the range is preferred when one exists. |
| `--font-subsets <list>` | `latin` | Comma-separated subsets. Add e.g. `latin-ext` or `greek` only if the workspace needs them — each one costs bytes. |
| `--font-italic` | off | Also embed the italic faces. Roughly doubles the font weight. |

The font is embedded, not linked, so a branded build is exactly as offline as an unbranded one once it is built.

## Network

A render is fully offline **unless** you pass `--font`, a remote `--logo`, or a workspace URL. Those are fetched during the build and then inlined; the output never fetches anything at runtime regardless.

This matters on a locked-down machine or in CI without egress: a local workspace with no `--font` and no remote `--logo` will build with the network unplugged.

## Requirements

Node 20 or newer, and nothing else. Renderizr checks the running version and exits with a message naming it, because `npx` runs against whatever Node is first on `PATH` — often not the one the shell reports.
55 changes: 55 additions & 0 deletions .agents/skills/renderizr/references/verifying.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Verifying an artifact before you hand it over

A single-file render makes no network requests. Check it rather than assert it — the failure mode is a recipient opening a page that renders blank or unstyled, long after you have moved on.

## Check the right thing

A rendered page contains plenty of `https://` — hyperlinks in the documentation, links to `structurizr.com` and `c4model.com`, the SVG namespace `http://www.w3.org/2000/svg`. **None of those is a leak.** They are content, and a page full of them is still perfectly self-contained.

What matters is whether anything is *loaded* over the network: `<script src>`, `<link href>`, `<img src>`, `<source>`, and their relatives.

A naive `grep https://` therefore fails forever and teaches you to ignore it. Match asset-bearing tags instead:

```bash
grep -Eoi '<(script|link|img|source|iframe|embed|video|audio|track|object)[^>]+(src|href|srcset|data|poster)[[:space:]]*=[[:space:]]*["'"'"']?(https?:)?//' \
/tmp/arch/artifact.html
```

**No output is the pass.** Anything printed is a real external dependency and the artifact is not safe to hand over.

## The stronger check

Open it the way the recipient will — with nothing beside it and no network:

```bash
mkdir -p /tmp/alone && cp /tmp/arch/artifact.html /tmp/alone/
# then open /tmp/alone/artifact.html in a browser
```

If it renders the workspace name, the view list and a diagram from a directory containing nothing else, it is genuinely standalone. Anything that had not been inlined has nothing to resolve against and nowhere to fetch from, so it fails visibly rather than subtly.

Renderizr's own test suite does exactly this, in headless Chrome, on every build.

## If the build refuses

Renderizr will sometimes stop rather than emit a file:

```
Cannot make this bundle artifact-safe: …
```

This is deliberate and means the bundle holds something a Claude artifact upload would reject — a lone surrogate or a U+FFFD in a position that cannot be rewritten safely. The message names the offset and the snippet.

**Do not work around it by disabling the check or hand-editing the output.** A refusal is the tool telling you the artifact would be rejected or corrupted downstream. Report the message; the offending literal is almost always in a dependency, and the fix belongs there or in the build.

## Quick sanity checks

```bash
# Both files exist and are a plausible size — under ~100 KB means something failed
ls -l /tmp/arch/

# The workspace really was baked in, not left to be fetched
grep -c "<workspace name>" /tmp/arch/artifact.html
```

For the multi-file build instead of `--single-file`, the equivalent is that `index.html`, `assets/` and `favicon.png` all exist, and that `assets/` holds one entry script and one stylesheet.
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,14 @@ yarn.lock
# tools sees them in `git status` and one of them eventually commits a
# directory of local scratch into a public repository. One line each is cheaper
# than that.
/.agents/
# Agent scaffolding is local, with one exception: the Renderizr skill under
# .agents/skills/renderizr is a published part of this project. A negation
# cannot reach inside an excluded directory, so the parents are opened and
# their contents excluded instead.
/.agents/*
!/.agents/skills
/.agents/skills/*
!/.agents/skills/renderizr
/.claude/
/.playwright-mcp/
/skills-lock.json
Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,18 @@ The one required argument is the workspace: a local path or an `http(s)` URL to

A font is the one option with a real cost: Inter at latin, weights 400–700, adds about 50KB gzipped. Everything else is a few kilobytes at most.

## Use it from an AI agent

Renderizr ships an agent skill, so a coding agent can render a workspace for you without being told how each time:

```bash
npx skills add formulamonks/renderizr
```

It teaches the agent the one command, which of the two `--single-file` outputs to hand over, how to get JSON out of a DSL workspace, and how to check the result is genuinely self-contained before passing it on. Ask for "an artifact of the architecture" and you get one back.

It pairs with [Scaffoldizr](https://formulamonks.github.io/scaffoldizr/), whose skill authors a workspace where this one renders it — `npx skills add formulamonks/scaffoldizr`. The skill lives in [`.agents/skills/renderizr`](.agents/skills/renderizr) if you would rather read it than install it.

## Renderizr or structurizr-site-generatr?

[structurizr-site-generatr](https://github.com/avisi-cloud/structurizr-site-generatr) solves the same problem and solves parts of it better. The two make opposite trades, so the choice is usually clear:
Expand Down
Loading