# Gramit Code - Full Language Reference (for AI assistants) This document is the complete specification of **Gramit Code**, the YAML language that powers the "Code to Diagram" feature in the Gramit diagram editor (https://gramit.io). It is written to be read by an AI assistant so it can generate valid Gramit Code on a user's behalf. If you are an AI reading this: produce a single YAML document that follows the rules below, then hand the user a link that opens it (section 11). Do not invent properties. Only the keys listed here are allowed, and unknown keys are rejected by the editor's validator. You do not have to carry this whole document in your head. Three companion resources answer, over HTTP, what would otherwise be a guess: | Resource | What it answers | |----------------------------------------------|--------------------------------------------------------| | https://www.gramit.io/docs/ai/schema.json | Is the document I just wrote valid? (JSON Schema) | | https://www.gramit.io/docs/ai/icons?q=lambda | Which icon id do I put on this node? | | https://www.gramit.io/docs/ai/examples | What does a whole document of this kind look like? | The short index, for when this reference is more than the task needs: https://www.gramit.io/docs/ai Human-readable docs with live previews: https://www.gramit.io/docs --- ## 1. Document structure A Gramit document is a YAML mapping with up to three kinds of top-level keys. Only `root` is required. ```yaml template: # optional - diagram-level settings name: My Flow theme: dark style: clean direction: LR spacing: 160 root: # required - the main flow nodes: [ ... ] edges: [ ... ] my_subflow: # optional - any other top-level key is a subflow nodes: [ ... ] edges: [ ... ] ``` - `template` - optional diagram-wide settings. - `root` - required. The main flow: a list of `nodes` and the `edges` between them. - Any other top-level key (not `template`, not `root`) defines a **subflow** - a nested diagram opened as its own level in the editor. The diagram only renders when the entire document is valid. --- ## 2. Template ```yaml template: name: User Onboarding # any string (optional) theme: dark # light | dark style: handmade # handmade | clean direction: LR # LR | TB spacing: 160 # px between nodes and between ranks ``` | Field | Values | Notes | |-------|-------------------|--------------------------------------------------| | name | any string | Diagram name. | | theme | `light` \| `dark` | Also switches the editor theme. | | style | `handmade` \| `clean` | Visual style. `clean` is the "Architect" look. | | direction | `LR` \| `TB` | Which way the automatic layout runs: left to right, or top to bottom. Defaults to `LR`, and beats the direction picked in the editor. | | spacing | positive number | Gap the automatic layout leaves between nodes and between ranks, in px. Omitted, the layout sizes the gaps from the content. | Unknown keys inside `template` are errors. --- ## 3. Nodes Every entry under `nodes:` requires a unique `name`. Everything else is optional and inherits from the active style when omitted. ```yaml nodes: - name: my_node # REQUIRED - the node id, unique across the WHOLE document type: simple # optional - defaults to 'simple' text: Hello World # the visible label color: '#1e3a5f' # ...plus any visual property allowed for the type ``` IMPORTANT: - `name` is the node **id**, not the visible label. The visible label is `text`. Use short, code-friendly names (e.g. `login_form`) and put human wording in `text`. - `name` must be **globally unique** - across `root` and every subflow. - Edges reference nodes by `name`. ### Node types Set `type` to choose the shape. Omit it for a plain rectangle (`simple`). | type | Shape on canvas | Extra/required fields | |------------|-----------------|---------------------------------| | `simple` | Rectangle | - | | `circle` | Ellipse/circle | - | | `decision` | Diamond | - | | `text` | Text label | - | | `person` | Person glyph | - | | `icon` | Icon + label | `icon` (icon id) | | `image` | Image + label | `url` (URL or `assets/…` path) | | `code` | Code block | `language` + `text` (REQUIRED) | | `frame` | Container box | optional: a `frame:` reference alone creates one (see 5) | ### Visual properties (per node) Each type supports a different subset. Properties not listed for a type are rejected. | Property | Values | |----------------|--------------------------------------------------------------| | `stroke` | hex (`'#rrggbb'`), `rgb()`, `rgba()`, or `transparent` | | `color` | fill - hex, `rgb()`, `rgba()`, or `transparent` | | `fill` | `hatch` \| `cross-hatch` \| `dots` \| `solid` (used when `color` is transparent) | | `stroke_width` | `0.5` \| `1` \| `2` \| `4` | | `stroke_style` | `solid` \| `dashed` \| `dotted` \| `dash-dot` | | `sloppiness` | `1` \| `2` \| `3` \| `4` (1 = smooth, 4 = rough) | | `edges` | `sharp` \| `slight` \| `round` \| `full` (corner rounding) | | `opacity` | `0`-`100` | | `x` / `y` | numbers - the node's center, in canvas units | Allowed properties by type: - `simple` : stroke, color, fill, stroke_width, stroke_style, sloppiness, edges, opacity - `circle` : stroke, color, fill, stroke_width, stroke_style, sloppiness, opacity (no `edges`) - `decision` : stroke, color, fill, stroke_width, stroke_style, sloppiness, edges, opacity - `text` : stroke, color, fill, stroke_width, stroke_style, sloppiness, edges, opacity - `person` : stroke, color, stroke_width, sloppiness, opacity (stroke cannot be `transparent`; no `fill`/`stroke_style`) - `icon` : icon, opacity (colors are ignored) - `image` : url, width, height, size, color, edges, opacity - `code` : language, text, stroke, color, stroke_width, edges, opacity - `frame` : stroke, color, fill, stroke_width, stroke_style, sloppiness, edges, opacity `name`, `type`, `text`, `opacity`, `frame`, `x` and `y` are always allowed on every type. ### Fixed positions A node with `x` and `y` sits exactly there and the automatic layout leaves it alone, even when the whole diagram is re-laid-out. The pair names the node's **center** in canvas units, so two nodes given the same `y` line up however differently sized they are. Both have to be present: `x` without `y` is an error. ```yaml - name: clients type: icon icon: general/connectivity/smartphone text: Clients x: 140 y: 460 ``` Nodes without the pair are still arranged automatically around the pinned ones. The editor writes `x`/`y` back as you drag those nodes; the coordinates button in the code panel chooses whether the code shows the pinned ones only (the default), every node, or none. ### Type-specific fields **icon** - `icon` is an icon id from the built-in library, formatted as a path (`provider/category/.../name`). The library holds a few thousand, so search for the id instead of guessing one: an id that does not exist renders as an empty icon. ``` GET https://www.gramit.io/docs/ai/icons?q=lambda GET https://www.gramit.io/docs/ai/icons?q=postgres&provider=dev&limit=5 ``` Every term has to match. The reply is JSON: `icons` holds `{ id, provider, category }` objects, most direct match first, and `id` is what the node takes. With no `q` the route lists the providers (`cloud`, `dev`, `apps`, `ai`, `os`, `design`, `general`) and how many icons each one has. Examples: ```yaml - name: fn type: icon text: Lambda icon: cloud/compute/aws/lambda - name: db type: icon text: DynamoDB icon: cloud/databases/aws/dynamodb - name: gw type: icon text: API Gateway icon: cloud/integration/aws/api-gateway ``` **image** - `url` is either a full absolute URL (e.g. `https://example.com/logo.png`) or a compact asset path of the form `assets/` referencing an image in the user's "My Assets" library (e.g. `assets/picture-of-my`). Other relative paths are rejected. Sizing (all optional, positive numbers): - `width` + `height` - exact pixel dimensions. - a single `width` or `height` - scales the other to keep the aspect ratio. - `size` - scale factor on the default size (e.g. `2` = double, `0.5` = half). Ignored when `width`/`height` are set. ```yaml - name: banner type: image text: Logo url: https://gramit.io/logo.png width: 320 height: 180 - name: thumb type: image url: assets/picture-of-my size: 2 ``` **code** - `text` is the code content (not a label). `language` is required. Supported languages: `json`, `javascript`, `typescript`, `python`, `html`, `css`, `markdown`. ```yaml - name: api_call type: code language: typescript text: | const res = await fetch('/api/users'); return res.json(); stroke: '#6366f1' color: '#1e1b4b' ``` **person** ```yaml - name: user type: person text: Alice stroke: '#16a34a' # cannot be 'transparent' color: '#bbf7d0' sloppiness: 1 ``` --- ## 4. Edges Edges connect two nodes by `name`. `from` and `to` are required and must reference existing node names. ```yaml edges: - from: node_a to: node_b text: Submit # optional label type: curved # straight | curved style: dashed # solid | dashed arrow: open # open | closed width: 2 # 1 | 2 | 3 | 4 stroke: '#ef4444' # hex | rgb() | rgba() sloppiness: 2 # 1 | 2 | 3 | 4 ``` | Property | Values | |--------------|---------------------------------| | `from` | node name (required) | | `to` | node name (required) | | `text` | string | | `type` | `straight` \| `curved` | | `style` | `solid` \| `dashed` | | `arrow` | `open` \| `closed` | | `width` | `1` \| `2` \| `3` \| `4` | | `stroke` | hex, `rgb()`, `rgba()` | | `sloppiness` | `1` \| `2` \| `3` \| `4` | All edge properties except `from`/`to` are optional. Edge routing is computed automatically, and so are node positions unless a node pins itself with `x`/`y` (section 3). Leave them out and the diagram lays itself out in `template.direction`, which is `LR` unless the document says otherwise. --- ## 5. Frames (grouping) A `frame` is a container drawn behind the diagram. Any node groups itself into one by naming it with `frame:`, and the frame is drawn around every node that points at it. You never give a frame a size or a position: it is the box its contents need. **The frame does not have to be declared.** A name nothing else uses IS the frame, and doubles as its title: ```yaml root: nodes: - name: api text: API frame: backend # no 'backend' node exists, so this creates the frame - name: worker text: Worker frame: backend - name: client text: Client # outside any frame edges: - from: client to: api - from: api to: worker ``` Declare the frame as a node when you want your own title or any styling on the box: ```yaml root: nodes: - name: backend type: frame text: Backend Services # the title, drawn at the top left of the box stroke: '#3b82f6' - name: api text: API frame: backend - name: worker text: Worker frame: backend ``` Rules: - `frame` names either a `type: frame` node **in the same flow** or a name nothing else uses, which brings that frame into existence titled with the name. - `frame` may NOT name a node that exists and is not a frame. That is an error. - A frame may name another frame, nesting one box inside the other (the inner one has to be declared, since it needs a `frame` of its own). A frame that ends up inside itself is an error. - Edges cannot attach to a frame. Connect the nodes inside it instead. - A frame's outline is dashed unless `stroke_style` says otherwise. - On the canvas, moving a frame moves everything inside it. - A frame nobody names renders as an empty box. --- ## 6. Subflows Any top-level key other than `template` and `root` is a subflow - a nested diagram opened as its own level. Node names remain globally unique across all flows. ```yaml root: nodes: - name: onboarding text: Onboarding edges: [] onboarding: # subflow nodes: - name: welcome text: Welcome Screen - name: setup text: Account Setup edges: - from: welcome to: setup ``` --- ## 7. Colors All color fields accept: ``` #rgb -> '#f00' #rrggbb -> '#ff0000' #rrggbbaa -> '#ff000080' rgb(r, g, b) -> rgb(255, 0, 0) rgba(r, g, b, a)-> rgba(255, 0, 0, 0.5) transparent -> no fill / no stroke ``` Always quote hex colors in YAML (`'#ff0000'`). Unquoted, `#` starts a YAML comment and the value is lost. --- ## 8. Style defaults Omitted visual properties inherit from the active style: | Property | clean (Architect) | handmade | |----------------|-------------------|---------------| | sloppiness | 1 (smooth) | 3 (rough) | | stroke_width | 1 | 1 | | edges | slight | round | | stroke_style | solid | solid | | edge arrows | open, curved | closed, curved| --- ## 9. Validation rules (what the editor enforces) Nodes: - `name` is required, non-empty, unique within its flow and globally. - `type` must be one of: simple, circle, icon, image, person, decision, text, code, frame. - Properties outside a type's allowed set are errors. - `sloppiness` in {1,2,3,4}; `stroke_width` in {0.5,1,2,4}; `opacity` in 0-100. - `color`/`stroke` must be valid color strings. - `code` nodes require `language` and `text`. `image` nodes need a `url` (full URL or `assets/…` path); `width`/`height`/`size` must be positive numbers. - `frame` names a `frame` node in the same flow or a name nothing else uses (which creates the frame); it may not name a node that is not a frame, and no frame may end up inside itself. Edges: - `from` and `to` are required and must reference existing node names, and neither may be a frame. - `width` in {1,2,3,4}; `sloppiness` in {1,2,3,4}. Nodes, continued: - `x` and `y` must be finite numbers and must be given together. Template: - `theme` in {light, dark}; `style` in {handmade, clean}; `direction` in {LR, TB}; `spacing` a positive number. - Unknown keys (in `template` or any flow) are errors. A flow may only contain `nodes` and `edges`. Everything above except the cross-references (an edge naming a node that exists, a frame that contains itself, names unique across the document) is published as JSON Schema at https://www.gramit.io/docs/ai/schema.json, generated from the same table the editor validates against. Parse your YAML and check it against the schema before you hand it over: it catches a misspelled property or an out-of-range value without a round trip through the user. --- ## 10. Complete example ```yaml template: name: User Registration Flow theme: dark style: handmade root: nodes: - name: signup type: frame text: Sign up stroke: '#3b82f6' - name: start text: Start color: '#1e293b' stroke: '#94a3b8' - name: form text: Fill Registration Form color: '#1e3a5f' stroke: '#3b82f6' stroke_width: 2 frame: signup - name: validate type: decision text: Valid? stroke: '#f59e0b' color: transparent fill: hatch frame: signup - name: error_msg text: Show Errors color: '#fee2e2' stroke: '#ef4444' frame: signup - name: done type: circle text: Done color: '#dcfce7' stroke: '#16a34a' - name: new_user type: person text: New User stroke: '#3b82f6' color: '#dbeafe' edges: - from: start to: form type: curved - from: form to: validate text: Submit type: curved - from: validate to: error_msg text: "No" style: dashed stroke: '#ef4444' arrow: open - from: validate to: done text: "Yes" stroke: '#16a34a' width: 2 - from: done to: new_user type: curved stroke: '#16a34a' arrow: closed notifications: nodes: - name: email_sent text: Send Welcome Email color: '#fef9c3' stroke: '#ca8a04' - name: sms text: Send SMS Verification color: '#fef9c3' stroke: '#ca8a04' edges: - from: email_sent to: sms ``` --- ## 11. How to deliver Gramit Code to the user Hand over a link, not a chore. Base64 the YAML document (UTF-8, standard base64; base64url and missing padding are accepted too) and put it in the fragment of: ``` https://gramit.io/new#code= ``` Opening it decodes the document, renders it, and saves it as a new board in that browser's gallery. Base64 is what makes the link survive the trip: raw YAML is newlines, `#`, `:` and `|`, and a chat client will cut the link at the first one of those it dislikes. The fragment never reaches a server, so the board stays between you and the person you sent it to. ```python # building the link import base64 encoded = base64.b64encode(yaml_text.encode("utf-8")).decode("ascii") url = f"https://gramit.io/new#code={encoded}" ``` Then: 1. Give the user the link, and say what it opens. 2. Show the YAML too, in a fenced code block, for anyone who wants to read or edit it. 3. If a link is not usable where you are, fall back to the paste route: open https://gramit.io, click **Menu -> Code to Diagram**, and paste the YAML into the panel. The diagram renders as it is typed.