# gridgram — full documentation > Concatenated English docs plus the generated `gg llm` reference. Auto-generated; do not edit by hand. --- # AI Guide Gridgram is built to be **author-able by an LLM agent**. You describe what you want in natural language; an agent produces a valid `.gg` file, renders it, reads back diagnostics, and iterates — no manual reference-pasting required. This guide is **task-oriented**. Pick one of the three usage paths below and follow it end-to-end. Each tutorial starts from a clean machine and ends with a rendered diagram plus the command to update later. ## Three ways to use gridgram from an agent | Tutorial | Best for | | ----------------------------------------- | ------------------------------------------------------------------------------ | | [Claude Code plugin](./claude-plugin) | You use Claude Code. Slash commands (`/gg-install`, `/gg-icons`, `/gg-author`, `/gg-render`) drive the whole workflow. | | [`gh skill`](./gh-skill) | You want the same skill bundle installed into Copilot, Cursor, Gemini CLI, or Codex alongside (or instead of) Claude Code. | | [context7 (MCP)](./context7) | You want an agent to retrieve gridgram's docs via MCP without installing anything plugin-side. Read-only. | You can mix and match. The Claude plugin includes a `/gg-install` skill that fetches the CLI binary; `gh skill` gives you the same skills in other hosts; context7 is a parallel retrieval channel that works even without any plugin. ## What you'll need None of this is installed by gridgram itself. Install whatever the tutorial of your choice calls for before starting. | Software | For which tutorial | Why | | ---------------------------------------------------------------- | ------------------------------------- | -------------------------------------- | | [git](https://git-scm.com/) | All | Plugin marketplaces clone over git. | | [Claude Code](https://code.claude.com/) | Claude plugin, context7 | Host that runs the skills / MCP. | | [GitHub CLI `gh`](https://cli.github.com/) (v2.90+) | `gh skill` | Provides the `gh skill` subcommand. | | [Cursor](https://cursor.com/) / [Gemini CLI](https://github.com/google-gemini/gemini-cli) / [Codex](https://openai.com/index/introducing-codex/) | `gh skill` (optional target) | Alternative skill hosts. | | `curl`, `tar`, `unzip` (windows) | `/gg-install` runtime | Downloading the `gg` binary. | | `bun` ([install](https://bun.sh/)) | Only if building `gg` from source | You can use the release binary instead.| You do **not** need to install the `gg` CLI manually — the Claude plugin ships a `/gg-install` skill that handles it, and `gh skill` users can run the same skill after installing it. If you'd rather install `gg` outside your agent, follow the standard [Quick start](/en/guide/) or [Install](/en/guide/install) page; any install that puts `gg` on `$PATH` is picked up by the skills automatically. ## At a glance: what gridgram exposes If you want the full tour before picking a tutorial: - **[`gg llm`](./cli#gg-llm)** — one-shot Markdown / JSON reference teaching an agent the `.gg` grammar, CLI, icons, JSON envelope, and canonical examples. - **[`gg icons`](./cli#gg-icons)** — scored semantic search over 5,039 outline + 1,053 filled Tabler icons. - **[`/llms.txt`](https://gridgram.ideamans.com/llms.txt) + [`/llms-full.txt`](https://gridgram.ideamans.com/llms-full.txt)** — public discovery files on the docs site. - **[`context7.json`](https://github.com/ideamans/gridgram/blob/main/context7.json)** — registers gridgram for [context7](./context7) MCP retrieval. - **[`plugins/gridgram`](https://github.com/ideamans/gridgram/tree/main/plugins/gridgram)** — four skills (`gg-install`, `gg-render`, `gg-icons`, `gg-author`) distributed via Claude Code and `gh skill`. The [CLI reference page](./cli) documents `gg llm` and `gg icons` in full — useful whether an agent is driving them through a skill or you're running them manually in a terminal. ## Tutorials Start here: - **[Claude Code plugin](./claude-plugin)** — 10-minute path if you already use Claude Code. - **[`gh skill`](./gh-skill)** — if you want the skills in Copilot / Cursor / Gemini / Codex. - **[context7](./context7)** — if you want an agent to *retrieve* gridgram docs over MCP without installing anything gridgram-specific. --- # Claude Code plugin tutorial End-to-end walkthrough from a clean machine to rendering a diagram with Claude Code's `/gg-*` slash commands. **Time**: ~10 minutes. **Result**: a rendered SVG you can open in a browser, plus the commands you'll use later to stay current. ## Prerequisites Install these yourself if you haven't already: - [**Claude Code**](https://code.claude.com/) (any recent version) - [**git**](https://git-scm.com/) - **A shell** (bash/zsh on macOS/Linux, PowerShell on Windows) - **Network access** to `github.com` You do **not** need the `gg` CLI installed yet — the plugin will install it for you. ## 1. Add the gridgram marketplace Start Claude Code and add the `ideamans/claude-public-plugins` marketplace: ```text /plugin marketplace add ideamans/claude-public-plugins ``` You should see: ``` ✔ Successfully added marketplace: ideamans-plugins ``` Verify: ```text /plugin marketplace list ``` ## 2. Install the gridgram plugin ```text /plugin install gridgram@ideamans-plugins ``` Output: ``` ✔ Successfully installed plugin: gridgram@ideamans-plugins ``` Verify: ```text /plugin list ``` You should see `gridgram@ideamans-plugins` with status `enabled`. Now, **reload the session** so the skill commands become available: ```text /reload-plugins ``` Type `/gg-` — Claude Code's autocomplete should now offer `/gg-install`, `/gg-render`, `/gg-icons`, and `/gg-author`. ## 3. Install the `gg` CLI The skills shell out to the `gg` binary. You have two options; either works — pick whichever you prefer. ### Option A: Use `/gg-install` inside Claude (recommended for first-timers) Ask Claude: > Install the gridgram CLI for my platform. Claude picks up `/gg-install`, which: 1. Detects your OS + CPU architecture (`uname`). 2. Fetches the latest release from . 3. Finds a writable directory on `$PATH` (tries `~/.local/bin`, `~/bin`, `/usr/local/bin`, `/opt/homebrew/bin` in that order). 4. Asks you to confirm the target directory. 5. Downloads + extracts + moves the binary into place. If no candidate is writable without sudo, the skill stages `gg` at `/tmp/gg` and prints the exact `sudo mv …` command to run: ``` gg staged at /tmp/gg — run 'sudo mv /tmp/gg /usr/local/bin/gg' to finish ``` ### Option B: Install `gg` the regular way If you already have a preferred install method, or you want to set up `gg` outside of Claude Code, follow the standard install guide at **[Quick start](/en/guide/)** (one-line curl / PowerShell scripts) or **[Install](/en/guide/install)** for manual alternatives. Any install that puts `gg` on `$PATH` will be picked up by the skills — they don't care how it got there. ### Verify ```sh gg --help ``` You should see `gg v` in the banner. ## 4. Render your first diagram Ask Claude in plain English: > Draw a gridgram diagram of a web app: browser client → API → Postgres > database with a Redis cache and a background job queue. Save it to > `~/first-diagram.svg`. Behind the scenes Claude will: - Invoke `/gg-icons` to find Tabler icons for browser/api/database/cache/queue. - Invoke `/gg-author` to compose a `.gg` source file and validate it. - Invoke `/gg-render` to produce the SVG and read back diagnostics. Open the SVG in a browser. You'll see a small architecture diagram. Claude will also have shown you the `.gg` source — save it somewhere if you want to edit it later. ### If the first render has warnings The `--diagnostics` channel reports non-fatal issues (unresolved icons show as red rings, labels that can't be placed cleanly, …). Just tell Claude: > That diagram has a red ring around the cache node. Fix it. Claude re-runs `/gg-icons` for the offending concept, patches the `.gg` source, and re-renders. The whole loop stays inside the chat. ## 5. Keep everything up to date Two moving parts: the plugin (skills + SKILL.md bodies) and the `gg` binary itself. ### Update the plugin ```text /plugin marketplace update ideamans-plugins /reload-plugins ``` The marketplace entry uses a `git-subdir` source pinned to gridgram's default branch, so this pulls the latest skills automatically. ### Update the `gg` binary Just ask: > Update my gridgram CLI to the latest version. `/gg-install` detects the existing `gg` on `PATH`, downloads the newest release, and replaces the binary in the same directory. No sudo prompt unless the existing location needs it (rare for `~/.local/bin`). Or directly: ```text /gg-install ``` The skill always checks for a newer version; if you're already on latest it says so and exits. ## 6. Uninstall (optional) ```text /plugin uninstall gridgram@ideamans-plugins /plugin marketplace remove ideamans-plugins ``` To remove the `gg` binary: ```sh rm "$(which gg)" rm -rf ~/.cache/gridgram # runtime-fetched sharp cache ``` ## Troubleshooting ### `/plugin marketplace add` fails with `Premature close` The non-interactive git clone inside Claude Code can hit this on hosts where the `owner/repo` shorthand SSH path isn't set up. Retry with the full HTTPS URL: ```text /plugin marketplace add https://github.com/ideamans/claude-public-plugins.git ``` ### `/gg-install` stages the binary at `/tmp/gg` but no `$PATH` dir is writable Run the `sudo mv` command the skill prints, then verify with `gg --help`. Alternatively, add a user-writable directory to your `PATH` in `~/.zshrc` / `~/.bashrc`: ```sh export PATH="$HOME/.local/bin:$PATH" ``` Reopen your shell and rerun `/gg-install`. ### PNG rendering fails on first use The PNG pipeline (sharp + libvips) is fetched lazily on first PNG render and cached in `~/.cache/gridgram/`. If that fetch fails (flaky network, older `gg` version), retry a second time. Versions older than 0.4.0 have a known Linux-arm64 bug with detect-libc resolution; upgrade with `/gg-install`. ## Where next - [CLI reference](./cli) — what `gg llm` and `gg icons` accept in detail, useful when you want to drive them by hand or understand what the skills do internally. - [`gh skill` tutorial](./gh-skill) — install the same skill bundle into Copilot / Cursor / Gemini / Codex. - [context7 tutorial](./context7) — give agents MCP-driven retrieval of gridgram docs with no plugin install. --- # CLI reference for AI workflows This is a **reference page**, not a tutorial. Start with one of the tutorials ([Claude plugin](./claude-plugin) / [`gh skill`](./gh-skill) / [context7](./context7)) if you haven't installed anything yet. Two `gg` subcommands exist specifically for LLM consumption. Neither needs network access; both are bundled in the `gg` single-file binary. The skills in `plugins/gridgram` drive them on the agent's behalf; you can also invoke them directly from a shell. ## `gg llm` Emits a self-contained reference that teaches an agent everything it needs to author `.gg` files. Pipe it into your agent's context on session start. ### Markdown (default) ```sh gg llm ``` The output covers: - CLI usage for every subcommand - `.gg` grammar (BNF) and statement semantics - Document-level settings (`doc { … }` keys) - Icon resolution order (inline map → `--icons` → built-in Tabler → error) - Exit codes (0 success, 1 parse error, 2 integrity error, 3 I/O) - JSON envelope shape returned by `gg render --format json` - Canonical examples from the `examples/` directory - Best-practices checklist for agent-authored diagrams The Markdown is regenerated from source (the BNF comment in `src/gg/dsl.ts`, the citty arg schema, the real example files) on every build, so it can never drift from the implementation. ### JSON ```sh gg llm --format json ``` Returns a structured view that agents can parse without worrying about Markdown heuristics: ```json { "version": "0.4.0", "iconCounts": { "outline": 5039, "filled": 1053, "total": 6092 }, "grammar": "…BNF extracted from dsl.ts…", "reference": "…full Markdown body as a string…" } ``` Useful when the agent just needs the version or the grammar block and doesn't want to eat the rest of the Markdown. ### Agent prompt template A good opening turn for a gridgram-authoring session: ```text You are going to author diagrams in the gridgram `.gg` format. The complete reference follows. It covers the grammar, CLI, icon naming, and the JSON output shape. --- --- When you produce a `.gg` file: 1. Validate with `gg --format json --diagnostics --stdout` 2. If the exit code is non-zero or diagnostics is non-empty, fix and retry. 3. Report the final `.gg` plus the diagnostics array. ``` ## `gg icons` Semantic search over the 6,092 built-in Tabler icons (5,039 outline + 1,053 filled). The index is generated at build time from Tabler's own metadata, unioned with gridgram-authored synonym tags in `src/data/icon-tags.json`. ### Commands ```sh # Exact-concept search. gg icons --search database --limit 5 # Browse available tags (useful when the keyword isn't obvious). gg icons --tags --limit 30 # Filter by tag. gg icons --tag queue --limit 10 # Restrict to a specific style. gg icons --set tabler-filled --search star # JSON output for agent consumption. gg icons --search loadbalancer --format json --limit 3 ``` ### Output formats **Plain text** (default) — tab-separated columns: `ref / label / category / tags`: ``` tabler/database Database Database storage,data,memory,… tabler/filled/database Database Database storage,data,memory,… tabler/database-cog Database cog Database cog,configuration,… ``` **JSON** — each hit includes its score: ```json [ { "name": "database", "set": "tabler-outline", "ref": "tabler/database", "label": "Database", "category": "Database", "tags": ["storage", "data", "memory", "database", …], "score": 10 }, … ] ``` ### Scoring | Score | Match type | | ----- | ------------------- | | 10 | exact name | | 7 | name prefix | | 5 | exact tag | | 4 | name substring | | 3 | label substring | | 2 | category substring | | 1 | tag substring | Results are sorted by score descending, then alphabetically for ties. Anything above score 5 is almost always the right pick. ### Recommended agent flow 1. **Try direct search first.** `gg icons --search --format json --limit 5`. 2. **If top score ≤ 2, pivot on tags.** `gg icons --tags --limit 30` shows the frequency ranking; pick a related tag, then `gg icons --tag --limit 15`. 3. **Choose a reference string to embed.** Output the picked icon as `tabler/` (outline) or `tabler/filled/` (filled). ### Why the scoring matters Tabler tags are rich (often 5–15 tags per icon), but they were written for human browsing, not for architecture-diagram vocabulary. The gridgram-authored overrides in `src/data/icon-tags.json` patch the common misses: - `cache` → `tabler/bolt`, `tabler/clock-play` - `microservice` → `tabler/box`, `tabler/puzzle` - `kubernetes` → `tabler/box-multiple` - `websocket` → `tabler/plug`, `tabler/route` - `loadbalancer` → `tabler/arrows-split`, `tabler/route` - `frontend` → `tabler/browser`, `tabler/world`, `tabler/layout` - `backend` → `tabler/server`, `tabler/database` - `payment` → `tabler/credit-card`, `tabler/cash` If you hit a concept that still returns poor results, file an issue — the tag overrides are a user-facing knob intended to be extended. ## Verifying a rendered diagram Every agent workflow should end in a validation step. `gg render` combines the SVG output with a diagnostics stream: ```sh gg render diagram.gg --format json --diagnostics --stdout > /tmp/envelope.json 2>/tmp/diag.json ``` - Exit code `1` → parse error. The error message is on stderr. - Exit code `2` → integrity error (unknown node reference, region not 4-connected, etc.). - Exit code `0` with `/tmp/diag.json === "[]"` → clean render. - Exit code `0` with non-empty diagnostics → SVG produced, but with warnings (missing icon, failed routing, etc.) that an agent should surface or retry on. See the [Diagnostics page](/en/developer/diagnostics) for the full diagnostic shape. --- # context7 tutorial [context7](https://context7.com/) indexes open-source documentation for retrieval over [MCP](https://modelcontextprotocol.io/). gridgram registers itself with context7 via a `context7.json` manifest, so any MCP-connected agent can query gridgram's docs without installing anything gridgram-specific. **Time**: ~5 minutes (most of it is configuring your MCP client). **Result**: your agent can pull gridgram docs on demand. Unlike [the Claude plugin](./claude-plugin) or [`gh skill`](./gh-skill), this path is **retrieval-only** — it gives the agent a searchable view of gridgram's documentation but no slash commands and no automatic CLI driving. For a full authoring workflow, pair it with one of the other two. ## Prerequisites - **An MCP-capable agent host**: Claude Code, Cursor, or anything else that can run [MCP servers](https://modelcontextprotocol.io/). - **[context7 MCP server](https://github.com/upstash/context7)** — usually added as an MCP server in the agent's config. - **Network access** to `context7.com`. No git, no `gh`, no binaries on PATH. Everything is pulled over MCP at query time. ## 1. Add the context7 MCP server ### Claude Code Claude Code's MCP configuration lives in `~/.claude/settings.json` (user scope) or `.claude/settings.json` (project scope). Add: ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp@latest"] } } } ``` Restart Claude Code. The `context7` tools should now appear in `/mcp`. ### Cursor and others Follow each host's MCP configuration docs; the `command` + `args` above is portable. Cursor writes MCP settings to `~/.cursor/mcp.json`. ## 2. Verify context7 sees gridgram Ask your agent: > Look up the `gridgram` library on context7. The agent calls `resolve-library-id("gridgram")`, which should return something like `/ideamans/gridgram`. If it returns "not found", the library hasn't been indexed yet — see [Registration](#registration-if-not-yet-indexed) below. ## 3. Query gridgram docs Try: > Using context7, fetch gridgram's `.gg` grammar reference and > explain how to declare an `icon` node. The agent calls `query-docs` against the resolved library id and gets back the relevant doc sections. Because context7 indexes `docs/en`, `examples/`, and `src/generated/` (per the `context7.json` manifest), the agent sees: - The English user + developer + AI guide pages (this whole site). - Canonical `.gg` examples. - The generated `src/generated/llm-reference.md` bundle. It does **not** see `docs/ja/`, tests, or internal source files. ## 4. Pair context7 with authoring context7 alone can't run `gg`. Combine it with one of: - [Claude Code plugin](./claude-plugin) — the most ergonomic pairing; slash commands render while context7 provides retrieval for anything the skill bodies don't cover. - [`gh skill`](./gh-skill) — same skills in other hosts. The agent will naturally use context7 when it needs background information and the plugin skills when it needs to invoke `gg`. ## 5. Staying up to date context7 **re-indexes automatically** from the GitHub repository whenever gridgram's default branch changes. There's nothing to update on your side; a new release of gridgram becomes available in context7 within a few minutes of the merge. You don't "install" a version of context7-backed gridgram docs — the agent always gets the current default branch view. ## Registration (if not yet indexed) If `resolve-library-id("gridgram")` returns "not found", context7 hasn't seen the repo yet. gridgram's `context7.json` is already in place at the repo root, but the initial submission happens once per library: - Visit . - Paste `https://github.com/ideamans/gridgram`. - context7 crawls the repo, validates the manifest, and indexes. Once listed on , every MCP client can discover it. This step is only needed once per library; future re-indexes happen automatically. ## The `context7.json` manifest For reference, gridgram's manifest at the repo root tells context7: - Index `docs/en`, `examples`, and `src/generated`. - Skip `docs/.vitepress`, `docs/ja`, `node_modules`, `tests`, `dist`, and `src/data`. - Apply a small set of hand-picked **rules** — the non-obvious constraints agents are most likely to get wrong (Preact not React, `doc { … }` replaced the legacy `%%{…}%%` directive, diagnostics are warnings not exceptions, etc.). See the [manifest on GitHub](https://github.com/ideamans/gridgram/blob/main/context7.json) for the current content. ## Troubleshooting ### Agent says "no MCP tools available" Restart the host. Most MCP clients only scan the config on startup. ### `resolve-library-id` returns a close-but-wrong match Specify the org: `resolve-library-id("ideamans/gridgram")`. Otherwise you may hit a different library called `gridgram` indexed earlier. ### Docs feel stale The agent might be using its own training-time knowledge instead of calling context7. Ask explicitly: > Query context7 for the latest gridgram CLI flags. ## Where next - [Claude Code plugin](./claude-plugin) — pair context7 with slash-command authoring. - [`gh skill`](./gh-skill) — same skill bundle in other hosts. - [llms.txt](https://gridgram.ideamans.com/llms.txt) / [llms-full.txt](https://gridgram.ideamans.com/llms-full.txt) — if you'd rather hand the docs to an agent as a single payload without MCP. --- # `gh skill` tutorial The GitHub CLI ships a `gh skill` subcommand (v2.90+) that installs Agent Skills from any GitHub repository — not just Anthropic ones. gridgram's four skills (`gg-install`, `gg-render`, `gg-icons`, `gg-author`) use standards-only frontmatter, so the same bundle you see in the Claude marketplace also works in Copilot, Cursor, Gemini CLI, Codex, and Antigravity. **Time**: ~5 minutes. **Result**: the gridgram skills installed into your agent of choice, plus the update command. ## Prerequisites Install these yourself: - [**GitHub CLI (`gh`)**](https://cli.github.com/), **v2.90 or later** (earlier versions don't have the `skill` subcommand). - **A target agent host**: Claude Code, GitHub Copilot, Cursor, Gemini CLI, Codex, or Antigravity. The `--agent` flag picks which. - **git** (gh uses it under the hood). - **Network access** to `github.com`. Authenticate gh once if you haven't already: ```sh gh auth status # check gh auth login # if "not logged in" ``` Authentication isn't strictly required for a public repo like gridgram, but it avoids rate-limiting on repeated installs. Check your `gh skill` availability: ```sh gh skill --help ``` If you see "unknown command: skill", upgrade gh: ```sh # Homebrew brew upgrade gh # Debian/Ubuntu sudo apt install --only-upgrade gh ``` ## 1. Pick your target agent `gh skill install --agent ` supports six host targets. Each tutorial snippet from here on has a tab per target — pick yours and the commands copy-paste directly. | Target | Where skills end up | | --------------- | ------------------------------------------------------------------ | | `claude-code` | `~/.claude/skills//SKILL.md` | | `copilot` | GitHub Copilot's skills directory (default if `--agent` is omitted)| | `cursor` | Cursor's skill directory | | `codex` | OpenAI Codex | | `gemini-cli` | Google Gemini CLI | | `antigravity` | Antigravity | ## 2. Preview before installing `gh skill preview` downloads a `SKILL.md` and shows the frontmatter + body so you can read what it'll do before letting it run: ```sh gh skill preview ideamans/gridgram plugins/gridgram/skills/gg-install ``` Do the same for the other three (`gg-render`, `gg-icons`, `gg-author`) if you want to inspect each. ## 3. Install the skills Install all four with one copy-paste. Switch tabs to match your target agent: ::: code-group ```sh [Claude Code] gh skill install ideamans/gridgram plugins/gridgram/skills/gg-install --agent claude-code gh skill install ideamans/gridgram plugins/gridgram/skills/gg-render --agent claude-code gh skill install ideamans/gridgram plugins/gridgram/skills/gg-icons --agent claude-code gh skill install ideamans/gridgram plugins/gridgram/skills/gg-author --agent claude-code ``` ```sh [Copilot] gh skill install ideamans/gridgram plugins/gridgram/skills/gg-install --agent copilot gh skill install ideamans/gridgram plugins/gridgram/skills/gg-render --agent copilot gh skill install ideamans/gridgram plugins/gridgram/skills/gg-icons --agent copilot gh skill install ideamans/gridgram plugins/gridgram/skills/gg-author --agent copilot ``` ```sh [Cursor] gh skill install ideamans/gridgram plugins/gridgram/skills/gg-install --agent cursor gh skill install ideamans/gridgram plugins/gridgram/skills/gg-render --agent cursor gh skill install ideamans/gridgram plugins/gridgram/skills/gg-icons --agent cursor gh skill install ideamans/gridgram plugins/gridgram/skills/gg-author --agent cursor ``` ```sh [Codex] gh skill install ideamans/gridgram plugins/gridgram/skills/gg-install --agent codex gh skill install ideamans/gridgram plugins/gridgram/skills/gg-render --agent codex gh skill install ideamans/gridgram plugins/gridgram/skills/gg-icons --agent codex gh skill install ideamans/gridgram plugins/gridgram/skills/gg-author --agent codex ``` ```sh [Gemini CLI] gh skill install ideamans/gridgram plugins/gridgram/skills/gg-install --agent gemini-cli gh skill install ideamans/gridgram plugins/gridgram/skills/gg-render --agent gemini-cli gh skill install ideamans/gridgram plugins/gridgram/skills/gg-icons --agent gemini-cli gh skill install ideamans/gridgram plugins/gridgram/skills/gg-author --agent gemini-cli ``` ```sh [Antigravity] gh skill install ideamans/gridgram plugins/gridgram/skills/gg-install --agent antigravity gh skill install ideamans/gridgram plugins/gridgram/skills/gg-render --agent antigravity gh skill install ideamans/gridgram plugins/gridgram/skills/gg-icons --agent antigravity gh skill install ideamans/gridgram plugins/gridgram/skills/gg-author --agent antigravity ``` ::: Verify: ```sh gh skill list ``` The list shows each skill plus the source repository + ref + tree SHA. That provenance metadata gets written directly into each `SKILL.md`'s frontmatter on install, so `gh skill update` can detect content changes later even without a version bump. ## 4. Install the `gg` CLI The skills shell out to the `gg` binary. Either path works: ### Option A: Use `/gg-install` inside your agent Restart your agent host so the new skills load, then ask: > Install the gridgram CLI for my platform. The agent picks up `/gg-install` and performs: 1. OS + arch detection. 2. Download of the latest release from . 3. Install into the first writable `$PATH` directory (or stage at `/tmp/gg` with a sudo hint). ### Option B: Install `gg` the regular way If you'd rather skip the skill, follow the standard install guide at **[Quick start](/en/guide/)** (one-line curl / PowerShell scripts) or **[Install](/en/guide/install)** for manual alternatives. Any install that puts `gg` on `$PATH` is picked up by the skills automatically. ### Verify ```sh gg --help ``` You should see `gg v`. ## 5. Try a first render > Draw a gridgram diagram showing a browser calling an API backed by > a Postgres database. Save it as `~/first-diagram.svg`. The agent uses `/gg-icons` to pick icons, `/gg-author` to compose the `.gg` source, and `/gg-render` to produce the SVG with diagnostics. Open the result in a browser. ## 6. Keep everything up to date ### Update skills ```sh gh skill update ``` With no arguments it updates every installed skill. The provenance metadata in each `SKILL.md` tells gh where to fetch from — the tree SHA comparison means you'll get genuine content changes, not spurious no-op updates. Update a single skill: ```sh gh skill update ideamans/gridgram plugins/gridgram/skills/gg-author ``` ### Update the `gg` binary Just ask the agent: > Update my gridgram CLI to the latest version. `/gg-install` replaces the existing binary in place. ## 7. Uninstall (optional) ```sh gh skill remove ideamans/gridgram plugins/gridgram/skills/gg-author # …and the other three ``` To remove `gg`: ```sh rm "$(which gg)" rm -rf ~/.cache/gridgram ``` ## Troubleshooting ### `unknown command: skill` Upgrade gh to **v2.90+**. Check with `gh --version`. ### "Rate limit exceeded" from `gh skill install` Run `gh auth login` and retry — authenticated requests get a higher rate limit. ### Skill installed but agent doesn't see it Restart the agent host. Most hosts only scan the skills directory at startup. ### Skill with extra frontmatter fields logs a warning Expected: Claude Code's frontmatter extensions (`disable-model-invocation`, `paths`, …) aren't in gridgram's SKILL files, so this shouldn't happen. If it does, file an issue. ## Where next - [Claude Code plugin tutorial](./claude-plugin) — slash-command-driven flow for Claude Code users, with marketplace-level auto-update. - [CLI reference](./cli) — what the skills are doing under the hood. - [context7](./context7) — complementary MCP retrieval; works alongside any skill host. --- # `llms.txt` — public discovery files gridgram's docs site serves two files at the root of the deployed domain. They follow the [llmstxt.org](https://llmstxt.org/) convention and the Mintlify / Anthropic "full bundle" convention respectively. | File | Size | Contents | | ------------------------------------------------------------------------ | --------- | -------------------------------------------------------------------------------------------------------------- | | [`/llms.txt`](https://gridgram.ideamans.com/llms.txt) | ~5 KB | Markdown index: project name, one-line summary, grouped links to every English docs page plus the LLM bundle. | | [`/llms-full.txt`](https://gridgram.ideamans.com/llms-full.txt) | ~230 KB | Every docs page's markdown body concatenated end-to-end, followed by the full [`gg llm`](./cli#gg-llm) reference. | Both are regenerated from `docs/en/**` at docs build time (`bun run build-llms-txt`), so the links and content always match the live site. ## When to use which - **`/llms.txt`** — when an agent is crawling and needs an index to decide what to fetch next. Every link points at the raw `.md` source of a page (not the rendered HTML), so the agent can consume it directly. - **`/llms-full.txt`** — when you want to **hand an agent the whole docs tree plus the grammar reference in one payload**. Around 230 KB fits comfortably in modern context windows; saves the agent from N round-trips. ## Quick examples ### Feed the bundle to a CLI agent ```sh curl -s https://gridgram.ideamans.com/llms-full.txt | your-agent --context - ``` ### Check the index from a shell ```sh curl -s https://gridgram.ideamans.com/llms.txt | head -30 ``` ### Extract the LLM reference block ```sh curl -s https://gridgram.ideamans.com/llms-full.txt \ | sed -n '//,$p' ``` ## Relationship to the other surfaces These files are a **discovery** channel, not a replacement for the other paths in this guide: - If you control an agent and can install plugins, the [Claude plugin](./claude-plugin) or [`gh skill`](./gh-skill) gives you slash-command authoring on top of the CLI. - If you want retrieval over MCP (the `context7` protocol), [context7](./context7) indexes the same docs but exposes them through an MCP server. - If you only have read-only HTTP, `llms.txt` / `llms-full.txt` is the zero-setup path — no auth, no config, just `curl`. An agent that walks `/llms.txt` can find everything else (including `/llms-full.txt` itself, the GitHub releases, and the plugin install commands), so it's a good single entry point if you're not sure what's available. ## Languages Only the English docs (`docs/en/**`) feed these files. Japanese pages (`docs/ja/**`) are not indexed — gridgram is registered as an English-primary library on context7, and keeping a single agent-facing locale avoids the risk of agents mixing translations mid-answer. ## Source The generator is `scripts/build-llms-txt.ts` in the gridgram repo. It's wired into `bun run ai:regen` and into `docs:build`, so a fresh deploy always ships current files. The source of truth is simply `docs/en/**/*.md` — there's no hand-maintained link list. --- # Developer Guide You're here because you want to drive Gridgram from code — a Preact or plain-ESM browser app, a Node server that renders on demand, an agent that edits `.gg` and re-renders in a loop, a build step in CI, or an MCP tool exposing diagramming to an LLM. The [User Guide](/en/guide/) covers authoring: writing `.gg` files and running the `gg` CLI. This side covers the TypeScript API — what the npm package exports, what runs where, and the structured feedback surface agents consume. ## Two entry points ```ts // Browser-safe. Pure — no fs, no path, no network. import { renderDiagram, parseGg, Diagram /* … */ } from 'gridgram' // Node-only. Reads the filesystem (and optionally fetches over HTTP). import { buildIconContext, loadProjectConfig } from 'gridgram/node' ``` The main entry point works in every ESM host — modern browsers (via Vite / Rspack / esbuild / any bundler), Node ≥ 22, Bun, Deno. The `gridgram/node` subpath is only safe inside a Node runtime; browser bundles that accidentally import it will fail to build. ## Rendering primitives Four entry points cover every integration pattern: | Import | Shape | When to reach for it | |--------------------------------|---------------------------------------------------------|------------------------------------------------------------------| | `renderDiagram(def, opts)` | `(DiagramDef, opts?) => { svg, diagnostics }` | Default choice. SVG string plus layout/icon feedback. | | `renderDiagramSvg(def, opts)` | `(DiagramDef, opts?) => string` | SVG only — the caller already knows the input is clean. | | `Diagram` | Preact FC — `` | Embedding inline in a Preact app. | | `buildDiagramTree(def, opts)` | `(DiagramDef, opts?) => VNode` | A raw Preact VNode tree for custom renderers / SSR pipelines. | All four ultimately take a `DiagramDef`. You can construct one programmatically or produce one from `.gg` source via `parseGg`. ## Reading order 1. **[Quickstart (TS API)](./quickstart)** — install, render your first diagram from code. 2. **[`renderDiagram` & friends](./render)** — the four entry points in depth, including ``, `RenderResult`, and `computeRenderDimensions`. 3. **[Types](./types)** — `DiagramDef` and its members. Normalized vs raw-input shapes. 4. **[Configuration](./config)** — the layered settings system (system → project → document → render override) and how every surface composes it. 5. **[Parser](./parser)** — `.gg` source to `DiagramDef`. `parseGg`, icon resolution, `GgError`, `checkIntegrity`. 6. **[Diagnostics](./diagnostics)** — the `PlacementDiagnostic` stream. Essential for agent workflows; useful for human authors too. 7. **[Integrations](./integrations)** — HTTP endpoint, MCP tool, headless rendering in CI, Preact embedding, PNG. 8. **[Specification](./spec)** — the reference: token grammar, resolution pipeline, determinism guarantees. ## Determinism is a contract Every Gridgram surface (CLI, TS API, HTTP, MCP) drives the same pipeline. Given the same `DiagramDef` — same nodes, same settings, same icon sources — the pipeline produces **byte-identical SVG** output across surfaces and runs. Agents rely on this: - Commit the `.gg` source to git. Every AI edit reads as a diff. - Pin an SVG baseline in CI. A rendering regression shows up as a visible change. - Skip the icon loader by pre-resolving `src` to inline SVG, and the renderer is fully pure — no I/O at all. The [Specification](./spec) page covers the invariants formally. --- # Configuration Gridgram resolves settings through four layers, in order of increasing precedence: ``` system defaults ↓ project config (gridgram.config.{ts,js,json,json5}) ↓ document (doc { … } block inside a .gg file) ↓ render override (CLI flag / DiagramOptions on a render call) ``` Every surface — CLI, TS API, HTTP endpoint, MCP tool — builds an array of `DiagramSettings` layers in this order and calls `resolveSettings()`. No surface branches past the resolver. ## The layer types ```ts interface DiagramSettings { cellSize?: number padding?: number columns?: number rows?: number theme?: Partial renderWidth?: number suppressErrors?: boolean assetAliases?: Record iconsDir?: string } interface ResolvedSettings extends Required> { padding?: number columns?: number rows?: number theme: DiagramTheme renderWidth?: number iconsDir?: string } ``` Every `DiagramSettings` field is optional. `ResolvedSettings` guarantees the fields that always have a sane default (`cellSize`, `theme`, `suppressErrors`, `assetAliases`). ## `resolveSettings(layers)` ```ts import { resolveSettings, SYSTEM_DEFAULTS } from 'gridgram' const resolved = resolveSettings([ projectLayer, // from gridgram.config.ts documentLayer, // from doc { } in the .gg renderOverride, // CLI flag or DiagramOptions ]) ``` Merge rules: - Scalars (`cellSize`, `renderWidth`, …) — last layer wins. - `theme` — deep-merged: each layer overlays its keys onto the previous theme. - `assetAliases` — shallow-merged: later entries replace earlier ones by alias name. - `SYSTEM_DEFAULTS` is always the starting point; you never need to include it explicitly. ## Project config files A project-wide baseline lives in the nearest `gridgram.config.*` walking up from `cwd`. The CLI and the library-level `loadProjectConfig` helper both use this discovery. ```ts // gridgram.config.ts import { defineConfig } from 'gridgram' export default defineConfig({ cellSize: 128, theme: { primary: '#1e3a5f', accent: '#e8792f', }, assetAliases: { brand: './assets/logos', // → '@brand/aws.svg' resolves under here }, }) ``` Supported extensions: `.ts`, `.mjs`, `.js`, `.cjs`, `.json5`, `.json`. TS/JS files can be fully typed via `defineConfig`, which is an identity helper: ```ts import { defineConfig } from 'gridgram' export default defineConfig({ … }) ``` ### Loading a config from code `loadProjectConfig` reads the filesystem, so it lives on the `'gridgram/node'` subpath: ```ts import { loadProjectConfig } from 'gridgram/node' import { resolveSettings } from 'gridgram' const found = await loadProjectConfig({ cwd: process.cwd() }) const projectLayer = found?.settings ?? {} const resolved = resolveSettings([projectLayer, renderOverride]) ``` Pass `explicitPath` to skip walk-up discovery. Returns `null` if no config is found (no error — projects without one are fine). In a browser bundle this import will fail. If you want per-project defaults in a browser app, serialize them into a plain `DiagramSettings` object at build time and import it as regular data. ## The `doc { }` layer A `.gg` file's `doc { }` block provides the document-layer settings: ```gg doc { cellSize: 256, theme: { primary: '#1e3a5f' }, } icon @A1 tabler/user "U" ``` When rendered through the CLI, this sits between the project config and any `--cell-size` / `--width` flags. Via the TS API it ends up on `rawDef.cellSize` / `.theme` after `parseGg`; `foldLayers` (inside `renderDiagram`) merges those into the document layer automatically. ## `SYSTEM_DEFAULTS` ```ts import { SYSTEM_DEFAULTS } from 'gridgram' SYSTEM_DEFAULTS = { cellSize: 256, suppressErrors: false, assetAliases: {}, theme: { primary: '#1e3a5f', secondary: '#3b5a80', accent: '#e8792f', text: '#2d3748', bg: '#ffffff', }, } ``` Read-only. Exported so callers can introspect (for example, a settings UI that shows "default" alongside an override). ## Surface-specific composition ### CLI ``` [projectLayer, documentLayer, cliFlagOverrides] ``` Walks up from `--config ` or cwd; parses the .gg source for its `doc { }`; collects CLI flags into the last layer. ### TS API (single call) ``` [documentLayer, opts] ``` `documentLayer` is extracted from the raw `DiagramDef`'s own `cellSize` / `theme` / etc. on the fly. `opts` is what you pass to `renderDiagram(def, opts)`. ### TS API (with a project config) Call `loadProjectConfig` yourself and prepend: ```ts const { settings: projectLayer } = (await loadProjectConfig({})) ?? {} const resolved = resolveSettings([projectLayer ?? {}, documentLayer, opts]) ``` ### HTTP / MCP Same shape as the TS API. The server decides whether a project config applies (usually yes — a single config per deployment) and prepends it. ## See also - [`renderDiagram` & friends](./render) — the settings flow into `foldLayers` automatically. - [Parser](./parser) — `parseGg` extracts the document layer from `doc { }`. - [User Guide: Document](../guide/document/merging) — same rules from the author's perspective. --- # Diagnostics Gridgram's pipeline doesn't just produce an SVG — it produces structured feedback about anything that didn't fit cleanly. Labels that couldn't find a collision-free slot, connectors that couldn't route around obstacles, icon sources that failed to resolve — each becomes a `PlacementDiagnostic` record the caller can inspect, print, or hand to an AI agent for remediation. This is the primary surface agents use to iterate on a diagram. ## The `PlacementDiagnostic` shape ```ts interface PlacementDiagnostic { kind: | 'label-collision' // a label was forced into an overlapping fallback | 'route-failed' // a connector couldn't route around obstacles | 'icon-unresolved' // node.src / badge.icon didn't resolve | 'region-disjoint' // (reserved; currently surfaces as an integrity error) severity: 'warning' | 'error' element: ElementRef message: string // human-readable one-liner suggestion?: string // optional remediation hint finalRect?: PixelRect // where the element actually drew attempts?: PlacementAttempt[] // full placement search history iconSrc?: string // icon-unresolved: the original DSL id iconReason?: 'not-found' | 'load-failed' | 'malformed' } ``` Every diagnostic carries enough context to: 1. **Identify the element** that had trouble (`element` + optional `finalRect` to pinpoint the drawn position). 2. **See what went wrong** (`message`, the `attempts` search history with `obstacles` at each tried slot). 3. **Choose a fix** — either automatically (agent) or manually (human reading CLI output). ## `ElementRef` ```ts type ElementRef = | { kind: 'node'; id: string; pos?: GridCellRef; line?: number } | { kind: 'note'; id: string; pos?: GridCellRef; line?: number } | { kind: 'region'; id?: string; span: GridSpanRef; line?: number } | { kind: 'connector'; id?: string; from: string; to: string; line?: number } interface GridCellRef { col: number; row: number; address: string } // 1-based interface GridSpanRef { from: GridCellRef; to: GridCellRef } ``` All coordinates here are **1-based with A1 addresses** — the same form an agent / human wrote in the source. No 0-based internal coordinates reach this boundary. ## `PlacementAttempt` ```ts interface PlacementAttempt { slot: string // human-readable ("top-right", "seg 2 / t=0.5 above") rect: PixelRect // where the label would have drawn obstacles: Obstacle[] // everything that blocked this slot accepted: boolean // the winning slot (last entry) vs rejected tries } ``` The last attempt in the array is always marked `accepted: true` — either a clean slot, or the fallback that got picked anyway even with obstacles. ## `Obstacle` ```ts type Obstacle = | { kind: 'label'; owner: ElementRef; rect: PixelRect } | { kind: 'icon'; owner: ElementRef; circle: PixelCircle } | { kind: 'line'; owner: ElementRef; line: PixelLine } | { kind: 'leader'; owner: ElementRef; line: PixelLine } | { kind: 'canvas-bounds'; bounds: { width: number; height: number } } ``` `owner` points back to the element whose label/icon/line blocked the placement. Pixel geometry lets an agent reason quantitatively about how deep the overlap went. ## Example: a label-collision diagnostic ```json { "kind": "label-collision", "severity": "warning", "element": { "kind": "node", "id": "api", "pos": { "col": 2, "row": 1, "address": "B1" } }, "message": "Label for node \"api\" could not find a clear slot across 7 candidates; final fallback still blocked by icon of node \"db\", label of node \"web\".", "finalRect": { "x": 512, "y": 200, "w": 82, "h": 28 }, "attempts": [ { "slot": "top-right", "rect": { "x": 520, "y": 148, "w": 82, "h": 28 }, "obstacles": [ { "kind": "icon", "owner": { "kind": "node", "id": "db", "pos": { "col": 2, "row": 2, "address": "B2" } }, "circle": { "cx": 512, "cy": 400, "r": 57.6 } } ], "accepted": false }, { "slot": "bottom-right", "rect": { "x": 520, "y": 252, "w": 82, "h": 28 }, "obstacles": [ { "kind": "line", "owner": { "kind": "connector", "from": "api", "to": "db" }, "line": { "x1": 512, "y1": 200, "x2": 512, "y2": 400 } } ], "accepted": false }, /* … five more attempts … */ { "slot": "top-left", "rect": { "x": 430, "y": 148, "w": 82, "h": 28 }, "obstacles": [/* fallback still collides */], "accepted": true } ] } ``` An agent reading this can: - Move `api` to an outer column — `C1` would clear `db`'s icon. - Shorten the `api` label so it fits in the narrower slots. - Remove the direct `api → db` connector or waypoint it. ## Example: a route-failed diagnostic ```json { "kind": "route-failed", "severity": "warning", "element": { "kind": "connector", "from": "a", "to": "b" }, "message": "Connector a→b crosses node(s) \"mid\" and no routed alternative was found; the line is drawn straight through.", "suggestion": "Move a / b to an outer cell, add waypoints to steer the connector, or relocate \"mid\" so a clear path exists.", "attempts": [{ "slot": "direct line", "rect": { "x": 0, "y": 0, "w": 0, "h": 0 }, "obstacles": [{ "kind": "icon", "owner": { "kind": "node", "id": "mid", "pos": { "col": 2, "row": 1, "address": "B1" } }, "circle": { "cx": 240, "cy": 120, "r": 58 } }], "accepted": true }] } ``` ## Example: an icon-unresolved diagnostic ```json { "kind": "icon-unresolved", "severity": "warning", "element": { "kind": "node", "id": "api" }, "message": "Node \"api\" src=\"tabler/userr\" could not be resolved (no matching Tabler icon or registered entry).", "iconSrc": "tabler/userr", "iconReason": "not-found" } ``` `iconReason` separates two distinct remediations: - `'not-found'` — no such tabler name, no registered bare name, no resolved path. **Typical fix: rename / register.** - `'load-failed'` — the loader attempted the source and got an I/O or network error. **Typical fix: check connectivity / path.** For `'load-failed'`, the loader's error message is included in the diagnostic's `message`. ## Getting diagnostics ### From the TS API ```ts import { renderDiagram, resolveDiagramIcons } from 'gridgram' const { def, diagnostics: iconDiagnostics } = resolveDiagramIcons(rawDef, ctx) const { svg, diagnostics: layoutDiagnostics } = renderDiagram(def) const all = [...iconDiagnostics, ...layoutDiagnostics] ``` ### From the CLI ```sh gg diagram.gg -o out.svg --diagnostics # ^ writes JSON array of diagnostics to stderr ``` Or bundled with `--format json`: ```sh gg diagram.gg --format json --stdout # { "def": { … resolved DiagramDef … }, # "diagnostics": [ … all diagnostics … ] } ``` A single stdout read gives an agent the resolved def plus its feedback. ## Writing an agent loop ```ts import { parseGg, resolveDiagramIcons, renderDiagram } from 'gridgram' import { buildIconContext } from 'gridgram/node' async function autoFixDiagram(source: string, maxAttempts = 5): Promise { for (let attempt = 0; attempt < maxAttempts; attempt++) { const { def, errors, icons } = parseGg(source) if (errors.length) throw new Error(errors.map((e) => e.message).join('\n')) const ctx = await buildIconContext({ jsonIconsMap: icons, def, docDir: '.' }) const iconResolve = resolveDiagramIcons(def, ctx) const rendered = renderDiagram(iconResolve.def) const diagnostics = [...iconResolve.diagnostics, ...rendered.diagnostics] if (diagnostics.length === 0) return rendered.svg // Hand the diagnostics to an LLM that edits the .gg source. source = await llm.rewriteDiagram(source, diagnostics) } throw new Error('diagram still has diagnostics after retry budget') } ``` The shape is chosen so the LLM can receive the diagnostics as JSON directly — no custom parsing layer. ## Severity - `'warning'` — the diagram still renders. Labels show in fallback positions; routes draw straight through obstacles. Red markers in the SVG (unless `suppressErrors: true`) make the issue visible. - `'error'` — the render is structurally broken. Currently unused at runtime (the reserved `region-disjoint` kind surfaces through `parseGg`'s integrity errors instead); kept in the type for future extension. ## See also - [`renderDiagram` & friends](./render) — returns `diagnostics`. - [Parser](./parser) — `resolveDiagramIcons` returns icon-unresolved diagnostics. - [Integrations](./integrations) — MCP and HTTP surfaces. --- # Integrations Patterns for embedding Gridgram in a host system. None of these are separate packages — they're conventions that compose the primitive API into whatever shape your host needs. > **Import boundary reminder:** the pure pipeline (`parseGg`, > `resolveDiagramIcons`, `renderDiagram`, …) comes from `'gridgram'` > and runs anywhere. Filesystem / HTTP helpers (`buildIconContext`, > `loadProjectConfig`) come from `'gridgram/node'` and only work in a > Node runtime. ## HTTP endpoint A minimal service that accepts `.gg` source and returns SVG + diagnostics: ```ts import { serve } from 'bun' import { parseGg, resolveDiagramIcons, renderDiagram } from 'gridgram' import { buildIconContext } from 'gridgram/node' serve({ port: 3000, async fetch(req) { if (req.method !== 'POST') return new Response('POST /render', { status: 405 }) const source = await req.text() const { def: rawDef, errors, icons } = parseGg(source) if (errors.length > 0) { return Response.json({ errors }, { status: 400 }) } const ctx = await buildIconContext({ jsonIconsMap: icons, def: rawDef, docDir: process.cwd(), }) const { def, diagnostics: iconDiags } = resolveDiagramIcons(rawDef, ctx) const { svg, diagnostics: layoutDiags } = renderDiagram(def) return Response.json({ svg, diagnostics: [...iconDiags, ...layoutDiags], }) }, }) ``` Notes: - No per-request project config — deploy-wide settings go in a `gridgram.config.ts` loaded once at startup and prepended via `resolveSettings` (see [Configuration](./config)). - Node.js works the same; swap `Bun.serve` for `http.createServer` or Fastify / Hono. ## MCP tool A Model Context Protocol server exposing Gridgram as an agent tool. Skeleton: ```ts import { Server } from '@modelcontextprotocol/sdk/server' import { parseGg, resolveDiagramIcons, renderDiagram } from 'gridgram' import { buildIconContext } from 'gridgram/node' const server = new Server({ name: 'gridgram', version: '0.1.0' }, { capabilities: { tools: {} }, }) server.setRequestHandler(CallToolRequestSchema, async (req) => { if (req.params.name !== 'render_diagram') throw new Error('unknown tool') const source = req.params.arguments?.source as string const { def: rawDef, errors, icons } = parseGg(source) if (errors.length) { return { content: [{ type: 'text', text: JSON.stringify({ errors }) }] } } const ctx = await buildIconContext({ jsonIconsMap: icons, def: rawDef, docDir: process.cwd(), }) const { def, diagnostics: iconDiags } = resolveDiagramIcons(rawDef, ctx) const { svg, diagnostics: layoutDiags } = renderDiagram(def) const diagnostics = [...iconDiags, ...layoutDiags] return { content: [ { type: 'text', text: JSON.stringify({ diagnostics }, null, 2) }, { type: 'image', data: Buffer.from(svg).toString('base64'), mimeType: 'image/svg+xml' }, ], } }) ``` The critical bit for agent usability: return the `diagnostics` array in the response so the LLM sees *why* its diagram didn't render cleanly, not just the red-tinted SVG. See [Diagnostics](./diagnostics) for an example agent loop. ## Headless build step In a static-site generator or CI job, process every `.gg` file in a directory: ```ts import { readdir, readFile, writeFile } from 'node:fs/promises' import { join, dirname } from 'node:path' import { parseGg, resolveDiagramIcons, renderDiagram } from 'gridgram' import { buildIconContext } from 'gridgram/node' async function buildAll(dir: string): Promise { for (const entry of await readdir(dir, { recursive: true })) { if (!entry.endsWith('.gg')) continue const path = join(dir, entry) const source = await readFile(path, 'utf-8') const { def: rawDef, errors, icons } = parseGg(source) if (errors.length) throw new Error(`${path}: ${errors[0].message}`) const ctx = await buildIconContext({ jsonIconsMap: icons, def: rawDef, docDir: dirname(path), }) const { def } = resolveDiagramIcons(rawDef, ctx) const { svg, diagnostics } = renderDiagram(def) if (diagnostics.length > 0) { console.warn(`${path}:`, diagnostics.map((d) => d.message).join('\n ')) } await writeFile(path.replace(/\.gg$/, '.svg'), `\n${svg}`) } } ``` The docs site in this repository uses exactly this pattern — see `scripts/build-docs-examples.ts` for the full version (adds PNG rasterization via `sharp` and a `.gg` / `.ts` parity check). ## PNG rasterization Gridgram doesn't ship `sharp` as a dependency — the TS API renders SVG only, and `sharp` is a heavy native module that belongs to the host. Install it when you need PNG: ```sh npm install sharp ``` ```ts import sharp from 'sharp' import { renderDiagram, computeRenderDimensions } from 'gridgram' const { svg } = renderDiagram(def, { renderWidth: 2048 }) const { width, height } = computeRenderDimensions(def, { renderWidth: 2048 }) await sharp(Buffer.from(svg)) .resize(width, height) .png() .toFile('out.png') ``` `computeRenderDimensions` and `renderDiagram` take the same `DiagramOptions`, so the aspect ratio lines up. Alternative — the `gg` CLI bundles sharp (lazy-loaded into `~/.cache/gridgram/` on first PNG render). If your pipeline already has access to the binary, shelling out to `gg -o out.png` is often simpler than wiring sharp into your app. ## Preact / browser embedding If your host is a Preact application (or any ES-module browser app bundled by Vite / Rspack / esbuild), use the `` component: ```tsx import { Diagram } from 'gridgram' import type { DiagramDef } from 'gridgram' export function DiagramView({ def }: { def: DiagramDef }) { return } ``` Or drop to the raw VNode tree with `buildDiagramTree` if you want to wrap it yourself. Only the pure entry point (`'gridgram'`) is used — safe to ship to browsers. `parseGg` and `resolveDiagramIcons` are pure too, so a browser-side live editor only needs those plus the rendering functions. See the [docs/.vitepress/theme/Editor.vue](https://github.com/ideamans/gridgram/blob/main/docs/.vitepress/theme/Editor.vue) source for a complete example. ## Workers / worker threads `renderDiagram` is pure and thread-safe — no module-level mutable state, no globals. Each worker can import `gridgram` and call it independently. The Tabler icon JSON is read-only and shared safely. ## Caching For a memoized render layer: ```ts const cache = new Map() function renderCached(def: DiagramDef): string { const key = JSON.stringify(def) // good enough for equality const cached = cache.get(key) if (cached !== undefined) return cached const { svg } = renderDiagram(def) cache.set(key, svg) return svg } ``` Because `renderDiagram` is deterministic, a cache keyed on the full input def is sound. If you've resolved icons upstream, the def's `src` fields are inline SVG strings — the cache key will be long but stable. ## See also - [CLI](../guide/cli) — built-in one-shot rendering. - [Configuration](./config) — composing project / deploy-wide defaults. - [Diagnostics](./diagnostics) — the feedback format every integration surfaces. --- # Parser `.gg` source → `DiagramDef` in three stages, each exposed as a separate function you can call directly: ``` .gg source │ ▼ parseGg(source) → { def, errors, icons } │ (includes integrity checks) ▼ buildIconContext({ … }) → IconContext (gridgram/node) │ (filesystem / network reads) ▼ resolveDiagramIcons(def, ctx) → { def, diagnostics } │ ▼ renderDiagram(def) → { svg, diagnostics } ``` **Browser vs Node**: `parseGg`, `resolveDiagramIcons`, `checkIntegrity`, `formatError`, and the icon classification helpers (`isPathRef`, `collectPathRefs`, `stripSvgWrapper`) are all pure and exported from `'gridgram'` — they work in every ESM host. The filesystem / HTTP icon loader (`buildIconContext` and friends) lives on the `'gridgram/node'` subpath and is Node-only. ## `parseGg(source)` ```ts import { parseGg } from 'gridgram' interface ParseResult { def: DiagramDef errors: GgError[] icons?: Record // from `doc { icons: … }` } function parseGg(source: string): ParseResult ``` Tokenizes the source, folds `doc`/`icon`/`region`/`note`/connector statements into a `DiagramDef`, and runs integrity checks (connector refs, note targets, region spans). Three categories of error end up in `errors`: | `source` | Meaning | |----------|---------| | `'dsl'` | Tokenizer / statement-parser issue — unknown statement, malformed attr. | | `'json'` | The JSON5 parser rejected a `doc { }` or `icons:` body. | | `'check'`| Integrity: unknown connector target, disjoint region, etc. | Parse errors are surfaced before the def is assembled; an errored def may still be partially usable for quick-look previews, but don't render one in production without handling `errors`. ### `GgError` shape ```ts interface GgError { message: string line: number // 1-based source: 'dsl' | 'json' | 'check' | 'icon' snippet?: string // the offending source line related?: { line: number; source: GgErrorSource; snippet?: string } } ``` Use `formatError` for a readable string: ```ts import { formatError } from 'gridgram' for (const e of errors) console.error(formatError(e, filename)) // Error: Unknown statement "icn" // at diagram.gg:3 (DSL: icn :a @A1 "hello") ``` ### The `icons` field A `.gg` file can register inline icon sources directly: ```gg doc { icons: { logo: 'https://example.com/logo.svg', brand: './assets/brand.svg', }, } icon @A1 logo "Us" icon @B1 brand "You" ``` `parseGg` returns those as `icons: Record` alongside the def. The resolver consumes it via `IconContext.inline` (for raw SVG strings) or via `buildIconContext`'s `jsonIconsMap` for URL / file / dataURL values that need loading. Keeps the source-time icon references off the `DiagramDef` type (which models the rendered diagram, not its provenance). ## `resolveDiagramIcons(def, ctx)` ```ts import { resolveDiagramIcons } from 'gridgram' function resolveDiagramIcons( def: DiagramDef, ctx: IconContext, ): { def: DiagramDef; diagnostics: PlacementDiagnostic[] } ``` Replaces every string-valued `node.src` with its resolved SVG fragment. Nodes whose src couldn't be resolved get `iconError: true` and their `src` deleted. Each of those failures emits a [PlacementDiagnostic](./diagnostics) with: - `kind: 'icon-unresolved'` - `iconSrc`: the original DSL identifier - `iconReason`: `'not-found'` (tabler miss / unregistered) or `'load-failed'` (the loader tried and errored) Callers usually concat these with the render-time diagnostics to hand an agent one stream: ```ts const iconResolve = resolveDiagramIcons(rawDef, ctx) const rendered = renderDiagram(iconResolve.def) const allDiagnostics = [...iconResolve.diagnostics, ...rendered.diagnostics] ``` Pure, synchronous, browser-safe. `ctx` is just a plain object — you can construct it by hand (typical for in-browser use where only Tabler built-ins and pre-loaded inline SVG are in play) or let `buildIconContext` populate it for you in Node. ## `IconContext` ```ts interface IconContext { inline?: Record // from doc.icons / --icons map dir?: Record // from --icons basename aliases?: Record // from --alias / project config paths?: Record // pre-loaded external refs errors?: GgError[] // non-fatal loader issues failedSources?: Map // identifier → reason } ``` Every field is optional. The simplest valid context — `{}` — resolves Tabler built-ins only and flags every other `src` as `iconError`. ## `buildIconContext(opts)` — Node only ```ts import { buildIconContext } from 'gridgram/node' function buildIconContext(opts: { iconsDir?: string jsonIconsMap?: Record aliases?: Record def?: DiagramDef docDir: string // for cwd-relative paths AND for the icons map aliasDir?: string // defaults to docDir }): Promise ``` Walks the def for every external-path icon reference and pre-reads it (filesystem or HTTP). Returns an `IconContext` the resolver consumes. Non-fatal per-icon failures end up on `ctx.errors` (as `GgError`) and `ctx.failedSources` (map keyed by the DSL identifier). ```ts import { buildIconContext } from 'gridgram/node' const ctx = await buildIconContext({ iconsDir: settings.iconsDir, // --icons jsonIconsMap: parseResult.icons, // from parseGg aliases: settings.assetAliases, // from project config def: rawDef, docDir: dirname(sourcePath), aliasDir: process.cwd(), }) // Surface per-icon loader errors early for (const err of ctx.errors ?? []) console.error(formatError(err, sourcePath)) ``` This import fails in browser bundles. In the browser, either skip external icon references entirely (Tabler only) or pre-load them in your host code and hand the result to `resolveDiagramIcons` via `ctx.inline` / `ctx.paths`. ### Path reference resolution | DSL form | Where it resolves | |-----------------------|-------------------| | `'@brand/aws.svg'` | `aliases.brand + '/aws.svg'` (absolute if `aliases.brand` is absolute; else joined with `aliasDir`) | | `'./foo.svg'` | `docDir + '/foo.svg'` | | `'/abs/path.svg'` | as-is | | `'foo.svg'` | `docDir + '/foo.svg'` | ## `checkIntegrity(def)` ```ts import { checkIntegrity } from 'gridgram' function checkIntegrity(def: DiagramDef): GgError[] ``` Post-parse checks that `parseGg` runs for you automatically: - Connector `from` / `to` reference known node ids - Note `targets` reference known node / connector ids - Region spans fit within `columns × rows` - Region spans form a single 4-connected shape - Coordinate malformations (bad A1, col < 1, …) Exposed separately so programmatic callers who build a `DiagramDef` from scratch (skipping `parseGg`) can still validate it. Error messages use 1-based A1 coordinates — "`A1-J10 exceeds A1-B2 grid`" — so agents don't need to decode the internal 0-based form. ## Icon classification helpers Also exported from `'gridgram'` and safe in the browser: ```ts import { isPathRef, collectPathRefs, stripSvgWrapper } from 'gridgram' isPathRef('@brand/aws.svg') // true — needs external loading isPathRef('tabler/user') // false — Tabler built-in isPathRef('logo') // false — bare name → icon map collectPathRefs(def) // string[] of every node.src / badge.icon // that looks like an external path stripSvgWrapper('') // '…' — keep the inner fragment ``` `collectPathRefs` is what `buildIconContext` uses to discover every external file it needs to pre-read. Reach for it if you're implementing your own async loader (e.g. resolving paths through a storage adapter instead of `fs`). ## See also - [Types](./types) — `DiagramDef` and its members. - [Diagnostics](./diagnostics) — consumer-side view of the icon-unresolved stream. - [User Guide: CLI](../guide/cli) — same parsing from the CLI side. --- # Quickstart (TS API) The TypeScript API is published to npm as ESM. Install it, import what you need, hand `renderDiagram` a `DiagramDef`, and get SVG back. ## Install ```sh npm install gridgram # or bun add gridgram pnpm add gridgram yarn add gridgram ``` Runtime requirements: - **ESM only.** `"type": "module"` is required in your `package.json` (or equivalent bundler setup). - **Node ≥ 22** for native ESM + JSON import attributes. Any version of Bun works. Modern browsers work through any ES-module-aware bundler (Vite, Rspack, esbuild, Rollup, webpack 5+). - **Preact** and **preact-render-to-string** are regular dependencies, resolved through your bundler. If your host already has a different Preact version, you'll get two copies — the output is identical either way (we pin nothing globally). - **No `sharp` in the TS API.** The library renders SVG only; PNG rasterization is done by the `gg` CLI at the edge, or by the host if it chooses to (see [Integrations](./integrations)). ## Your first render ```ts import { renderDiagram, tablerOutline } from 'gridgram' import type { DiagramDef } from 'gridgram' const def: DiagramDef = { nodes: [ { id: 'user', pos: 'A1', src: tablerOutline('user'), label: 'User' }, { id: 'api', pos: 'B1', src: tablerOutline('server'), label: 'API' }, { id: 'db', pos: 'C1', src: tablerOutline('database'),label: 'DB' }, ], connectors: [ { from: 'user', to: 'api', label: 'HTTPS' }, { from: 'api', to: 'db', label: 'SQL' }, ], } const { svg, diagnostics } = renderDiagram(def) console.log(svg) // console.log(diagnostics) // [] for a clean layout ``` No coordinates? No icon sources? All fine — `renderDiagram` fills in sensible defaults (auto-position across row 0, placeholder rings) and reports anything noteworthy via `diagnostics`. ## Using the Preact component If your host is a Preact app, skip the string round-trip and embed the VNode tree directly: ```tsx import { Diagram } from 'gridgram' import type { DiagramDef } from 'gridgram' export function Architecture({ def }: { def: DiagramDef }) { return } ``` `` accepts every `DiagramOptions` field as a prop (e.g. `renderWidth`, `suppressErrors`, `theme`, `cellSize`). Internally it's a thin wrapper around `buildDiagramTree`. ## Building a `DiagramDef` `DiagramDef` is a plain object. See [Types](./types) for the full field reference. The important parts: - `nodes: NodeDef[]` — required. Each has an `id`, optional `pos`, optional `label`, and an `src` that names its icon. - `connectors?: ConnectorDef[]` — optional. `from` / `to` reference node ids. - `regions?: RegionDef[]` — background cell spans with a fill. - `notes?: NoteDef[]` — callout boxes with optional leader lines. - `theme?`, `cellSize?`, `columns?`, `rows?`, `padding?` — document settings. Any of them can also come from a [project config file](./config). Coordinates accept three forms — A1 strings, 1-based tuples, 1-based objects: ```ts pos: 'A1' // column 1, row 1 (top-left) pos: [1, 1] // same thing pos: { col: 1, row: 1 } ``` The pipeline normalizes them all to the canonical 0-based `{ col, row }` object before rendering. See [Types](./types) for `GridPosInput` vs `GridPos`. ## Adding an icon The default node has no icon — it renders as a ring with a label. Usual forms for `src`: ```ts import { tablerOutline, tablerFilled } from 'gridgram' { id: 'user', pos: 'A1', src: tablerOutline('user'), label: 'User' } { id: 'star', pos: 'B1', src: tablerFilled('star'), label: 'Hot' } { id: 'raw', pos: 'C1', src: '', label: 'Raw' } ``` `tablerOutline(name)` / `tablerFilled(name)` return the inline SVG fragment for any of the 5,500+ Tabler icons. Safer than a string identifier — the TypeScript compiler won't catch a typo'd name, but a missing icon at runtime is obvious (the node gets a red ring). For URL / file-path icons, either pre-resolve them yourself (set `src` to the final SVG string) or run the `.gg` icon loader (see [Integrations](./integrations) and the `gridgram/node` subpath). ## Writing to a file ```ts import { renderDiagram } from 'gridgram' import { writeFileSync } from 'node:fs' const { svg } = renderDiagram(def) writeFileSync('out.svg', `\n${svg}`) ``` For PNG, see the [PNG rasterization](./integrations#png-rasterization) pattern — you bring `sharp`, we give you `computeRenderDimensions` for the canvas size. ## From a `.gg` source When the diagram is authored as text, parse it first. `parseGg` and `resolveDiagramIcons` are pure (no filesystem), so they work in any ESM host: ```ts import { parseGg, resolveDiagramIcons, renderDiagram, formatError, } from 'gridgram' const source = ` icon :user @A1 tabler/user "User" icon :api @B1 tabler/server "API" user --> api "HTTPS" ` const { def: rawDef, errors, icons } = parseGg(source) if (errors.length > 0) { throw new Error(errors.map((e) => formatError(e, 'inline.gg')).join('\n')) } // With `inline`, the resolver handles Tabler built-ins + any inline // SVG strings from `doc { icons: { … } }`. For URL / file-path refs, // pre-load them via `buildIconContext` (Node only) — see below. const { def, diagnostics: iconDiagnostics } = resolveDiagramIcons(rawDef, { inline: icons, }) const { svg, diagnostics: layoutDiagnostics } = renderDiagram(def) const allDiagnostics = [...iconDiagnostics, ...layoutDiagnostics] ``` For filesystem / HTTP icon references (`./foo.svg`, `@brand/aws.svg`, `https://…`), import the async loader from the Node subpath: ```ts import { buildIconContext } from 'gridgram/node' const ctx = await buildIconContext({ jsonIconsMap: icons, def: rawDef, docDir: '/path/to/project', }) const { def } = resolveDiagramIcons(rawDef, ctx) ``` See [Parser](./parser) for the full pipeline and [Diagnostics](./diagnostics) for what the `diagnostics` arrays carry. ## Next - [`renderDiagram` & friends](./render) — options, return shapes, `Diagram` and `buildDiagramTree` for Preact embedding. - [Types](./types) — every field on every diagram-def type. - [Parser](./parser) — `.gg` → `DiagramDef` in depth. - [Diagnostics](./diagnostics) — agent-facing feedback stream. --- # `renderDiagram` and friends The render module is the top of the public API — three functions and one Preact component, all taking the same `DiagramDef` shape and sharing one internal pipeline. The difference is only what you want back: an SVG string, a VNode tree, or an inlined JSX element. ## `renderDiagram(def, opts?)` ```ts function renderDiagram(def: DiagramDef, opts?: DiagramOptions): RenderResult interface RenderResult { svg: string diagnostics: PlacementDiagnostic[] } ``` The default choice. Returns the rendered SVG alongside any placement / route / icon diagnostics the pipeline produced. Diagnostics are empty on a clean layout. ```ts const { svg, diagnostics } = renderDiagram(def, { renderWidth: 1024 }) if (diagnostics.length > 0) { // One record per element that couldn't be placed or routed cleanly. // See the Diagnostics reference for the shape. console.warn(diagnostics.map((d) => d.message).join('\n')) } ``` ## `renderDiagramSvg(def, opts?)` ```ts function renderDiagramSvg(def: DiagramDef, opts?: DiagramOptions): string ``` Back-compat variant. Returns only the SVG string; discards any diagnostics. Use it when the caller doesn't need feedback — for instance inside a build step that already knows the input is clean. Internally it's just `renderDiagram(def, opts).svg`; there's no performance difference. ## `` — Preact component ```tsx import { Diagram } from 'gridgram' import type { DiagramProps } from 'gridgram' ``` A Preact functional component wrapping `buildDiagramTree`. Every `DiagramOptions` field is accepted as a prop (e.g. `renderWidth`, `theme`, `suppressErrors`, `cellSize`). Use this when your host already renders Preact — no intermediate string, no double parse. `DiagramProps` is `DiagramOptions & { def: DiagramDef }`. ## `buildDiagramTree(def, opts?)` ```ts function buildDiagramTree(def: DiagramDef, opts?: DiagramOptions): any ``` Returns the raw Preact VNode tree for the diagram's root ``. `` is a thin wrapper around this. Reach for it directly when: - You want to manipulate the tree before Preact mounts it. - You're streaming through a custom renderer (a non-SSR Preact renderer, a vdom-to-canvas adapter, etc.). The return type is `any` because Preact's `VNode` import isn't part of Gridgram's API surface; cast at the call site if you prefer. ## `computeRenderDimensions(def, opts?)` ```ts function computeRenderDimensions( def: DiagramDef, opts?: DiagramOptions, ): { width: number; height: number } ``` Returns the pixel size the SVG's `width` / `height` attributes will report — i.e. the final rasterized canvas size. Two distinct uses: - **PNG output via sharp**: sharp's `.resize()` needs integers, and the aspect ratio has to match the SVG's `viewBox`. `computeRenderDimensions` bakes in any `renderWidth` override. - **Responsive wrappers**: if you need to pre-allocate a container of the right aspect ratio in HTML, call this function at build time. ```ts const { width, height } = computeRenderDimensions(def, { renderWidth: 2048 }) ``` ## The `DiagramOptions` shape ```ts interface DiagramOptions { /** Override per-cell pixel size in the internal coordinate space. */ cellSize?: number /** Pad the canvas by this many internal-space pixels. */ padding?: number /** Force the grid dimensions (otherwise auto-inferred). */ columns?: number rows?: number /** Override the theme (any subset of DiagramTheme). */ theme?: Partial /** Hide the red iconError / labelError decorations. */ suppressErrors?: boolean /** Final rendered width in px (aspect preserved; viewBox * unchanged so geometry stays pixel-perfect). */ renderWidth?: number } ``` Every field is optional. `DiagramOptions` is an alias for `DiagramSettings` — the render call treats the options as the "render override" layer in the [layered settings system](./config). ## Determinism notes - The pipeline is synchronous and free of wall-clock / random inputs. Same `DiagramDef` ⇒ same bytes, every run, every platform. - Icon resolution (network fetches, filesystem reads) happens upstream in `buildIconContext` (`gridgram/node`) / `resolveDiagramIcons` (`gridgram`). After that step the def is self-contained — render is pure. - Preact SSR doesn't re-order attributes; label-placement candidate order is fixed per element kind. Any attribute-order drift between runs would be a renderer bug. ## What counts as "safe" concurrent use `renderDiagram` is a pure function of its inputs — no globals, no caches, no mutation of the passed `def`. Multiple calls can run in parallel across worker threads. The only shared state is the Tabler icon JSON loaded at module import time, which is read-only. ## See also - [Types](./types) — `DiagramDef`, `NodeDef`, et al. - [Configuration](./config) — how `DiagramOptions` composes with project / document layers. - [Diagnostics](./diagnostics) — what comes back in `RenderResult.diagnostics`. --- # Specification This page is the **reference** — the underlying rules that make Gridgram's DSL behave the way it does. It's for readers who hit a surprising edge case or want to generate `.gg` programmatically. The Guide pages cover the "how" with examples; this page covers the "why" with grammar and invariants. ## 1. `.gg` grammar Informal BNF. Upper-case names are terminals (tokens), lower-case are non-terminals. `|` is alternation, `*` is zero-or-more, `?` is optional, `+` is one-or-more. ``` file := statement* statement := doc-stmt | icon-stmt | region-stmt | note-stmt | connector-stmt doc-stmt := 'doc' body icon-stmt := 'icon' arg* body? region-stmt := 'region' arg* body? note-stmt := 'note' arg* body? connector-stmt := IDENT arrow IDENT arg* body? arg := id-sigil | pos | span | target-list | label | attr | word # icon src shorthand (icon-stmt only) id-sigil := ':' IDENT pos := '@' A1_ADDR | '@' INT ',' INT span := '@' A1_ADDR RANGE_SEP A1_ADDR | '@' INT ',' INT RANGE_SEP INT ',' INT RANGE_SEP := ':' | '-' # ':' (Excel) preferred label := DQ_STRING | SQ_STRING target-list := '(' IDENT (',' IDENT)* ')' attr := IDENT '=' (BARE_WORD | DQ_STRING) body := '{' '}' arrow := '-->' | '->' | '<--' | '<->' | '---' | '..>' | '<..' | '<..>' | '...' # Terminals A1_ADDR := [A-Za-z]+ [0-9]+ # "A1", "aa100", "AAA9999" INT := [0-9]+ IDENT := [A-Za-z_] [A-Za-z0-9_-]* BARE_WORD := any run of non-whitespace, non-';', '{', '(', '"', "'" DQ_STRING := '"' (. | '\\n' | '\\"' | '\\\\' | '\\t')* '"' SQ_STRING := "'" (. | '\\n' | '\\'' | '\\\\' | '\\t')* "'" STMT_END := '\n' (at depth 0) | ';' (at depth 0) ``` **Statement dispatch** is a short peek: 1. If `tokens[1]` is an `arrow`, the statement is a **connector**. 2. Else, `tokens[0]` must be one of `doc` / `icon` / `region` / `note`. That's why `icon` / `region` / `note` / `doc` are reserved as **line-leading identifiers**, but can appear freely in any other role (icon ref, node id in a connector, label text, attribute value, …). **Arguments after the command keyword are order-independent.** Each arg carries its own prefix (`:`, `@`, `"`, `(`, `=`), so the tokenizer can assign roles without relying on position. ## 2. Coordinate system Gridgram is **1-based** in every user-facing form. `A1` = column 1, row 1 = top-left. Internally the normalizer shifts to 0-based just before layout math. | Form | Meaning | |-------------------|--------------------------------------| | `@A1` | single cell (Excel-style; preferred) | | `@1,1` | single cell (numeric) | | `@A1:B2` | rectangular range (Excel-style; preferred) | | `@A1-B2` | rectangular range (legacy separator) | | `@1,1:2,2` | rectangular range (numeric) | | `@1,1-2,2` | rectangular range (numeric, legacy) | | `pos: [1, 1]` | TS tuple | | `pos: { col: 1, row: 1 }` | TS named object | | `pos: "A1"` | TS string | **A1 column math**: A=1 … Z=26, AA=27 … AZ=52, BA=53 … ZZ=702, AAA=703 etc. Lower-case is accepted (`aa100`). `col = Σ letter × 26^(n-1-i)` with A-Z → 1-26. **Validation errors (parse or normalize time)**: - `col < 1` or `row < 1` → `"Grid coordinate is 1-based"` - Malformed A1 (`@1A`, `@A0`, empty) → `"Invalid cell address"` - Out-of-bounds vs `cols` / `rows` → integrity error - Non-integer `col` / `row` on a node → error (waypoints may be fractional) Auto-layout: when `@pos` is omitted on `icon`, positions are assigned in declaration order. `col` increments along `row=1`, wrapping at `cols` if it's set. ## 3. Resolution pipeline ```gg-diagram doc { cols: 4, rows: 2, theme: { primary: '#1e3a5f', accent: '#e8792f' }, } icon :src @A1 tabler/file-text "Source" icon :tok @B1 tabler/code "Tokens" icon :stmt @C1 tabler/list "Statements" icon :def @D1 tabler/file-code "DiagramDef" icon :check @A2 tabler/check "Integrity" icon :fold @B2 tabler/stack "foldLayers" icon :layout @C2 tabler/ruler "resolveDiagram" color=accent icon :svg @D2 tabler/photo "SVG" color=accent src --> tok "tokenize" tok --> stmt "parse" stmt --> def "parseGg" def --> check "check" check --> fold "resolve" fold --> layout "layout" layout --> svg "render" ``` Textually, the same pipeline: ``` 1. scan + tokenize → Token[] 2. parseStatements → per-statement ParseLineResult[] 3. parseGg → merge doc blocks (deep), concat arrays, assign auto-ids for nameless icons 4. checkIntegrity → references, bounds, 4-connectivity 5. foldLayers (render-time) → resolve settings (4-layer merge), normalize coords (1-based → 0-based), expand badge presets, auto-position 6. resolveDiagram → layout, connector routing, label placement 7. render → Preact VNode tree → SVG ``` ### Settings merge (4 layers, later wins) ``` system defaults → project config (gridgram.config.{ts,js,json,json5} via walk-up) → document doc { … } blocks (deep-merged in source order) → CLI / programmatic override ``` ### Array merge (`doc { nodes/connectors/regions/notes: [...] }`) | Array | Dedup key | On collision | |---------------|-----------|--------------| | `nodes` | `id` | **error** | | `connectors` | none | concat | | `regions` | none | concat | | `notes` | none | concat | Nodes from `doc { nodes: [...] }` concatenate with DSL-declared nodes. Ids must be globally unique. ## 4. Icon (`src=`) resolution priority ``` 1. doc { icons: { … } } map (per-file bare-name aliases) 2. --icons CLI flag (per-invocation bare-name map) 3. Path refs (./x.svg, @alias/x.svg, /abs/x.svg, https://…) 4. Tabler built-ins (tabler/, tabler/filled/) 5. otherwise → iconError (red ring + `{ iconError: true }`) ``` The `doc` map wins so a single file can override a shared registration without touching anything else. ## 5. Label placement All labels — node labels, connector labels, region labels — go through the same placer. For each, a list of candidate `LabelRect`s is tried in priority order; the first non-colliding rect wins. If every candidate collides, the fallback (usually the first candidate) is used and the result is flagged `error: true` (surfaces as a red placement marker unless `--no-errors`). ### Placement order ``` notes (position fixed by @pos) → node labels (by descending connector-degree) → connector labels → region labels ``` Earlier placements occupy cells; later ones avoid them. ### Candidate order | Kind | Candidates (tried in order) | |------------|----------------------------------------------------------| | Node | top-right, bottom-right, bottom-left, top-left, top-center, bottom-center | | Connector | middle segment outward (hop order); within a segment: middle → inset positions | | Region | top-left, top-right, bottom-right, bottom-left, top-center, bottom-center (skipping corners that fall outside the union) | ### Collision rule A rect **collides** if any of the following is true: 1. It leaves the canvas bounds (only when `bounds` is supplied). 2. It overlaps another already-placed label rect (with ~4 px padding). 3. It is crossed by any connector line segment (with ~6 px padding). 4. **It overlaps any node's icon disc** (with ~4 px padding), excluding that node's own disc when placing its own label. Rule 4 is why a big icon at `@D1` pushes the label of a smaller icon at `@C1` to a different corner — labels don't get placed on top of other people's glyphs. ### Node-label callout geometry For a node at pixel center `(x, y)` with radius `r`, a label rect is constructed for each candidate corner (`top-right`, etc.). The leader line goes from: - **Edge point**: on the circle, on the ray from `(x, y)` toward the leader-target corner of the label rect. In other words, extending the leader through the edge hits the node's center — so the leader always reads as "pointing at the middle of the icon". - **Target point**: the corner of the label rect nearest the node. ## 6. Connector routing ``` 1. Compute straight polyline (source center → waypoints → target center) 2. Check for cross-through: does the line pass through any *other* node's icon disc? If so, step 3. 3. Auto-route: graph BFS on cell-corner intersection points, detouring around the offending node's corners. Picks the path with fewest used intersections (to reduce visual crossing with prior connectors). 4. Apply nodeMargin pullback: arrow tips retract `nodeMargin * radius` from each endpoint so they don't overlap the ring. ``` Dashed lines (`..>`, `<..`, …) set `dash: '6 3'` automatically; an explicit `dash="…"` overrides. ## 7. Region blob A region is a union of `@pos` / `@span` entries. ``` 1. Rasterize: build a cols × rows occupancy matrix from the spans. 2. 4-connectivity check: BFS from one filled cell. If any filled cell is unreachable, the region is disjoint → integrity error. 3. Trace the boundary: walk the union's edges (clockwise), producing a single SVG path. 4. Corner rounding: each convex / concave vertex is rounded with the region's radius (clamped to half the neighboring edge length so arcs don't intersect). ``` Label placement for regions checks candidate corners for "representative cell is inside the union", then runs the collision loop against everything else (labels, lines, icon discs). ## 8. Color Every element's `color` field resolves through the same grammar: ``` primary # theme keyword (unmodified) accent/60 # theme keyword + 2-digit hex alpha accent/8 # single-digit alpha → expanded to 88 #e8792f # hex literal #e8792f40 # hex literal + alpha red # CSS named color rgb(200, 0, 0) # any CSS function ``` ### Auto-tint - **Node interior**: when `color` is set (any form), the node's fill is the color at ~8% opacity. Gives "outline + pale fill" from one attribute. - **Region fill for bare theme keywords**: `region … color=accent` renders at ~7% opacity automatically. `color=accent/30` or `color=#aabbcc` bypasses auto-tint — the literal value is used. ### Transparent canvas `theme.bg` = `'transparent'` / `'none'` / `''` → no background rect is drawn. Connector-label pills fall back to solid white on transparent canvases so labels stay readable against arbitrary backdrops. ## 9. Z-order (back to front) ``` 1. Canvas background rect (unless bg is transparent) 2. Regions (blob fills) 3. Connectors (line + arrowheads + label pills) 4. Nodes (ring + icon + badges) 5. Notes (leader lines + bubble + text) 6. Labels (node, region) painted after their owner ``` Notes sit *above* nodes so their leader lines visually overlay the grid; regions sit *below* so they function as backgrounds. ## 10. Reserved words and auto-ids - **Statement-leading keywords**: `doc`, `icon`, `region`, `note`. Anywhere **after** the keyword they're just normal words. - **Auto-ids**: nameless `icon` statements receive `__n1`, `__n2`, … assigned at parseGg time so every node has a stable key. Identifiers starting with `__` are **reserved** for this scheme; user code should avoid them. - **Identifier rule**: `[A-Za-z_][\w-]*`. No leading digit. ## 11. Error model Errors are collected (not thrown) and returned alongside the (partial) DiagramDef. Three severity classes: | `source` | Raised by | |------------|---------------------------------------------| | `dsl` | tokenizer / per-statement parser (line N) | | `json` | `doc { … }` JSON5 parse | | `check` | integrity (duplicate ids, unknown refs, …) | The CLI exit codes map 1:1: - `1` = at least one `dsl` or `json` error - `2` = at least one `check` error (parse succeeded) - `3` = I/O / render failure (file read, config load, PNG rasterize) - `0` = success ## 12. Defaults (quick table) | Setting | Default | |------------------------|--------------------| | `cellSize` | 256 px | | `padding` | `2 × max(cellSize × 0.025, 4)` | | Node diameter fraction | 0.45 of cell | | Default arrow | `end` (`-->`) | | Default connector width| 2 px (`strokeWidth`) | | Default connector dash | none (solid) | | Default node-margin | 0.6 | | Region auto-tint alpha | ~0x12 (≈ 7%) | | Node auto-tint alpha | ~0x15 (≈ 8%) | | Label padding | ~4 px (rect-rect), ~6 px (rect-line), ~4 px (rect-circle) | These aren't tunable per-element by any attribute except where noted (see Icon › Size, Connector › Styles, Region › Styling, Color › Theme in the Guide). --- # Types Every shape the library accepts or produces. All types are exported from the package root: ```ts import type { DiagramDef, NodeDef, ConnectorDef, RegionDef, NoteDef, GridPos, GridPosInput, GridSpan, WayPoint, WayPointInput, DiagramTheme, NodeBadge, BadgePosition, BadgeSpec, SvgFragment, DiagramOptions, DiagramProps, RenderResult, DiagramSettings, ResolvedSettings, PlacementDiagnostic, PlacementAttempt, Obstacle, ElementRef, ParseResult, GgError, GgErrorSource, IconContext, } from 'gridgram' ``` ## Coordinate system All user-facing coordinates are **1-based**: `A1` / `{col: 1, row: 1}` / `[1, 1]` all refer to the top-left cell, matching spreadsheet convention. The pipeline normalizes everything to a canonical internal 0-based form before layout runs — `GridPos`. ### `GridPos` — canonical, 0-based ```ts interface GridPos { col: number // 0-based column index row: number // 0-based row index } ``` Produced only by the normalization pass; never write one in user code. ### `GridPosInput` — user-facing input union ```ts type GridPosInput = | { col: number; row: number } // 1-based object | readonly [col: number, row: number] // 1-based tuple | string // A1 string ('A1', 'aa100') ``` Every public field that accepts a position uses `GridPosInput`. ### `WayPoint` / `WayPointInput` Same shape as grid pos, but fractional. `{col: 1.5, row: 2}` threads a connector between cell centres. A1 strings are **not** accepted for waypoints — use tuple or object form. ## `DiagramDef` — the top-level shape ```ts interface DiagramDef { cellSize?: number padding?: number columns?: number // explicit grid width; inferred otherwise rows?: number theme?: DiagramTheme regions?: RegionDef[] // rendered first (background) nodes: NodeDef[] // required, can be empty array connectors?: ConnectorDef[] notes?: NoteDef[] } ``` Required: `nodes`. Everything else has a sensible default. ## `NodeDef` — icons on the grid ```ts interface NodeDef { id: string // unique; referenced by connectors / notes pos?: GridPosInput // auto-positions across row 0 if omitted src?: SvgFragment // icon asset (see below) label?: SvgFragment // visible label (string or VNode) size?: number // absolute diameter as fraction of cell (0–1) sizeScale?: number // multiplier on the default (0.45 × this) color?: string // ring / icon color (CSS literal or theme keyword) labelScale?: number // font-size multiplier (default 1) iconTheme?: 'theme' | 'native' clip?: 'square' | 'circle' | 'none' badges?: BadgeSpec[] iconError?: boolean // set by resolveDiagramIcons on a lookup miss } ``` ### About `src` Accepts several forms — in order of how they're resolved: | Form | Example | Notes | |-------------------------|----------------------------------|-------| | Tabler namespace | `'tabler/user'`, `'tabler/filled/star'` | 5,500+ outline + filled subset | | Raw SVG | `''` or `''` | Passed through after wrapper stripping | | Path reference | `'./foo.svg'`, `'@brand/aws.svg'` | Loaded via the icon loader | | Bare name | `'logo'`, `'widget'` | Resolved against `doc.icons` / `--icons` | See [Parser](./parser) for the full resolution order. ### About `color` Accepts any CSS color literal (`'#e8792f'`, `'red'`, `'rgb(…)'`) and five theme keywords: `'primary'`, `'secondary'`, `'accent'`, `'text'`, `'muted'`. Keyword / alpha pairs also work: `'primary/40'` mixes the theme's primary color with 25% alpha. ## `ConnectorDef` — edges between nodes ```ts interface ConnectorDef { id?: string from: string // node id to: string // node id arrow?: 'none' | 'start' | 'end' | 'both' // default 'end' strokeWidth?: number // default 1.5 color?: string dash?: string // e.g. '6 3' for dashed label?: SvgFragment waypoints?: WayPointInput[] nodeMargin?: number // default 0.6 — pull-back distance as radius fraction labelScale?: number } ``` If `waypoints` is empty and the straight line passes through another node's disc, the router automatically routes around. If no clean route exists, `lineError` surfaces via a `route-failed` diagnostic and the line falls back to drawing straight through. ## `RegionDef` — background zones ```ts interface RegionDef { spans: GridSpan[] // one or more contiguous cell ranges color: string // fill (theme keywords supported — auto-tinted) label?: SvgFragment borderRadius?: number labelScale?: number } interface GridSpan { from: GridPosInput to: GridPosInput } ``` All spans of one region must form a **single 4-connected shape** (L-shapes are fine; diagonals-only are not). A disjoint set of spans is reported as an integrity error at parse time. ## `NoteDef` — callout boxes with optional leaders ```ts interface NoteDef { pos: GridPosInput text: string targets?: string[] // ids of nodes or connectors to draw leaders to bg?: string color?: string labelScale?: number } ``` `text` supports `\n` for line breaks and `**bold**` for inline emphasis. See [the User Guide note page](../guide/note/) for the authoring perspective. ## `DiagramTheme` ```ts interface DiagramTheme { primary: string secondary: string accent: string text: string bg?: string muted?: string } ``` `SYSTEM_DEFAULTS.theme` from `'gridgram'` has sane defaults. `DiagramOptions.theme` accepts a partial — omitted keys inherit from the layer below. ## `SvgFragment` ```ts type SvgFragment = | string // raw markup or a text label | number // coerced to string | VNode // a Preact virtual DOM node | null | undefined | false | SvgFragment[] ``` Every field that accepts SVG content — `label`, `src`, badge icons — takes an `SvgFragment`. Strings are the common case; `VNode` lets a programmatic caller drop a pre-built Preact tree in. ## Normalized variants (internal-but-exported) After `normalizeDiagramDef` runs, every coordinate is a canonical 0-based object and every node has a resolved `pos`. The pipeline internals operate on **branded** variants that encode that guarantee: ```ts interface NormalizedNodeDef extends Omit { pos: GridPos } interface NormalizedConnectorDef extends Omit { waypoints?: WayPoint[] } interface NormalizedNoteDef extends Omit { pos: GridPos } interface NormalizedRegionDef extends Omit { spans: NormalizedGridSpan[] } interface NormalizedDiagramDef extends Omit { nodes: NormalizedNodeDef[]; /* … */ } ``` Most callers never need to touch these — `renderDiagram` normalizes automatically. They matter if you're writing a custom layout extension that expects already-normalized inputs (e.g. a plugin that consumes the output of `resolveDiagram`). ## See also - [`renderDiagram` & friends](./render) — functions that consume these types. - [Parser](./parser) — `parseGg` produces a `DiagramDef` from `.gg` text. - [Configuration](./config) — `DiagramSettings` merge rules. --- # Editor A live `.gg` playground. Type on the left, the SVG on the right updates as you go. Parse errors and icon-resolution warnings land under the panes. The full `.gg` syntax is supported — `doc { }` blocks, icons, regions, connectors, notes. Only **Tabler built-ins** (`tabler/name`, `tabler/filled/name`) resolve in the browser; external icon files (`./x.svg`, `@alias/x.svg`) aren't available here. Use the `gg` CLI for those. ## Tips - `icon :id @A1 tabler/user "Label"` — a node with an id, position, icon, and label. - `a --> b "label"` — a connector from node `a` to node `b` with an edge label. - `doc { cols: 4, rows: 3 }` — pin the grid; omit to auto-infer from `@pos`. - `doc { theme: { primary: "#e8792f", accent: "#1e3a5f" } }` — retheme. - See the [User Guide](./guide/) for the full reference. --- # Gallery Collected `.gg` diagrams organised by application domain. Every example is a live render — each fenced `gg-diagram` block below is parsed, resolved, and drawn at build time. The goal is to show the range: Gridgram's DSL works well beyond the "boxes and arrows in a cloud architecture" case it was originally sketched for. Examples collectively exercise every feature — regions, notes, badges, dashed lines, theme overrides, waypoints, `sizeScale`, `labelScale` — so the category read-through doubles as a feature tour. ## Domains ### Technology & engineering - **[Infrastructure](./infrastructure)** — cloud topology, zones, failover - **[Microservices](./microservices)** — service mesh, API gateway, saga - **[Software architecture](./software-architecture)** — layered, hexagonal, plugin - **[Database](./database)** — replicas, shards, CDC, caching - **[API design](./api-design)** — request lifecycle, versioning, middleware - **[Event-driven](./event-driven)** — pub/sub, event sourcing, CQRS - **[CI / CD](./ci-cd)** — build pipelines, multi-env, feature previews - **[Testing](./testing)** — test pyramid, matrix, environments - **[Release & roadmap](./release-roadmap)** — rollout, deprecation, train - **[Security & auth](./security-auth)** — OAuth2, mTLS, zero trust - **[AI / ML](./ai-ml)** — training, RAG, agent topology, serving - **[Data pipelines](./data-pipelines)** — ETL, streaming, lambda, warehouse - **[IoT / hardware](./iot-hardware)** — sensors, gateways, edge inference ### Business & people - **[Business workflow](./business-workflow)** — approvals, invoices, tickets - **[Customer journey](./customer-journey)** — AARRR, onboarding, support - **[Org chart](./org-chart)** — reporting, matrix, squads - **[Project planning](./project-planning)** — dependencies, sprint, milestones - **[Sales funnel](./sales-funnel)** — pirate metrics, B2B stages, segmentation ### Operations & logistics - **[Supply chain](./supply-chain)** — upstream / production / downstream, returns - **[Warehouse](./warehouse)** — zone layout, pick-and-pack route - **[Manufacturing](./manufacturing)** — line stages, quality gates - **[Delivery](./delivery)** — hub-and-spoke, last mile, returns ### Knowledge & communication - **[Education](./education)** — concept maps, biology, history - **[Knowledge graph](./knowledge-graph)** — taxonomy, ontology, relations ## How the gallery is rendered Each page uses the **`gg-diagram`** fenced code block. The `docs/.vitepress/plugins/gg-diagram.ts` plugin parses every fence at build time and inlines the resulting SVG, so the gallery never ships raw `.gg` text to the browser — you see the rendered diagram directly. Only Tabler built-ins and inline raw SVG are available in this mode; `@alias/` paths, URLs, and file references are skipped (they'd need the async loader that markdown-it can't await). If you want those in a custom page, add an `examples//` entry and use the `` component instead. --- # AI / ML Model training, retrieval-augmented generation, agent loops, and serving topologies. A purple theme marks the ML domain; the central agent / model gets a dramatic sizeScale. ## Training pipeline with a gate Data → features → train → eval → registry, told as a three-frame story. Hover and step through: frame 1 is the pipeline at rest, frame 2 clears the eval gate, frame 3 lands the model in the registry. ## RAG: retrieval-augmented generation The model's answer is grounded in documents retrieved from a vector store. The LLM sits at the centre with the weight; the store / docs feed it as a region. ```gg-diagram gallery doc { cols: 4, rows: 2, theme: { primary: '#6d28d9', accent: '#db2777' }, } region @B1:C2 "Retrieve" color=primary/24 icon :q @A1 tabler/search "Query" icon :embed @B1 tabler/brain "Embed" icon :vec @C1 tabler/database "Vector DB" sizeScale=1.2 icon :docs @C2 tabler/files "Docs" icon :llm @D1 tabler/sparkles "LLM" sizeScale=1.6 icon :ans @D2 tabler/message "Answer" q --> embed embed --> vec vec --> docs "top-k" docs --> llm "context" width=2 llm --> ans ``` ## Agent + tools (MCP-style) A giant agent in the middle, compact tool satellites around it. Size variation makes the "orchestrator vs capability" relationship obvious. ```gg-diagram gallery doc { cols: 3, rows: 3, theme: { primary: '#6d28d9', accent: '#db2777' }, } icon :user @A2 tabler/user "User" icon :agent @B2 tabler/robot "Agent" sizeScale=2.0 icon :t1 @C1 tabler/search "Search" icon :t2 @C2 tabler/code "Code" icon :t3 @C3 tabler/database "DB" user --> agent agent <-> t1 "call" agent <-> t2 "call" agent <-> t3 "call" ``` ## Online serving with cache A prediction service fronted by a cache. Cold queries fall through to the model; results are written back. ```gg-diagram gallery doc { cols: 4, theme: { primary: '#6d28d9', accent: '#db2777' }, } region @A1:A1 "Client" color=accent/24 region @B1:C1 "Serve" color=primary/24 region @D1:D1 "Response" color=accent/32 icon :req @A1 tabler/device-mobile "Request" icon :cache @B1 tabler/bolt "Cache" icon :model @C1 tabler/brain "Model" sizeScale=1.4 icon :resp @D1 tabler/check "Response" req --> cache cache --> resp "hit" width=2 cache --> model "miss" dash="2 4" model --> cache "store" dash="2 4" model --> resp ``` --- # API design Request lifecycles, middleware, versioning strategies. ## Request lifecycle From the browser to the handler and back. Each hop adds something (auth, rate limit, logging) that a handler shouldn't re-implement per endpoint. ```gg-diagram gallery doc { cols: 5 } icon :client tabler/user "Client" icon :lb tabler/equal "LB" icon :auth tabler/lock "Auth" icon :rl tabler/clock "Rate limit" icon :h tabler/server "Handler" client --> lb lb --> auth auth --> rl rl --> h ``` ## Middleware chain Cross-cutting concerns compose as a chain. Order matters — e.g. rate-limiting must come after auth when the limit is per-user. ```gg-diagram gallery doc { cols: 5 } icon :req tabler/message-circle "Request" icon :log tabler/file-text "Log" icon :authn tabler/lock "Authn" icon :authz tabler/shield-check "Authz" icon :h tabler/server "Handler" req --> log log --> authn authn --> authz authz --> h ``` ## API versioning (path-based) Multiple versions coexist; clients pin to one. Old versions fade out after a deprecation window. ```gg-diagram gallery doc { cols: 4 } icon :client tabler/device-laptop "Client" icon :v1 tabler/package "/v1" icon :v2 tabler/package "/v2" icon :core tabler/server "Core" client --> v1 "legacy" client --> v2 "current" v1 --> core v2 --> core ``` ## GraphQL + resolvers One endpoint, many data sources. Each resolver pulls from the store most natural for its field. ```gg-diagram gallery doc { cols: 4, rows: 3 } icon :client @A2 tabler/user "Client" icon :gql @B2 tabler/api "GraphQL" icon :users @D1 tabler/database "Users DB" icon :catalog @D2 tabler/database "Catalog" icon :search @D3 tabler/search "Search" client --> gql "query" gql --> users "user(id)" gql --> catalog "product(id)" gql --> search "search(q)" ``` --- # Business workflow Approvals, document flows, tickets — processes that span people and systems. ## Purchase approval chain Three frames walk through each approval tier — hover and use ◀ / ▶ to compare. Frame 1 is a small request the manager auto-approves; frame 2 escalates to finance; frame 3 pulls in exec sign-off for a six-figure line item. ## Invoice processing From receipt to payment. The OCR step extracts line items; a human reviews exceptions before the ERP posts the transaction. ```gg-diagram gallery doc { cols: 5 } icon :inv tabler/file-invoice "Invoice" icon :ocr tabler/scan "OCR" icon :review tabler/eye "Review" icon :erp tabler/database "ERP" icon :pay tabler/credit-card "Pay" inv --> ocr ocr --> review review --> erp erp --> pay ``` ## Support ticket lifecycle Triage → assignment → resolution. The "escalated" branch routes to a specialist queue when the first-tier agent can't resolve. ```gg-diagram gallery doc { cols: 4, rows: 2 } icon :cust @A1 tabler/user "Customer" icon :ti @B1 tabler/ticket "Ticket" icon :tri @C1 tabler/filter "Triage" icon :t1 @D1 tabler/headset "Tier 1" icon :t2 @D2 tabler/headset "Tier 2" cust --> ti ti --> tri tri --> t1 tri --> t2 "escalate" ``` ## Document approval Draft → review → sign. Versioned at each transition; the "changes requested" loop sends it back for revision. ```gg-diagram gallery doc { cols: 4 } icon :draft tabler/pencil "Draft" icon :review tabler/eye "Review" icon :revise tabler/arrow-back "Revise" icon :sign tabler/signature "Sign" draft --> review review --> sign "approve" review --> revise "changes" revise --> draft dash="4 4" ``` --- # CI / CD Continuous integration and delivery flows, from a developer's commit to a running production. ## Pipeline with gated promotions Region bands (three tinted tiers) + success badges + a manual gate marked with extra stroke width. Gives the feel of a control console. Hover the diagram and use ◀ / ▶ to scrub through the three frames — frame 1 is a cold pipeline, frame 2 is CI / staging green, frame 3 is the gate released and prod lit. ## Matrix build A single commit fans out across five workers in parallel. The diagram is deliberately plain — one theme colour, no regions — so it contrasts with the richer examples above and below. ```gg-diagram gallery doc { cols: 3, rows: 3 } icon :src @A2 tabler/git-commit "Commit" icon :n18 @B1 tabler/server "18/linux" icon :n20 @B2 tabler/server "20/linux" icon :n22 @B3 tabler/server "22/linux" icon :merge @C2 tabler/check "Merge" sizeScale=1.3 src --> n18 src --> n20 src --> n22 n18 --> merge n20 --> merge n22 --> merge ``` ## Feature branch preview Dashed "preview" deploy for peer review; solid path for merge-to- main. A note anchors the decision point so reviewers know what triggers the automation. ```gg-diagram gallery doc { cols: 4, rows: 2 } icon :pr @A1 tabler/git-pull-request "PR" icon :preview @B1 tabler/world "Preview" icon :merge @C1 tabler/git-merge "merge" icon :main @D1 tabler/server "staging" pr --> preview "deploy" dash="2 4" pr --> merge merge --> main "promote" width=2 note @B2 (preview) "URL posted\nback to the PR" ``` ## Deployment topology Final CI step: one artefact, one binary, many runtimes. The hub carries the visual weight via `sizeScale`. ```gg-diagram gallery doc { cols: 3, rows: 2 } region @A1:C1 "Artefact" color=primary/28 region @A2:C2 "Runtimes" color=secondary/24 icon :art @B1 tabler/package "Artefact" sizeScale=1.5 icon :r1 @A2 tabler/brand-ubuntu "Linux" icon :r2 @B2 tabler/brand-apple "macOS" icon :r3 @C2 tabler/brand-windows "Windows" art --> r1 art --> r2 art --> r3 ``` --- # Customer journey The path a user takes through a product or brand. Coloured bands make the stages read like a funnel at a glance. ## AARRR (pirate metrics) Acquire → activate → retain → refer → revenue. One theme override tints everything to brand orange; each stage sits in its own accent band. ```gg-diagram gallery doc { cols: 5, theme: { primary: '#b45309', secondary: '#d97706', accent: '#f59e0b' }, } region @A1:A1 "Acquire" color=primary/28 region @B1:B1 "Activate" color=primary/28 region @C1:C1 "Retain" color=primary/28 region @D1:D1 "Refer" color=primary/28 region @E1:E1 "Revenue" color=accent/32 icon :acq @A1 tabler/target "Top-of-funnel" icon :act @B1 tabler/bolt "First value" icon :ret @C1 tabler/refresh "Habit" icon :ref @D1 tabler/share "Refer" icon :rev @E1 tabler/coin "Paid" sizeScale=1.3 acq --> act "signup" act --> ret "day-7" ret --> ref "NPS ≥ 9" ref --> rev "conv" note @C2 (ret) "Cohort retention\nweek 4: 30%" ``` ## E-commerce purchase flow Conversion funnel scrubbed step-by-step across four frames — hover and use ◀ / ▶ to walk the shopper from landing to shipped. The cart frame surfaces the notorious abandon-rate note. ## Onboarding flow First-run setup. Five compact steps in one row; the dashboard is the emphasized end state. ```gg-diagram gallery doc { cols: 5 } region @A1:B1 "Account" color=primary/24 region @C1:D1 "Setup" color=secondary/24 region @E1:E1 "Ready" color=accent/32 icon :signup @A1 tabler/user-plus "Sign up" icon :verify @B1 tabler/mail-check "Verify" icon :profile @C1 tabler/user-circle "Profile" icon :connect @D1 tabler/plug "Connect" icon :dash @E1 tabler/layout-dashboard "Dashboard" sizeScale=1.4 signup --> verify verify --> profile profile --> connect connect --> dash ``` ## Support journey Users self-serve where possible, escalate when they can't. Docs region is colored — reading docs deflects tickets. ```gg-diagram gallery doc { cols: 4 } region @A1:A1 "User" color=accent/24 region @B1:B1 "FAQ" color=primary/24 region @C1:C1 "Ticket" color=secondary/24 region @D1:D1 "Agent" color=primary/28 region @B2:B2 "Docs" color=accent/28 icon :user @A1 tabler/user "User" icon :faq @B1 tabler/book "FAQ" icon :ticket @C1 tabler/ticket "Ticket" icon :agent @D1 tabler/headset "Agent" icon :docs @B2 tabler/file-text "Docs" sizeScale=1.2 user --> faq faq --> docs "read" dash="2 4" faq --> ticket "file" ticket --> agent ``` --- # Data pipelines Moving data from where it lands to where analysts and models can reach it. ## Classic ETL Simplest possible pipeline, shown in its simplest form. Sequential, uniform node size, one theme colour — intentionally flat so the following examples read as elaborations. ```gg-diagram gallery doc { cols: 5 } icon :src @A1 tabler/database "Source" icon :xtrct @B1 tabler/download "Extract" icon :xform @C1 tabler/refresh "Transform" icon :load @D1 tabler/upload "Load" icon :wh @E1 tabler/database "Warehouse" src --> xtrct xtrct --> xform xform --> load load --> wh ``` ## Streaming with consumers Kafka-style event log fanning out to three consumers. The log node takes visual weight via `sizeScale`; a purple theme signals "stream domain." ```gg-diagram gallery doc { cols: 3, rows: 3, theme: { primary: '#6d28d9', secondary: '#7c3aed', accent: '#db2777' }, } icon :app @A2 tabler/server "App" icon :kafka @B2 tabler/arrows-shuffle "Kafka" sizeScale=1.8 icon :sink @C1 tabler/database "DW" icon :ml @C2 tabler/brain "ML" icon :audit @C3 tabler/file-text "Audit" app --> kafka width=2 kafka --> sink dash="2 4" kafka --> ml dash="2 4" kafka --> audit dash="2 4" ``` ## Lambda architecture (speed + batch) Two lanes over the same events. Regions separate the real-time and scheduled bands visually; annotations name the latencies. ```gg-diagram gallery doc { cols: 4, rows: 2 } region @B1:B1 "Speed" color=accent/30 region @B2:B2 "Batch" color=secondary/28 icon :src @A1 tabler/database "Events" sizeScale=1.3 icon :stream @B1 tabler/bolt "Speed" icon :batch @B2 tabler/clock "Batch" icon :serve @D1 tabler/server "Serve" icon :hist @D2 tabler/files "History" src --> stream src --> batch stream --> serve batch --> hist note @C1 (stream) "Approximate,\nlow-latency" note @C2 (batch) "Exact,\nreplayable" ``` ## CDC → lake → catalog Change data capture into a lake, discoverable via a catalog. Badges mark the "health" of each CDC feed. ```gg-diagram gallery doc { cols: 4 } icon :db @A1 tabler/database "OLTP" sizeScale=1.2 icon :cdc @B1 tabler/arrows-shuffle "CDC" { badges: ['check'] } icon :lake @C1 tabler/files "Lake" icon :catalog @D1 tabler/book "Catalog" db --> cdc cdc --> lake lake <-> catalog "register" ``` --- # Database Replication, sharding, caching, and change-data-capture topologies. ## Primary with read replicas Writes to one, reads from many. Two regions carve out the "write side" and "read side" of the data tier; the primary is `sizeScale`-emphasised. ```gg-diagram gallery doc { cols: 4, rows: 2 } region @B1:B2 "Write path" color=accent/30 region @C1:D2 "Read-only" color=primary/28 icon :app @A1 tabler/server "App" icon :pri @B1 tabler/database "Primary" sizeScale=1.4 icon :r1 @C1 tabler/database-export "r-1" icon :r2 @D1 tabler/database-export "r-2" app --> pri "writes" width=2 app --> r1 "reads" app --> r2 "reads" pri --> r1 dash="2 4" pri --> r2 dash="2 4" note @C2 (r1, r2) "Async\nlag ≤ 5s" ``` ## Sharding by key Plain layout — three shards, one router. A deliberate contrast with the richer example above. ```gg-diagram gallery doc { cols: 3, rows: 3 } icon :app @A2 tabler/server "App" icon :router @B2 tabler/arrows-shuffle "Router" sizeScale=1.3 icon :s1 @C1 tabler/database "Shard A" icon :s2 @C2 tabler/database "Shard B" icon :s3 @C3 tabler/database "Shard C" app --> router router --> s1 router --> s2 router --> s3 ``` ## Read-through cache Two frames contrast the hot and cold paths. Hover and flip between them — frame 1 is the fast round-trip served from cache; frame 2 is the miss falling back to the DB with a "populate on read" note. ## CDC → downstream Four downstream consumers fed off the primary. Badges mark which feeds are healthy vs catching up. ```gg-diagram gallery doc { cols: 4, rows: 2 } icon :db @A1 tabler/database "Primary" sizeScale=1.3 icon :cdc @B1 tabler/arrows-shuffle "CDC" icon :search @C1 tabler/search "Search" { badges: ['check'] } icon :bi @D1 tabler/chart-dots "BI" { badges: ['check'] } icon :cache @C2 tabler/bolt "Cache" { badges: ['alert'] } icon :ml @D2 tabler/brain "ML" { badges: ['check'] } db --> cdc cdc --> search cdc --> bi cdc --> cache dash="2 4" cdc --> ml ``` --- # Delivery Parcel and freight flows between hubs and final destinations. ## Hub-and-spoke Parcels consolidate at regional hubs, move between hubs by trunk, then fan out to local delivery. Common for national postal / logistics networks. ```gg-diagram gallery doc { cols: 5, rows: 3 } region @A1:A3 "Regional" color=primary/14 region @E1:E3 "Regional" color=primary/14 icon :sw @A1 tabler/building "SW depot" icon :sc @A2 tabler/building "SC depot" icon :se @A3 tabler/building "SE depot" icon :hub1 @B2 tabler/building-warehouse "Hub W" icon :hub2 @D2 tabler/building-warehouse "Hub E" icon :ne @E1 tabler/building "NE depot" icon :nc @E2 tabler/building "NC depot" icon :nw @E3 tabler/building "NW depot" sw --> hub1 sc --> hub1 se --> hub1 hub1 --> hub2 "trunk" width=3 hub2 --> ne hub2 --> nc hub2 --> nw ``` ## Last-mile with returns A local depot dispatches drivers to customers. Failed deliveries and returns loop back via a dashed path. ```gg-diagram gallery doc { cols: 4, rows: 3 } icon :depot @A2 tabler/building-warehouse "Depot" icon :drv @B2 tabler/truck-delivery "Driver" icon :cust1 @D1 tabler/user "Customer 1" icon :cust2 @D2 tabler/user "Customer 2" icon :cust3 @D3 tabler/user "Customer 3" depot --> drv "load" drv --> cust1 drv --> cust2 drv --> cust3 cust2 --> depot "returned" dash="4 4" color=accent ``` ## Route optimisation (before → after) Naive sequential route vs optimised. Note calls out the savings. ```gg-diagram gallery doc { cols: 5, rows: 3 } icon :a1 @A1 tabler/map-pin "A" icon :b1 @B1 tabler/map-pin "B" icon :c1 @C1 tabler/map-pin "C" icon :d1 @D1 tabler/map-pin "D" icon :e1 @E1 tabler/map-pin "E" a1 --> b1 b1 --> c1 c1 --> d1 d1 --> e1 icon :a2 @A3 tabler/map-pin "A" icon :c2 @B3 tabler/map-pin "C" icon :e2 @C3 tabler/map-pin "E" icon :d2 @D3 tabler/map-pin "D" icon :b2 @E3 tabler/map-pin "B" a2 --> c2 c2 --> e2 e2 --> d2 d2 --> b2 note @C2 "Optimised saves\n18% distance" ``` ## Package tracking states A parcel's lifecycle as a state machine. Each transition is an event captured by the tracking API. ```gg-diagram gallery doc { cols: 5 } icon :created tabler/package "Created" icon :transit tabler/truck "Transit" icon :out tabler/truck-delivery "Out" icon :deliv tabler/home "Delivered" icon :failed tabler/alert-triangle "Failed" created --> transit transit --> out out --> deliv out --> failed "no recipient" dash="4 4" color=accent failed --> out "retry" ``` --- # Education Concept maps, processes, timelines — the slide-deck diagrams that show up in teaching material. ## Water cycle The classic grade-school diagram. Evaporation → condensation → precipitation → collection, back to start. ```gg-diagram gallery doc { cols: 4 } icon :sea tabler/droplet "Ocean" icon :vapor tabler/cloud "Evaporate" icon :cloud tabler/cloud "Cloud" icon :rain tabler/cloud-rain "Rain" sea --> vapor "evap" vapor --> cloud "cond" cloud --> rain "precip" rain --> sea "collect" ``` ## Cell biology: photosynthesis Chloroplasts absorb light; water and CO₂ become glucose and O₂. Regions group inputs vs outputs; a theme override matches the standard green/blue/orange chemistry conventions. Hover and scrub: frame 1 shows the inputs arriving at the leaf, frame 2 lights the leaf up with the full formula, frame 3 produces O₂ and glucose. ## Historical timeline Five eras on one line. Connectors make relative position explicit so readers don't mistake the spacing for proportional time. ```gg-diagram gallery doc { cols: 5 } icon :anc tabler/tower "Ancient" icon :med tabler/building-castle "Medieval" icon :ren tabler/flower "Renaissance" icon :ind tabler/bulldozer "Industrial" icon :mod tabler/rocket "Modern" anc --> med med --> ren ren --> ind ind --> mod ``` ## Learning progression Prerequisite graph for a curriculum. A student starts at "basics" and chooses a track based on their goal. ```gg-diagram gallery doc { cols: 4, rows: 3 } icon :basics @A2 tabler/book "Basics" icon :algo @B1 tabler/circles "Algo" icon :web @B3 tabler/world "Web" icon :sys @C1 tabler/server "Systems" icon :app @C3 tabler/device-mobile "Apps" icon :pro @D2 tabler/trophy "Pro" basics --> algo basics --> web algo --> sys web --> app sys --> pro app --> pro ``` --- # Event-driven Patterns where the unit of work is an event, not a function call. ## Pub/sub with regions and theming Producers emit events via a central bus; consumers subscribe. The emerald-teal theme plus a regioned fan-out makes the "one-to-many" shape obvious at a glance. ```gg-diagram gallery doc { cols: 4, theme: { primary: '#047857', secondary: '#0d9488', accent: '#f59e0b' }, } region @A1:A1 "Producer" color=primary/28 region @B1:B1 "Bus" color=accent/28 region @C1:D2 "Consumers" color=secondary/24 icon :pub @A1 tabler/broadcast "Pub" icon :bus @B1 tabler/arrows-shuffle "Bus" sizeScale=1.4 icon :sub1 @C1 tabler/bell "Email" icon :sub2 @D1 tabler/device-mobile "Push" icon :sub3 @C2 tabler/file-text "Log" icon :sub4 @D2 tabler/database "Warehouse" pub --> bus "emit" width=2 bus --> sub1 dash="2 4" bus --> sub2 dash="2 4" bus --> sub3 dash="2 4" bus --> sub4 dash="2 4" ``` ## Event sourcing State is a reduction over an append-only log. Compact, plain styling here — no regions, no badges — lets the next page read as "and now the complicated version." ```gg-diagram gallery doc { cols: 4 } icon :cmd @A1 tabler/terminal "Command" icon :store @B1 tabler/database "Event store" sizeScale=1.3 icon :proj @C1 tabler/refresh "Projector" icon :view @D1 tabler/eye "Read model" cmd --> store "append" store --> proj "replay" proj --> view "build" ``` ## Retries and dead-letter queue One unhealthy subscriber gradually drives the system toward its dead-letter branch. Hover to scrub: frame 1 is the steady state, frame 2 flags sub-2 red, frame 3 routes the failed message into the DLQ with a retries note. ## Saga state machine Compact linear flow with one compensating branch. Minimal styling — a good compact finisher after the three richer ones. ```gg-diagram gallery doc { cols: 4 } icon :o @A1 tabler/shopping-cart "Order" icon :p @B1 tabler/credit-card "Pay" icon :s @C1 tabler/truck-delivery "Ship" icon :d @D1 tabler/check "Done" o --> p p --> s s --> d p --> o "refund" dash="4 4" ``` --- # Infrastructure Cloud topology, deployment layouts, capacity planning. Regions pull their weight here — public vs private zones, regional failover bands, blast-radius boundaries. ## Three-tier web architecture The classic front / app / data split. Two strongly-coloured regions call out the public and private trust boundaries; a note marks the SLA target for the API tier. ```gg-diagram gallery doc { cols: 4 } region @A1:B1 "Public zone" color=accent/28 region @C1:D1 "Private zone" color=primary/28 icon :client tabler/device-laptop "Client" icon :web tabler/world "Web" icon :api tabler/server "API" sizeScale=1.2 icon :db tabler/database "DB" client --> web "HTTPS" width=2 web --> api "REST" api --> db "SQL" note @C2 (api) "SLA 99.95%\np99 < 200ms" ``` ## Load-balanced backend with read replica LB fans out across two app instances; writes land on the primary, reads flow from a replica. Three tinted regions make the tiers legible at a glance. ```gg-diagram gallery doc { cols: 4 } region @A1:A1 "Edge" color=accent/28 region @B1:B1 "App tier" color=primary/24 region @C1:D1 "Data" color=secondary/24 icon :lb @A1 tabler/equal "LB" icon :app1 @B1 tabler/server "app-1" icon :dbw @C1 tabler/database "primary" sizeScale=1.3 icon :dbr @D1 tabler/database-export "replica" lb --> app1 app1 --> dbw "write" width=2 dbw --> dbr dash="2 4" note @C2 (dbr) "Lag ≤ 5s" ``` ## CDN + origin + cache Cold and warm cache split across two frames. Hover and flip between them: frame 1 is a cold cache — the miss fans out to origin and fills; frame 2 is a warm cache serving the cached reply directly. ## Active / passive multi-region Two regions hold the same stack. DNS routes all traffic to the active region until a health check trips. Regions make the "everything east" vs "everything west" split obvious. ```gg-diagram gallery doc { cols: 3, rows: 3 } region @A1:C1 "DNS" color=accent/24 region @A2:A3 "us-east (active)" color=primary/28 region @C2:C3 "eu-west (standby)" color=secondary/24 icon :dns @B1 tabler/world "DNS" sizeScale=1.2 icon :us @A2 tabler/server "us-east" icon :eu @C2 tabler/server "eu-west" icon :dbus @A3 tabler/database "db-us" icon :dbeu @C3 tabler/database "db-eu" dns --> us "100%" width=2 dns --> eu "0%" dash="2 4" us <-> dbus eu <-> dbeu dbus <-> dbeu "async" dash="4 4" note @B2 "Failover via\nhealth checks" ``` --- # IoT / hardware Sensor networks, edge computing, device control loops. ## Sensor → gateway → cloud Classic IoT pipeline. Constrained devices ship data via a local gateway to a cloud backend that handles storage and analytics. ```gg-diagram gallery doc { cols: 4 } icon :sens tabler/temperature "Sensor" icon :gw tabler/router "Gateway" icon :cld tabler/cloud "Cloud" icon :dash tabler/chart-dots "Dashboard" sens --> gw "Bluetooth" gw --> cld "MQTT" cld --> dash ``` ## Robotic control loop Sense → plan → act, repeated every tick. The planning step is where an ML model might sit; the rest is deterministic. ```gg-diagram gallery doc { cols: 4 } icon :world tabler/leaf "Environment" icon :sense tabler/camera "Sense" icon :plan tabler/brain "Plan" icon :act tabler/engine "Actuate" world --> sense sense --> plan plan --> act act --> world "effect" ``` ## Smart home topology Hub-and-spoke over the local network. The hub bridges to the cloud for off-site control and voice assistants. ```gg-diagram gallery doc { cols: 3, rows: 3 } icon :hub @B2 tabler/home-bolt "Hub" sizeScale=1.3 icon :lights @A1 tabler/bulb "Lights" icon :lock @C1 tabler/lock "Lock" icon :cam @A3 tabler/camera "Camera" icon :therm @C3 tabler/temperature "Thermo" hub <-> lights hub <-> lock hub <-> cam hub <-> therm ``` ## Edge + cloud inference Latency-critical inference runs at the edge; aggregate analytics go to the cloud. Models retrained in the cloud and pushed back to the edge on a schedule. ```gg-diagram gallery doc { cols: 4, rows: 2 } icon :sens @A2 tabler/camera "Camera" icon :edge @B2 tabler/device-tv "Edge" icon :cloud @D2 tabler/cloud "Cloud" icon :model @D1 tabler/brain "Retrain" sens --> edge "stream" edge --> cloud "metrics" cloud --> model "new data" model --> edge "deploy" ``` --- # Knowledge graph Concept hierarchies, taxonomies, and relationship networks. ## Taxonomy Strict tree: every node has exactly one parent. Good for file systems, category browsing, biological classification. ```gg-diagram gallery doc { cols: 3, rows: 3 } icon :anim @B1 tabler/paw "Animal" sizeScale=1.3 icon :mam @A2 tabler/paw "Mammal" icon :bir @C2 tabler/feather "Bird" icon :cat @A3 tabler/cat "Cat" icon :dog @B3 tabler/dog "Dog" icon :eag @C3 tabler/feather "Eagle" anim --> mam anim --> bir mam --> cat mam --> dog bir --> eag ``` ## Faceted tags Items categorised along multiple independent axes. A "cross-cutting" model — nothing is in a strict hierarchy anymore. ```gg-diagram gallery doc { cols: 4, rows: 3 } icon :item @B2 tabler/box "Item" icon :col @A1 tabler/palette "Color" icon :siz @C1 tabler/ruler "Size" icon :use @D1 tabler/tag "Use" icon :mat @A3 tabler/stack "Material" item <-> col item <-> siz item <-> use item <-> mat ``` ## Concept relations Nouns connected by named edges — the skeleton of a small ontology. RDF / Wikidata graphs look like this with more vocabulary. ```gg-diagram gallery doc { cols: 4, rows: 3 } icon :author @A2 tabler/user "Author" icon :book @B2 tabler/book "Book" icon :topic @D1 tabler/tag "Topic" icon :pub @D2 tabler/building "Publisher" icon :year @D3 tabler/calendar "Year" author --> book "wrote" book --> topic "about" book --> pub "by" book --> year "in" ``` ## Knowledge graph for an agent What an LLM "sees" when you hand it a knowledge graph for retrieval. Each edge is a typed relation the agent can traverse. ```gg-diagram gallery doc { cols: 4, rows: 3 } icon :q @A2 tabler/search "Query" icon :ent @B2 tabler/box "Entity" icon :rel1 @C1 tabler/link "relatedTo" icon :rel2 @C3 tabler/link "partOf" icon :res @D2 tabler/bulb "Answer" q --> ent ent --> rel1 ent --> rel2 rel1 --> res rel2 --> res ``` --- # Manufacturing Production lines, quality gates, and continuous-improvement loops. Badges mark pass/fail decisions; regions partition the plant floor. ## Production line with quality gate Four stages feed a QA gate before packaging. Failed units loop back to rework. Badge marks the stage that most often catches defects. ```gg-diagram gallery doc { cols: 5, rows: 2 } icon :raw @A1 tabler/box "Raw" icon :cut @B1 tabler/scissors "Cut" icon :weld @C1 tabler/bolt "Weld" icon :qa @D1 tabler/clipboard-check "QA" { badges: ['star'] } icon :pack @E1 tabler/package "Pack" icon :re @C2 tabler/refresh "Rework" raw --> cut cut --> weld weld --> qa qa --> pack "pass" qa --> re "fail" dash="4 4" color=accent re --> weld ``` ## Plant floor zones Raw material storage / fab / assembly / shipping as rectangular regions. The MES (manufacturing execution system) sits outside the line and talks to every zone. ```gg-diagram gallery doc { cols: 5, rows: 2 } region @A1:A2 "Raw" color=secondary/14 region @B1:C2 "Fab" color=primary/14 region @D1:D2 "Assembly" color=accent/14 region @E1:E2 "Ship" color=primary/14 icon :wh @A2 tabler/building-warehouse "WH" icon :cnc @B2 tabler/tools "CNC" icon :paint @C2 tabler/brush "Paint" icon :asm @D2 tabler/hammer "Assemble" icon :out @E2 tabler/truck-delivery "Out" icon :mes @C1 tabler/device-desktop "MES" sizeScale=1.2 wh --> cnc cnc --> paint paint --> asm asm --> out mes --> cnc "plan" dash="2 4" mes --> paint "plan" dash="2 4" mes --> asm "plan" dash="2 4" ``` ## Kaizen improvement cycle The PDCA loop. Each cycle picks up metrics from the previous one; small improvements compound over quarters. ```gg-diagram gallery doc { cols: 4 } icon :plan tabler/pencil "Plan" icon :do tabler/hammer "Do" icon :check tabler/clipboard-check "Check" icon :act tabler/check "Act" plan --> do do --> check check --> act act --> plan "next" dash="4 4" note @A2 (plan) "1 week\ncadence" ``` ## OEE dashboard inputs Overall Equipment Effectiveness aggregates availability, performance, and quality. Each machine feeds its own metrics; the dashboard rolls them up. ```gg-diagram gallery doc { cols: 4, rows: 3 } icon :m1 @A1 tabler/tools "Machine 1" icon :m2 @A2 tabler/tools "Machine 2" icon :m3 @A3 tabler/tools "Machine 3" icon :avail @C1 tabler/clock "Availability" icon :perf @C2 tabler/chart-line "Performance" icon :qual @C3 tabler/star "Quality" icon :oee @D2 tabler/chart-bar "OEE" sizeScale=1.3 m1 --> avail m2 --> perf m3 --> qual avail --> oee perf --> oee qual --> oee ``` --- # Microservices Decomposition patterns for modern service architectures. Size and colour communicate which node bears the most load / coordination. ## API gateway with downstream services One gateway sits in front of many services. It's given a dramatic `sizeScale=1.6` so the "fan-in / fan-out" hierarchy reads at a glance. ```gg-diagram gallery doc { cols: 4, rows: 2 } region @A1:B2 "Edge" color=accent/28 region @C1:D2 "Services" color=primary/24 icon :client @A1 tabler/device-laptop "Client" icon :gw @A2 tabler/api "Gateway" sizeScale=1.6 icon :users @C1 tabler/users "Users" { badges: ['check'] } icon :orders @D1 tabler/shopping-cart "Orders" { badges: ['check'] } icon :notify @C2 tabler/bell "Notify" { badges: ['alert'] } icon :billing @D2 tabler/credit-card "Billing" { badges: ['check'] } client --> gw gw --> users gw --> orders gw --> notify dash="2 4" gw --> billing ``` ## Service-to-service via queue Producers keep going if consumers are slow. Badges show the health-check status of each service; the dashed arrow marks the async hop. ```gg-diagram gallery doc { cols: 4 } icon :orders @A1 tabler/shopping-cart "Orders" { badges: ['check'] } icon :queue @B1 tabler/inbox "Queue" sizeScale=1.3 icon :fulfill @C1 tabler/truck-delivery "Fulfill" { badges: ['check'] } icon :notify @D1 tabler/bell "Notify" { badges: ['alert'] } orders --> queue "enqueue" width=2 queue --> fulfill "consume" queue --> notify "consume" dash="4 4" note @B2 (notify) "Lagging —\ninvestigate" ``` ## Saga: distributed transaction A three-step saga told across three frames — hover the diagram and use ◀ / ▶ to step through. Frame 1 is the happy forward path; frame 2 flags a failed payment; frame 3 draws the compensating refund branch. ## Service mesh sidecar pattern Every app pod gets a sidecar proxy. The mesh layer owns mTLS, retries, and telemetry uniformly across languages. ```gg-diagram gallery doc { cols: 4, rows: 2 } region @A1:D1 "App containers" color=primary/24 region @A2:D2 "Mesh sidecars" color=secondary/24 icon :app1 @A1 tabler/server "app-1" icon :app2 @B1 tabler/server "app-2" icon :app3 @C1 tabler/server "app-3" icon :app4 @D1 tabler/server "app-4" icon :sc1 @A2 tabler/shield "proxy" icon :sc2 @B2 tabler/shield "proxy" icon :sc3 @C2 tabler/shield "proxy" icon :sc4 @D2 tabler/shield "proxy" app1 --> sc1 app2 --> sc2 app3 --> sc3 app4 --> sc4 sc1 --> sc2 "mTLS" width=2 sc3 --> sc4 "mTLS" width=2 ``` --- # Org chart People and reporting lines. Gridgram's grid layout handles shallow hierarchies well; for deep trees, consider a dedicated tree layout tool. ## Team with real photo avatars Swaps the silhouette icons for actual headshots (placeholders via `picsum.photos`). `clip=circle` rounds each photo into an avatar disc; `iconTheme=native` preserves the original photo colours instead of tinting them with the node's theme colour. Raster assets behave the same as Tabler icons everywhere else — `pos`, `sizeScale`, regions, and connectors work unchanged. The difference is just which file the icon resolver serves for `src=...`. ## Small-company reporting CEO at the top, functional reports below. No cross-connections — every report has exactly one manager. ```gg-diagram gallery doc { cols: 4, rows: 2 } icon :ceo @B1 tabler/user-star "CEO" sizeScale=1.4 { badges: ['star'] } icon :cto @A2 tabler/user "CTO" icon :cpo @C2 tabler/user "CPO" icon :cfo @D2 tabler/user "CFO" ceo --> cto ceo --> cpo ceo --> cfo ``` ## Engineering team shape Director with two line managers, each with engineers. A squad reports to its line manager but takes technical direction from a principal. ```gg-diagram gallery doc { cols: 4, rows: 3 } icon :dir @B1 tabler/user-star "Director" icon :m1 @A2 tabler/user "EM A" icon :m2 @C2 tabler/user "EM B" icon :e1 @A3 tabler/users "team A" icon :e2 @C3 tabler/users "team B" icon :prin @D2 tabler/star "Principal" dir --> m1 dir --> m2 m1 --> e1 m2 --> e2 prin --> e1 "tech" prin --> e2 "tech" ``` ## Matrix organization Employees report to both a functional manager (discipline) and a project manager (product). The "two hats" problem visualised. ```gg-diagram gallery doc { cols: 3, rows: 2 } icon :eng @A1 tabler/users "Eng" icon :des @C1 tabler/palette "Design" icon :p1 @A2 tabler/briefcase "Prod A" icon :p2 @C2 tabler/briefcase "Prod B" eng --> p1 eng --> p2 des --> p1 des --> p2 ``` ## Squad / tribe structure Spotify-style model. A tribe is a business-area grouping of squads; chapters are cross-squad disciplines; guilds are voluntary communities of practice. ```gg-diagram gallery doc { cols: 4, rows: 2 } icon :tribe @B1 tabler/users-group "Tribe" sizeScale=1.2 icon :s1 @A2 tabler/users "Squad A" icon :s2 @C2 tabler/users "Squad B" icon :chap @D1 tabler/hierarchy "Chapter" icon :guild @D2 tabler/users "Guild" tribe --> s1 tribe --> s2 chap --> s1 chap --> s2 ``` --- # Project planning Task dependencies, sprint cycles, and milestone timelines. ## Task dependency graph A small project where some tasks can run in parallel and others must serialise. Critical path surfaces as the longest chain. ```gg-diagram gallery doc { cols: 4, rows: 3 } icon :design @A2 tabler/pencil "Design" icon :api @B1 tabler/code "API" icon :ui @B3 tabler/layout "UI" icon :int @C2 tabler/link "Integrate" icon :test @D2 tabler/check "Test" design --> api design --> ui api --> int ui --> int int --> test ``` ## Sprint cycle A two-week agile loop: plan → work → review → retro, then repeat. The retro feeds improvements back into the next plan. ```gg-diagram gallery doc { cols: 4 } icon :plan tabler/calendar "Plan" icon :work tabler/hammer "Work" icon :review tabler/eye "Review" icon :retro tabler/refresh "Retro" plan --> work work --> review review --> retro retro --> plan "next sprint" ``` ## Milestone timeline Linear progression of project milestones. Useful for keeping stakeholders aligned on "where are we right now." ```gg-diagram gallery doc { cols: 5 } icon :m1 tabler/flag "Kickoff" icon :m2 tabler/target "Alpha" icon :m3 tabler/rocket "Beta" icon :m4 tabler/trophy "GA" icon :m5 tabler/chart-line "Scale" m1 --> m2 m2 --> m3 m3 --> m4 m4 --> m5 ``` ## Release train Fixed cadence trains leaving on schedule; features board whichever train is ready. Missing a train is fine — you catch the next. ```gg-diagram gallery doc { cols: 5 } icon :f1 tabler/box "Feature A" icon :f2 tabler/box "Feature B" icon :f3 tabler/box "Feature C" icon :t1 tabler/train "Train Q1" icon :t2 tabler/train "Train Q2" f1 --> t1 "ready" f2 --> t1 "ready" f3 --> t2 "next" t1 --> t2 dash="4 4" ``` --- # Release & roadmap Version lifecycles, feature rollouts, deprecation timelines. ## Version timeline Three releases across a year, each introducing a theme. Useful for communicating direction to stakeholders outside the team. ```gg-diagram gallery doc { cols: 5 } icon :v1 tabler/flag "v1.0" icon :v2 tabler/rocket "v1.1" icon :v3 tabler/bolt "v2.0" icon :v4 tabler/star "v2.1" icon :v5 tabler/trophy "v3.0" v1 --> v2 v2 --> v3 v3 --> v4 v4 --> v5 note @C2 "v1→v3: search & accounts\nv4→v5: mobile & AI" ``` ## Feature rollout (canary → beta → GA) Percentage-based rollout. Each stage holds for a period; a spike in errors rolls back instead of advancing. ```gg-diagram gallery doc { cols: 4 } icon :dev tabler/pencil "Dev" icon :canary tabler/percentage "Canary 1%" icon :beta tabler/percentage "Beta 10%" icon :ga tabler/percentage "GA 100%" dev --> canary "deploy" canary --> beta "no spikes" beta --> ga "no spikes" ``` ## Quarterly roadmap Four quarters, themed. Mid-quarter reviews check if the theme is still the right bet; the plan shifts if the market moved. ```gg-diagram gallery doc { cols: 4 } icon :q1 tabler/calendar "Q1 Foundations" icon :q2 tabler/calendar "Q2 Scale" icon :q3 tabler/calendar "Q3 Polish" icon :q4 tabler/calendar "Q4 Ecosystem" q1 --> q2 q2 --> q3 q3 --> q4 ``` ## Deprecation schedule New-and-old running in parallel, then old turned off. The "announce → deprecate → sunset" cadence gives consumers time to migrate. ```gg-diagram gallery doc { cols: 4 } icon :ann tabler/bell "Announce" icon :dep tabler/alert-triangle "Deprecate" icon :sun tabler/sunset "Sunset" icon :rem tabler/trash "Remove" ann --> dep "6 months" dep --> sun "3 months" sun --> rem "immediate" ``` --- # Sales funnel Prospect-to-customer flows and conversion stages. ## Classic marketing funnel Awareness → interest → consideration → purchase. Each stage loses users; the widths of the real funnel reflect conversion rates. ```gg-diagram gallery doc { cols: 5 } icon :aware tabler/eye "Aware" icon :interest tabler/heart "Interest" icon :consider tabler/scale "Consider" icon :buy tabler/credit-card "Buy" icon :loyal tabler/star "Loyal" aware --> interest interest --> consider consider --> buy buy --> loyal ``` ## B2B sales pipeline From lead qualification through contract. Each stage has its own playbook; gates prevent premature promotion. ```gg-diagram gallery doc { cols: 5 } icon :lead tabler/user-plus "Lead" icon :qual tabler/filter "Qualify" icon :demo tabler/presentation "Demo" icon :pilot tabler/flask "Pilot" icon :close tabler/signature "Close" lead --> qual qual --> demo demo --> pilot pilot --> close ``` ## Customer segmentation Leads route to different playbooks based on company size. Each segment has its own sales motion. ```gg-diagram gallery doc { cols: 4, rows: 3 } icon :leads @A2 tabler/users "Leads" icon :smb @C1 tabler/building-store "SMB" icon :mid @C2 tabler/building "Mid-market" icon :ent @C3 tabler/building-skyscraper "Enterprise" icon :close @D2 tabler/signature "Close" leads --> smb leads --> mid leads --> ent smb --> close mid --> close ent --> close ``` ## Campaign attribution Multiple channels lead to the same conversion. Attribution models decide which channel "gets credit" — last-touch, first-touch, linear, time-decay. ```gg-diagram gallery doc { cols: 4, rows: 3 } icon :paid @A1 tabler/ad "Paid" icon :organic @A2 tabler/world "Organic" icon :social @A3 tabler/brand-x "Social" icon :visit @C2 tabler/user "Visit" icon :conv @D2 tabler/coin "Convert" paid --> visit organic --> visit social --> visit visit --> conv ``` --- # Security & auth Authentication flows, trust boundaries, secret handling. ## OAuth 2 authorization code The browser-assisted flow for third-party login. The client never sees the resource owner's password. ```gg-diagram gallery doc { cols: 4 } icon :user tabler/user "User" icon :client tabler/device-laptop "Client" icon :auth tabler/lock "Authz" icon :api tabler/server "API" user --> client "creds" client <-> auth "OAuth2" client --> api "+token" ``` ## JWT verification Tokens flow through the gateway. Downstream services trust the token because the gateway verified it against the auth service. ```gg-diagram gallery icon :client tabler/user-circle "Client" icon :gw tabler/api "Gateway" icon :authn tabler/key "Authn" icon :svc tabler/server "Service" client --> gw "JWT" gw --> authn "verify" authn --> gw "claims" gw --> svc "+ claims" ``` ## mTLS between services Both sides present certificates. The mesh / sidecar rotates them automatically so app code never sees them. The trust boundary is made visible as a region. ```gg-diagram gallery doc { cols: 4 } region @A1:D1 "Trust boundary (mesh)" color=primary/14 icon :a tabler/server "service A" icon :certa tabler/certificate "cert" icon :certb tabler/certificate "cert" icon :b tabler/server "service B" a --> certa certa --> certb "mTLS" width=2 certb --> b ``` ## Zero trust network access Every request is verified regardless of network origin. There's no "inside" vs "outside" the firewall. ```gg-diagram gallery doc { cols: 4, rows: 2 } icon :user @A1 tabler/user "User" icon :policy @B1 tabler/shield-check "Policy" icon :trust @C1 tabler/lock "Trust" icon :app @D1 tabler/server "App" icon :device @A2 tabler/device-laptop "Device" icon :ident @B2 tabler/fingerprint "Identity" user --> policy device --> ident ident --> policy policy --> trust trust --> app ``` --- # Software architecture Layering, boundaries, and dependency direction. ## Layered (n-tier) Presentation → application → domain → infrastructure. Each layer only knows the one below. ```gg-diagram gallery doc { cols: 4 } icon :ui @A1 tabler/user-circle "UI" icon :app @B1 tabler/server "App" icon :domain @C1 tabler/atom "Domain" icon :infra @D1 tabler/database "Infra" ui --> app app --> domain domain --> infra ``` ## Hexagonal (ports & adapters) The domain sits at the centre. Everything outside — HTTP, DB, queues — is an adapter pluggable via the domain's ports. ```gg-diagram gallery doc { cols: 4, rows: 3 } icon :http @A2 tabler/world "HTTP" icon :cli @B1 tabler/terminal "CLI" icon :core @C2 tabler/atom "Domain" sizeScale=1.3 icon :db @D2 tabler/database "DB" icon :queue @D3 tabler/inbox "Queue" http --> core cli --> core core <-> db core --> queue ``` ## Clean architecture Dependency arrows all point inwards. The framework is a detail; entities don't know it exists. ```gg-diagram gallery doc { cols: 4 } icon :fw @A1 tabler/package "Framework" icon :ctrl @B1 tabler/settings "Controller" icon :uc @C1 tabler/refresh "Use case" icon :ent @D1 tabler/atom "Entity" fw --> ctrl ctrl --> uc uc --> ent ``` ## Plugin architecture A host application loads plugins at runtime. Each plugin implements the host's interface and contributes behaviour without touching the host's code. ```gg-diagram gallery doc { cols: 3, rows: 3 } icon :host @A2 tabler/box "Host" sizeScale=1.5 icon :p1 @C1 tabler/puzzle "Plugin A" icon :p2 @C2 tabler/puzzle "Plugin B" icon :p3 @C3 tabler/puzzle "Plugin C" host <-> p1 host <-> p2 host <-> p3 ``` --- # Supply chain Material moving from suppliers to end customers, with reverse flow for returns. Regions group the upstream / production / downstream bands; dashed lines mark the reverse path. ## Three-stage supply chain with returns Upstream suppliers feed production; production pushes through distribution to the customer; a dashed reverse arrow carries returns back to the DC. Note calls out lead time. ```gg-diagram gallery doc { cols: 6, rows: 3, theme: { primary: '#1e3a5f', accent: '#e8792f' } } region @A1:B3 "Upstream" color=primary/14 region @E1:F3 "Downstream" color=accent/14 icon :sup1 @A1 tabler/building-factory "Supplier A" icon :sup2 @A3 tabler/building-factory "Supplier B" icon :wh @B2 tabler/building-warehouse "Raw WH" icon :prod @C2 tabler/hammer "Production" sizeScale=1.3 icon :qa @D2 tabler/clipboard-check "QA" icon :dc @E2 tabler/building-warehouse "DC" icon :cust @F2 tabler/user "Customer" sup1 --> wh sup2 --> wh wh --> prod prod --> qa qa --> dc dc --> cust cust --> dc "returns" dash="4 4" note @C1 (prod) "Lead time\n14 days" ``` ## Multi-tier suppliers Tier-2 suppliers feed Tier-1 assemblers, who feed the OEM. Visual tiers make the dependency depth obvious at a glance. ```gg-diagram gallery doc { cols: 4, rows: 3 } region @A1:A3 "Tier 2" color=secondary/14 region @B1:B3 "Tier 1" color=primary/14 icon :t2a @A1 tabler/building-factory "raw mat" icon :t2b @A2 tabler/building-factory "components" icon :t2c @A3 tabler/building-factory "packaging" icon :t1a @B1 tabler/building-factory "assembly" icon :t1b @B2 tabler/building-factory "assembly" icon :oem @C2 tabler/building-factory "OEM" sizeScale=1.3 icon :ship @D2 tabler/truck-delivery "Ship" t2a --> t1a t2b --> t1a t2b --> t1b t2c --> t1b t1a --> oem t1b --> oem oem --> ship ``` ## Inventory flow: receive → store → pick The minimal loop any DC runs. Barcode / QR scans drive every transition; mismatches route to an exception bin. ```gg-diagram gallery doc { cols: 5 } icon :recv tabler/truck-loading "Receive" icon :scan tabler/barcode "Scan" icon :store tabler/stack-2 "Store" icon :pick tabler/package "Pick" icon :ship tabler/truck-delivery "Ship" recv --> scan scan --> store "match" scan --> pick "mismatch" dash="4 4" color=accent store --> pick pick --> ship ``` ## Reverse logistics Returns flow back through inspection, then branch to refurbish or recycle. Hover the diagram and scrub — frame 1 is intake and inspection, frame 2 takes the serviceable-refurb branch, frame 3 takes the damaged-recycle branch. --- # Testing Test organisation, CI matrices, and environment topologies. ## Test pyramid Many fast unit tests at the base, fewer integration tests in the middle, very few end-to-end tests at the top. Inverts the "ice-cream cone" anti-pattern where slow E2E dominate. ```gg-diagram gallery doc { cols: 3, rows: 3 } icon :e2e @B1 tabler/eye "E2E" icon :int @A2 tabler/link "Int" icon :int2 @C2 tabler/link "Int" icon :u1 @A3 tabler/circle-check "Unit" icon :u2 @B3 tabler/circle-check "Unit" icon :u3 @C3 tabler/circle-check "Unit" e2e --> int e2e --> int2 int --> u1 int --> u2 int2 --> u3 ``` ## CI matrix: OS × language version Same test suite, multiple targets. Any red cell blocks the merge. ```gg-diagram gallery doc { cols: 5, rows: 3 } icon :src @A2 tabler/git-commit "Commit" icon :m1 @C1 tabler/server "linux/18" icon :m2 @C2 tabler/server "linux/20" icon :m3 @C3 tabler/server "linux/22" icon :w1 @D1 tabler/server "macos/20" icon :w2 @D2 tabler/server "win/20" icon :merge @E2 tabler/check "Merge" src --> m1 src --> m2 src --> m3 src --> w1 src --> w2 m1 --> merge m2 --> merge m3 --> merge w1 --> merge w2 --> merge ``` ## Test environments Each environment is a deployment target tuned for a different feedback loop. Smoke tests gate promotion. ```gg-diagram gallery doc { cols: 5 } icon :local tabler/device-laptop "Local" icon :ci tabler/settings "CI" icon :stage tabler/server "Staging" icon :perf tabler/chart-bar "Perf" icon :prod tabler/rocket "Prod" local --> ci ci --> stage stage --> perf "nightly" stage --> prod "approve" ``` ## Test coverage feedback Coverage reports attach to PRs so reviewers see which lines the new code actually exercised. ```gg-diagram gallery icon :test tabler/check "Tests" icon :cov tabler/percentage "Coverage" icon :report tabler/file-text "Report" icon :pr tabler/git-pull-request "PR" test --> cov "measure" cov --> report "collect" report --> pr "annotate" ``` --- # Warehouse Physical zone layouts and pick routes. Regions carve out the functional areas; waypoints thread pick paths around the shelves. ## Warehouse zones Receiving / storage / picking / shipping laid out in a grid, each in its own coloured region. The diagram doubles as a map. ```gg-diagram gallery doc { cols: 4, rows: 3 } region @A1:A3 "Receive" color=primary/14 region @B1:C2 "Storage" color=secondary/14 region @B3:C3 "Picking" color=accent/14 region @D1:D3 "Ship" color=primary/14 icon :dock1 @A1 tabler/truck-loading "Dock 1" icon :dock2 @A3 tabler/truck-loading "Dock 2" icon :a1 @B1 tabler/stack-2 "Aisle A" icon :a2 @C1 tabler/stack-2 "Aisle B" icon :a3 @B2 tabler/stack-2 "Aisle C" icon :a4 @C2 tabler/stack-2 "Aisle D" icon :pick1 @B3 tabler/package "Pick 1" icon :pick2 @C3 tabler/package "Pick 2" icon :out1 @D1 tabler/truck-delivery "Out 1" icon :out2 @D3 tabler/truck-delivery "Out 2" ``` ## Pick path with waypoints A single pick tour visits four aisles on a fixed route. Waypoints force the path instead of letting the router pick shortcuts. ```gg-diagram gallery doc { cols: 5, rows: 4 } icon :start @A1 tabler/map-pin "Start" icon :a1 @B1 tabler/stack-2 "A1" icon :b1 @C1 tabler/stack-2 "B1" icon :a3 @B3 tabler/stack-2 "A3" icon :b3 @C3 tabler/stack-2 "B3" icon :ship @E4 tabler/truck-delivery "Ship" start --> ship "route" { waypoints: [ { col: 2, row: 1 }, { col: 3, row: 1 }, { col: 3, row: 3 }, { col: 2, row: 3 }, { col: 5, row: 3 }, ] } ``` ## Pick-and-pack flow Orders land; WMS breaks them into picks; a packer consolidates each order before it leaves. Note calls out the target throughput. ```gg-diagram gallery doc { cols: 5 } icon :order tabler/shopping-cart "Orders" icon :wms tabler/device-desktop "WMS" icon :picker tabler/user-check "Picker" icon :pack tabler/package "Pack" icon :out tabler/truck-delivery "Out" order --> wms wms --> picker "drop" picker --> pack pack --> out ``` ## Cycle count & audit A scheduled inventory audit runs in the background. Discrepancies branch off to investigation; the audit log feeds both compliance and a continuous-improvement loop. ```gg-diagram gallery doc { cols: 4, rows: 3 } icon :sched @A2 tabler/calendar "Schedule" icon :count @B2 tabler/barcode "Count" icon :match @C2 tabler/equal "Match" icon :invest @D1 tabler/search "Investigate" icon :log @D3 tabler/clipboard-list "Log" sched --> count count --> match match --> log match --> invest "delta > 2%" dash="4 4" color=accent invest --> log ``` --- # Quick start Install the `gg` binary and render your first diagram in under a minute. If you'd rather not pipe a remote script into a shell, see [Install](./install) for manual alternatives. ## Install Pick the command for your operating system. Both scripts place `gg` on your `PATH` automatically. ### macOS / Linux ```sh curl -fsSL https://bin.ideamans.com/install/gg.sh | bash ``` ### Windows (PowerShell) ```powershell irm https://bin.ideamans.com/install/gg.ps1 | iex ``` ### Verify ```sh gg --help ``` You should see the usage banner. If `gg: command not found`, open a new shell (so the updated `PATH` is picked up) or consult [Install](./install). ## Render your first diagram Pipe a one-line `.gg` source straight into `gg` — `-` as the input path tells the CLI to read from stdin. `;` separates statements so the whole diagram fits on one command line. ### macOS / Linux ```sh echo 'icon :u tabler/user "User"; icon :a tabler/server "API"; u --> a "request"' | gg -o hello.png - --width 1024 ``` Swap `hello.png` for `hello.svg` (drop `--width`) to get a vector file, or use `--format svg --stdout` to print the SVG straight to your terminal. ### Windows (PowerShell) ```powershell 'icon :u tabler/user "User"; icon :a tabler/server "API"; u --> a "request"' | gg -o hello.png - --width 1024 ``` Open `hello.png` in your image viewer — you should see two icons with a labelled arrow between them. That's it; the CLI is working. ## Where to next - **[First Gridgram](./first-gridgram)** — a guided tour of the `.gg` language, one concept at a time. - **[Install](./install)** — GitHub Releases, build from source, and other ways to get the binary. - **[CLI reference](./cli)** — every flag and exit code. --- # CLI The `gg` CLI renders a `.gg` file to SVG, PNG, or JSON (for debugging the merged `DiagramDef`). It's the tool most people will use. ## Usage ``` gg [options] gg [args…] ``` ```sh gg diagram.gg -o out.svg gg diagram.gg -o out.png --width 2048 gg diagram.gg --cell-size 128 -o out.png gg diagram.gg --icons ./icons/ -o out.svg gg diagram.gg --format json > merged.json ``` Without `-o`, `gg` writes to stdout. Without `--format`, the extension of `-o` picks the format (`.svg` / `.png` / `.json`); lacking both, the default is SVG. ## Subcommands | Subcommand | What it does | |----------------------|-----------------------------------------------------------------------------------| | `gg render ` | Render a `.gg` file (the default when a file path is given — `gg ` is a shortcut). | | `gg icons` | Search the 6,000+ built-in Tabler icons (by name, tag, or category). | | `gg llm` | Emit the LLM-facing reference bundle — `.gg` grammar, CLI, icons, JSON schema, examples. | | `gg license` | Print bundled third-party license texts. | `gg icons` and `gg llm` are primarily for LLM-driven workflows; the [AI Guide → CLI reference](/en/ai/cli) page documents both in full (flags, scoring model, JSON output shape, agent prompt templates). See the [AI Guide overview](/en/ai/) if you want to drive `gg` from Claude Code, `gh skill`, or an MCP agent. ## Options | Flag | Meaning | |------------------------|---------| | `-o, --output ` | Output path. Extension drives format when `--format` is absent. | | `--format ` | `svg` / `png` / `json`. `json` emits the merged `DiagramDef`. | | `--config ` | Explicit `gridgram.config.{ts,js,json,json5}` (skips walk-up discovery). | | `--no-config` | Disable project-config discovery entirely. | | `--icons ` | Register every `/*.svg` by its basename (bare-name alias). | | `--alias name=dir` | Register an asset-alias prefix: `@name/x.svg` → `/x.svg`. Repeatable. | | `--cell-size ` | Pixel size per cell (default `256`). Changes geometry. | | `--width ` | Final output width in px, aspect preserved. Doesn't change geometry. | | `--scale ` | Extra multiplier on the final width (default `1`; useful for high-DPI PNG). | | `--stdout` | Write to stdout even when `-o` is set. | | `--no-errors` | Suppress red error markers (missing icons, unresolved refs) in the output. | | `--license` | Print bundled third-party license texts. | | `-h, --help` | Show the help text. | ## Settings resolution `gg` merges configuration from four sources, later layers winning: ``` system defaults ↓ project config (gridgram.config.{ts,js,json,json5}) ↓ document doc { … } (blocks inside the .gg file) ↓ CLI flags (--cell-size, --width, --icons, …) ``` Each layer contributes whatever it sets; omitted fields fall through from the layer above. So `--cell-size 128` always wins over `doc { cellSize: 256 }`, which always wins over the project config, which always wins over the built-in defaults. ## Project config `gg` walks up from the current working directory to find the first `gridgram.config.{ts,js,json,json5}`. Everything it exports / returns is merged as the "project" layer above. Use it to pin shared theme tokens, icon aliases, and cell size across a whole docs tree: ```ts // gridgram.config.ts import { defineConfig } from 'gridgram' export default defineConfig({ cellSize: 200, theme: { primary: '#065f46', accent: '#d97706', }, assetAliases: { brand: './assets/brand', }, }) ``` Skip discovery with `--no-config`, or point at a specific file with `--config`. ## Output formats - **SVG** (default) — text output; portable, editable, DPI-independent. - **PNG** — rasterized via `sharp` at `--width` × aspect. Use `--scale 2` for retina output. - **JSON** — the merged `DiagramDef` as JSON. Useful for debugging `doc` merges, feeding other tools, or inspecting what the parser resolved from a `.gg` file. ## Exit codes | Code | Meaning | |------|---------| | `0` | Success | | `1` | Parse error (bad DSL, malformed `doc { … }`, unknown flag) | | `2` | Integrity error (duplicate node id, unresolved ref, disjoint region spans) | | `3` | I/O or render error (config load, file read/write, icon load, PNG render) | The exit codes split into "your source has a problem" (1, 2) vs "the environment has a problem" (3). CI scripts can use that to decide whether to fail the build or retry the step. --- # Color Gridgram exposes color through a small, consistent vocabulary. Every element — nodes, connectors, regions, notes, badges — accepts the same grammar, so you rarely have to remember where each kind of value is allowed. ## The grammar ``` primary # bare theme keyword accent # theme keyword accent/60 # theme keyword with 0–99% alpha accent/8 # single-digit alpha → expanded to AA (88 hex) #e8792f # literal hex #e8792f40 # literal hex with alpha red # CSS named color rgb(200, 0, 0) # any CSS color function ``` ## Theme keywords The keyword resolves against the active theme at render time: | Keyword | Used as default for | |------------|------------------------------| | `primary` | node ring + monochrome icon | | `secondary`| connector line | | `accent` | highlight / emphasis | | `text` | note border + text | | `muted` | reserved; user-referenceable | | `bg` | canvas background | Changing the theme changes every `primary`-colored element at once — no find-and-replace needed. ## Alpha: the `/NN` suffix Append `/NN` to a theme keyword to apply alpha: ``` accent/60 → theme.accent at 60% opacity accent/8 → theme.accent at 88% opacity (single digit expands to AA) primary/14 → theme.primary at 14% opacity ``` The two-digit form is a **hex alpha** (`00`–`99`), not a percentage. Numbers from `00` to `ff` are accepted; the `/NN` syntax constrains to two hex digits, and single-digit `/8` expands to `/88` the same way CSS shortens `#888` to `#888888`. ## Literal colors Anything that CSS accepts works — names, hex, `rgb()`, `rgba()`, `hsl()`. Literal colors **bypass the auto-tint** rule for regions; see [Region › Styling](../region/styling) for the full details. ```gg icon :a @A1 tabler/user "red" color=red icon :b @B1 tabler/user "#8b5cf6" color=#8b5cf6 icon :c @C1 tabler/user "rgba" color=rgba(139,92,246,0.6) ``` ## Auto-tint for nodes When you set a **single color** on a node, Gridgram draws the ring in that color **and** fills the interior with ~8% of the same color — automatic. You never have to pick two colors to get the "outline + pale fill" look. The tint happens whenever `color` is set (theme keyword or literal). Alpha you specify explicitly is respected as written. ## Where to next - **[Theme](./theme)** — defining a custom theme, canvas background, transparency --- # Theme A **theme** is the palette Gridgram draws against — the set of color slots that theme keywords (`primary`, `accent`, …) resolve to. Overriding the theme is how you reskin a whole diagram in one place. ## The default theme | Slot | Hex | |-------------|-------------| | `primary` | `#1e3a5f` | | `secondary` | `#3b5a80` | | `accent` | `#e8792f` | | `text` | `#2d3748` | | `bg` | `#ffffff` | `bg` is the canvas background (the rectangle drawn behind everything). All the others are referenced by keyword on elements. ## Overriding slots Set the theme in a `doc { … }` statement (or on `DiagramDef.theme` in TS): ```gg doc { theme: { primary: '#065f46', secondary: '#0369a1', accent: '#d97706', text: '#1f2937', bg: 'transparent', }, } ``` You only need to override the slots you want to change. Unset slots fall back to the default values above via deep-merge. ## Transparent canvas Set `bg` to `'transparent'`, `'none'`, or an empty string `''` to **skip the background rect entirely**. The SVG renders on a transparent canvas — useful when the diagram is inlined into a colored surface (a slide, a card, a dark README). ```gg doc { theme: { bg: 'transparent' } } ``` When the canvas is transparent, **connector-label pills fall back to white** (instead of the usual `theme.bg`) so labels remain readable on any backdrop. ## Multiple `doc` blocks: deep merge If you declare the theme across multiple `doc` blocks, later values win via **deep merge**. That lets you split shared and local overrides: ```gg doc { theme: { primary: '#065f46' } } # … DSL … doc { theme: { accent: '#d97706' } } ``` Both values survive — `primary` and `accent` are overridden, the rest stay at defaults. ## `muted` `muted` is a reserved slot with no Gridgram-internal use. Set it in the theme, then reference `color=muted` on elements you want "secondary" in a consistent way — the single central value keeps all those elements in sync. ```gg doc { theme: { muted: '#94a3b8' } } icon :caption @A3 tabler/info-circle "v0.9" color=muted ``` --- # Connector A **connector** is an arrow (or plain line) drawn between two nodes. It's how you show a relationship — a request, a data flow, a dependency. ## The arrow table Gridgram picks arrow semantics from the operator you write between the two node ids. Solid and dashed variants share the same direction semantics — `-->` / `..>` both point at the target: | Operator | Direction | Line style | |-----------|------------------|------------| | `-->` | Source → Target | solid | | `->` | alias of `-->` | solid | | `<--` | Target → Source | solid | | `<->` | both ends | solid | | `---` | no arrow heads | solid | | `..>` | Source → Target | dashed | | `<..` | Target → Source | dashed | | `<..>` | both ends | dashed | | `...` | no arrow heads | dashed | ## Syntax ``` ["