meshfox
 /\_/\
( ¬‿¬ )──●──●──●
  c c

An executable canvas: a hierarchical, node-based document where nodes hold Markdown, and code blocks inside that Markdown can be run — with their output optionally cached right next to the code.

Status: early bootstrap. This document is itself a valid meshfox canvas — every ## section here is a node, nested under this root. The leading HTML comment above the title is the meshfox:canvas marker (see SPEC.md, or browse this file with meshfox view — "File format" below includes it live); it's what lets tooling recognize this as a canvas despite the plain .md extension.

Screenshot

Concept
  • A project is one canvas: a tree of nodes starting from a single root node.
  • From the root, large section nodes branch off (e.g. one section per feature).
  • From sections, further block nodes branch off. Blocks hold Markdown.
  • Markdown inside a block can contain fenced code. A fence can be marked runnable; running it executes the code and (optionally) writes the result back into the same node, right under the code, so nobody has to re-run it just to see what happened last time.
  • Two ways to interact with the same files:
    • a browser UI (canvas view + block runner) backed by a small Rust server — opens read-only, so pulling up a canvas to look around never one-click-modifies it: running a block is always allowed (you're still explicitly clicking "run", and it's the whole point of a canvas), but a cached block's output isn't written back to the file until an explicit "Edit" button is clicked, which also unlocks dragging, resizing, and saving layout. Output streams into the browser live as the block runs (not just once it's finished), and a running block gets a Kill button, for when one hangs.
    • a CLI that runs blocks non-interactively, make-style, for use in scripts/CI — meshfox list prints every runnable block as a tree, so there's no need to go spelunking through the file to find out what's runnable. meshfox run streams output live too (the same async, killable executor as the browser UI); Ctrl+C kills whichever step is currently running, whole process group and all, and stops there — whatever earlier steps in the chain already completed stays cached.
File format

meshfox canvas format

Reference spec for .canvas.md. Also available at any time via meshfox spec.

A canvas is a Markdown outline: heading nesting is the node tree; bookkeeping (id, position, extra edges) lives in HTML comments, invisible to any normal Markdown viewer. Model comes from JSON Canvas (jsoncanvas.org); this format moves the same tree/graph-of-Markdown-nodes model into plain Markdown instead of JSON, for readable diffs and hand-editability.

File structure

  • # (H1) — exactly one per document. Content up to the first ## is the root node. Always a node, no marker needed (it's the only H1).
  • ##, ###, ... — a heading is a node only if immediately followed by a <!-- meshfox:node ... --> comment. Without one, the heading (and everything under it) is just prose inside its enclosing node — headings can be freely used for sub-structure without fragmenting the canvas. A node's parent is the nearest enclosing shallower node heading (or root) — unless overridden by parent= (below).
  • <!-- meshfox:node ... --> — right after a heading line. Turns the heading into a node and holds its bookkeeping as key="value" attributes: id, type, x, y, w, h, color, tags, parent. All optional; a bare <!-- meshfox:node --> is enough. id defaults to a slug of the heading text; only write it explicitly for a stable handle that survives renames (e.g. because an edge references it). First write-back (running a cached block, saving layout) pins the id used, so identity is stable afterward. tags="a,b,c" is a comma-separated list of free-form labels — purely descriptive (no structural meaning), shown as small chips on the node in the web UI. parent="other-id" overrides the heading-nesting-implied parent — needed once a subtree is already ###### (H6, CommonMark's ceiling: headings can't nest any deeper), where a further child has nowhere left to go but another ###### heading, which plain heading nesting alone would read as a sibling rather than a child. Nesting past H6 keeps working — every deeper node just keeps writing ###### and disambiguates with parent= instead of heading depth. Written automatically ("add child" in the UI reaches for it exactly when it's needed); hand-editing it is only for restructuring an already-flattened deep subtree.
  • <!-- meshfox:edge from="other-id" ... --> — one per line, right after a node's meshfox:node line. Declares an extra incoming edge from another node, for graphs that aren't a clean nesting tree. Any number allowed, in addition to the one implicit nesting-parent edge. Besides from, an edge line accepts the same kind of optional styling attributes the web UI's on-canvas edge editor writes: label (arrow text), color (hex or a "1""6" preset, same palette as a node's own color), style ("solid", "dashed" — the default look when omitted — or "dotted"), arrowStart/arrowEnd ("none" or "arrow" — an edge with neither set gets an arrowhead only at arrowEnd, matching the pre-styling default), and tags (comma-separated, same convention as a node's own). All are omitted from the line unless explicitly set — a plain from="other-id" with nothing else is exactly the old, pre-styling form. These extra (meshfox:edge) edges render as a curved connector in the web UI, distinct from the structural nesting tree's right-angle routing.
  • <!-- meshfox:canvas --> — optional, first line of the file. Marks a plain *.md file as a canvas even though it isn't named *.canvas.md (used so this doubles as auto-discovery hint; not required for parsing — any correctly-structured file parses as a canvas regardless of name).

Node types

type= on meshfox:node picks one of JSON Canvas's four kinds. Defaults to (and is omitted for) text.

  • text (default) — freeform Markdown body.
  • group — purely organizational; body must be empty. Children are whatever nests under it structurally (no separate containment mechanism).
  • file / link — body must be exactly one Markdown link and nothing else: [label](target). A file node also accepts two optional display attributes on its meshfox:node line:
    • display="link" (default) or display="code"code shows the target's own file content as a read-only, non-runnable syntax-highlighted preview instead of a plain clickable link. The file is read fresh from disk on every view, confined to the canvas's own directory tree (same boundary include targets are resolved within) — never written back.
    • lang="..." — syntax-highlighting language hint for display="code" (e.g. lang="rust"). Optional; when omitted, the language is guessed from the target's file extension. Ignored when display isn't code.
    <!-- meshfox:node type="file" display="code" lang="rust" -->

    [main](./src/main.rs)

link nodes don't support display/code — their target is an external URL, not something meshfox reads from disk.

  • include — same one-link body as file/link, but the target (another .md or .canvas.md file) is spliced in dynamically by whatever consumer resolves includes — never written back to disk. run/fmt/validate see the bare link, same as file/link. See "Includes" below.
  • constraint — body must be exactly one ```starlark fence and nothing else — a sandboxed contract over the document tree, evaluated by meshfox check. See "Constraint nodes" below.

Any other type= value, a non-empty group body, a file/link/include body that isn't a single link, or a constraint body that isn't a single ```starlark fence is a parse error (meshfox validate catches these, plus a missing/cyclic/unparseable include target).

Includes

type="include" dynamically splices another file's content into this node, resolved fresh every time a consumer asks for it (e.g. the server, before serving GET /api/canvas to meshfox view) — nothing is ever written into the including file. run/fmt/validate operate on a single file's raw text and never resolve includes; only validate reaches far enough in to catch a broken target, a parse error in it, or a cycle.

The target is told apart as a canvas or plain Markdown the same way auto-discovery already does: .canvas.md suffix, or a plain .md file that opens with the meshfox:canvas marker.

  • canvas target — parsed and spliced in as real children. Every spliced node's id is namespaced {include_id}/{original_id} to avoid collisions with the including document (and with any other include spliced in alongside it), and every level is shifted down by the include node's own level. The include node itself becomes a group.
  • plain Markdown target — has no meshfox structure of its own, so it becomes the include node's own body verbatim, except every heading in it is shifted down (clamped to H6, CommonMark's ceiling) by the include node's own level — so e.g. the target's top-level # doesn't read as a second document root once nested. The include node becomes text.

An included file can itself declare includes; those are resolved too, with a cycle (A includes B includes A) reported as an error rather than recursing forever.

Constraint nodes

A type="constraint" node's body is a sandboxed Starlark contract over the document tree — a way to assert invariants a canvas should hold (e.g. "every node tagged table has exactly one file child") as part of the document itself, checked by meshfox check rather than enforced only by convention. There's no separate "document" object: doc is simply the root node, and every node — doc included — exposes the same navigation methods, so a constraint scopes a check to its own subtree with self.descendants() the exact same way it would reach the whole document via doc.descendants(). A constraint typically governs the subtree it's placed in, so a natural place for one is as the parent of whatever it's checking, e.g. a constraint node sitting above a group of table-tagged nodes:

#### Table shape
<!-- meshfox:node type="constraint" -->

##### Users
<!-- meshfox:node tags="table" -->
...
    for n in self.descendants():
        if "table" in n.tags:
            files = [c for c in n.children() if c.type == "file"]
            if len(files) != 1:
                fail(n.id + ": expected exactly one file child, got " + str(len(files)))

The script sees:

  • doc — the document's root node.
  • self — the constraint's own node, so a script can find its place in the tree without hardcoding its own id.
  • On every node (both of the above, and any node reached through them):
    • .id, .title, .type (a string, e.g. "file"), .parent (a string, or None for the root), .tags (a list of strings) — plain read-only fields.
    • .children() — its direct structural children (same tree Canvas::children walks — not extra meshfox:edge parents).
    • .descendants() — everything in its subtree (children, their children, ...), not just direct children — the usual way to scope a check to "this constraint's own subtree" (self.descendants()) instead of the whole document (doc.descendants()).
    • .node(id) — the node with that id anywhere in the document, or None.
    • .nodes_with_tag(tag) — every node in the whole document whose tags includes tag, regardless of where it's called from. Prefer self.descendants() filtered by tag when a constraint should only govern its own subtree; reach for this only when a rule is genuinely document-wide.
  • fail(msg) — records a violation without stopping the script, so one constraint can report every offending node in a single run instead of just the first. A script that never calls fail passes.

Beyond these, and Starlark's own built-ins (len, range, string methods, list/dict comprehensions, ...), the sandbox has nothing: no file I/O, no network, no way to see any other node's fully-resolved include tree, and no way to mutate the document — a constraint only ever reads and reports. Evaluation is resource-bounded (instruction count, call depth, heap size); a script that times out or errors (syntax error, unbound name, ...) counts as a failing constraint, with that error as its one message. Each constraint node gets its own fresh sandbox — nothing persists between them, and nothing carries over between one meshfox check run and the next.

meshfox check runs every constraint node and reports pass/fail per node, exiting non-zero if any fails (or if the file doesn't parse — validate's job, which check implies). Distinct from meshfox validate: validate asks whether the file parses as a well-formed canvas; check asks whether the document, once parsed, actually satisfies whatever rules its own constraint nodes declare.

Runnable code fences

Lives inside a node's Markdown text, as fence-info-string attributes:

    cargo build --workspace
  • name — identifies a fence for meshfox run/output caching; must be unique within its node. Normally required to make a fence runnable — except a node may have one fence with no name at all, which is runnable too, implicitly named after its own node id. A second unnamed fence in the same node makes the omission ambiguous, so neither gets a name (same as any unnamed fence today — not an error, just not runnable). This is what lets meshfox run/list skip a redundant trailing block-name argument that would just repeat the node's own id — see "CLI" below — and applies the same way to a fence whose explicit name happens to already match its node's id.
  • cache — optional flag (cache or cache=true); opts into persisting output back into the file.
  • default — optional flag (default or default=true); marks this fence as its node's default block — the one meshfox run <path-to-node> addresses without a trailing block name (see "CLI" below). A fence whose (implicit or explicit) name already equals its own node's id counts as default too, without needing this flag. At most one block per node may be default (explicitly, implicitly, or one of each) — meshfox validate reports a conflict as an error.
  • deps — optional, comma-separated list of other blocks this one runs after. Each entry is either a bare block name (a block in the same node) or node-id/block-name (a block in another node, addressed by node id the same way meshfox:edge from= addresses nodes). Running a block always runs its full dependency chain first, automatically — each dependency's own deps are resolved transitively, in order, with no block run twice even if several blocks in the chain depend on it. A cycle, or a deps entry naming a block that doesn't exist, is a meshfox validate error.
  • env — optional, comma-separated list of declared meshfox:vars (see "Variables" below) this block wants in its own process environment. Each entry is a bare name (pass the declared variable through under the same name) or local=name (expose it under a different name in this block's own environment) — a leading $ on the variable-name side is accepted and stripped, but purely cosmetic: env="$X,LOCAL=$Y" and env="X,LOCAL=Y" mean exactly the same thing. A block with no env= never resolves or prompts for any declared variable, however many the document declares as a whole — this is what scopes "does running this block need to ask about anything" to the block itself, not the whole canvas. An env= entry naming a variable nothing declares is a meshfox validate error.
  • tty — optional flag (tty or tty=true); this block wants a real interactive terminal instead of the usual captured/streamed output — e.g. bash on its own (a login-style shell), or anything else that reads from its own stdin expecting a real terminal (ssh, git commit's editor, a REPL, a curses UI). Mutually exclusive with cache (meshfox validate error) — an interactive session isn't the deterministic "exit code plus text" cache saves/replays. A tty block may only be a deps= target of another tty block (meshfox validate error otherwise) — a non-interactive chain auto-running one as a dependency would mean an unrequested interactive step ambushing it; two chained tty blocks just hand the terminal over twice, back to back, in dependency order (each still running its own deps= first, same as any other block). See "Interactive (tty) blocks" below for how the CLI and web UI actually run one.

Supported languages: bash (sh is an alias for it). A fence in any other language never counts as runnable at all — not with an explicit name=, and not as a node's sole unnamed fence — so an ordinary Markdown document's own example fences (a yaml config sample, a json snippet, ...) never get mistaken for something to run just because meshfox was pointed at the file directly (the meshfox:canvas marker/.canvas.md suffix is only required for auto-discovery — see "CLI" below — an explicitly-given path still parses whatever heading structure it finds).

    cargo build --workspace
    cargo test --workspace

Running test here always runs build first.

Interactive (tty) blocks

A tty block (see its flag above) hands its process the real terminal instead of the captured/streamed output every other block gets — CLI and web UI each do this differently, since only one of them actually has a terminal to hand over.

  • CLI — before running a tty step, meshfox run checks that both stdin and stdout are an interactive terminal; if either isn't (piped, redirected, CI), it errors out rather than hanging or silently running non-interactively. When they are, the block's process is connected directly to the real terminal (not captured line-by-line the way every other block's output is) — a script can read from the user, run vim, prompt for a password, whatever a normal interactive shell command could do. Earlier steps in the same deps= chain still run captured/streamed exactly as usual; only the tty step(s) themselves take over the terminal, handing it back once each one exits.
  • Web UImeshfox view gives the block a real pseudo-terminal (not just piped stdout/stderr, which can't do cursor movement, raw input mode, or terminal-size queries) and streams it to an in-browser terminal, keys typed there going back to the process's stdin. Clicking run on a tty block opens this as a floating panel over the canvas (draggable/collapsible, like the node text editor), rather than filling in the node's own inline output area the way a normal run does.
  • Either way, output from a tty block is never written back into the file — that's what the cache conflict (above) rules out.
  • A real terminal (CLI's own, or the web UI's pseudo-terminal) is a genuine tty, so the "commands run without a pseudo-terminal" note under "Cached output" below doesn't apply to tty blocks — a tool that auto-detects color support (cargo, git, ...) sees a real terminal and colors its output without needing --color=always.

Variables

Document-scoped configuration values a canvas wants from whoever runs it — an install prefix, a log level, an API token — asked for once and then remembered, the same idea as CMake's cached variables or a ./configure step. Declared as <!-- meshfox:var ... --> comments, only inside the root node's own body — a meshfox:var found in any other node is a meshfox validate error, not silently ignored, since a variable is always document-wide, never per-node:

<!-- meshfox:var name="INSTALL_PATH" prompt="Install prefix?" default="/usr/local/bin" -->
<!-- meshfox:var name="LOG_LEVEL" type="select" choices="debug,info,warn,error" default="info" -->
<!-- meshfox:var name="API_TOKEN" secret -->

Attributes:

  • name — required. What a runnable fence's own env= (see "Runnable code fences" above) refers to when it wants this variable — declaring one here doesn't, by itself, put it in any block's environment; that's opt-in per block, see "Consumption" below.
  • typestring (default), int, bool, or select. Purely a hint for how to prompt (a bool prompts y/n, a select shows its choices as a menu, ...) and how a UI renders an input for it — an incoming value (from --set, the environment, the cache, or a typed answer) is never validated against it; it's just a string either way.
  • prompt — question text to show when asking for a value; defaults to name itself.
  • default — used if nothing else resolves the variable (see below).
  • choices — comma-separated; required when type="select".
  • secret — flag (secret or secret=true). A secret variable is never read from or written to the on-disk cache (see below) and never pre-filled anywhere — the only way to supply one without an interactive prompt is --set/the process environment. It's asked for fresh every single time it's needed.
Consumption (env=)

A meshfox:var declaration on its own does nothing to any block — it's only a name a fence's own env= attribute can reference (see "Runnable code fences" above). Only variables a block actually lists in its own env= are ever resolved or prompted for on its behalf, and only those end up in its process environment (under whatever local name it asked for): running a block that declares no env= at all never touches meshfox:var resolution, however many variables the document as a whole declares. This is what keeps, say, meshfox run README.md some-unrelated-block from ever being asked about an INSTALL_PATH that only some other block in the document actually uses.

meshfox run <path...> <a,b,c> resolves each requested block's (and its deps= chain's) own env= independently, right before that specific block runs — not the whole document's variables up front. If the same variable is referenced by more than one block in a single invocation (directly or via deps=), it's only ever resolved/prompted for once: the first block to need it answers the prompt, which is immediately cached, so every later block referencing the same variable in that same invocation just reads the cached answer.

Resolution

Resolving one declared variable (for whichever block's env= asked for it) tries, in order: an explicit override (meshfox run --set NAME=value, or a value submitted through the web UI's form) → the process environment → the on-disk cache (skipped entirely for secret) → the declaration's own default. Whatever isn't resolved by any of those needs an interactive answer — a terminal prompt for the CLI, a form for the web UI — which, for a non-secret variable, is then written to the cache so a later run of any block referencing the same variable doesn't ask again.

The cache lives at <dir>/.meshfox/<filename>.env, next to the canvas file itself (e.g. examples/hello.canvas.md -> examples/.meshfox/hello.canvas.md.env) — a plain NAME=value-per-line file, meant to be .gitignored, the same way CMakeCache.txt usually is. It's safe to hand-edit or delete: deleting it just means every non-secret variable gets asked about again next time some block's env= needs it.

CLI
  • meshfox configure [canvas] — the one place that does walk every declared non-secret variable in the whole document, regardless of which (if any) block currently references it — showing its currently-resolved value (cache / env / default) as the prompt's own default, and writing whatever you answer (even if unchanged) back to the cache. The explicit "set these up now" step, the same role ccmake/cmake -L plays for a CMake cache; nothing else in meshfox requires running it. Requires a terminal; refuses to run (rather than silently do nothing or apply bare defaults) if stdin isn't one. Secret variables are never shown here — asking for one that's never cached and immediately discarded again wouldn't do anything useful.
  • meshfox run resolves each executed block's own env= lazily: only a variable that block actually references, and that's still unresolved after the above precedence, gets an interactive prompt, right before that block runs — no separate configure step required, and no prompt at all for a block whose env= is empty or already fully resolved. --set NAME=value (repeatable) supplies overrides on the command line, the non-interactive equivalent of answering a prompt — the same flag CI would use in place of a TTY, which run otherwise requires whenever some referenced variable is still missing after --set/env/cache/default. A --set value is saved to the cache regardless of whether anything in the current invocation actually references it, same as cmake -D always updating CMakeCache.txt.

Cached output

Running a cached block writes/updates a fenced block immediately after the source, wrapped in markers so re-runs replace just that region:

    cargo build --workspace
<!-- meshfox:output name="build" -->
    exit code: 0
    ...
<!-- /meshfox:output -->

Output (live or cached) that contains ANSI SGR color/style escape codes renders in color in the web UI, both while a block is still streaming and for a previously-cached block's saved output. Commands run without a pseudo-terminal, so a tool that auto-detects "not a real terminal" and disables its own color output (cargo, git, plain ls) prints plain text here unless it's told to force color (--color=always, or a script emitting raw escape codes itself).

Minimal example

<!-- meshfox:canvas -->
# Hello Project
<!-- meshfox:node id="root" -->

Project root.

## Tests
<!-- meshfox:node id="tests" type="group" -->

### Smoke Test
<!-- meshfox:node id="smoke-test" -->

```bash name="smoke" cache
echo "hello from meshfox"
```

Addressed as: meshfox run tests smoke-test smoke (node-id path from root, then the block name).

CLI

  • meshfox run [--no-deps] <path...> <names> — run one or more comma-separated named blocks reached by walking node ids from the root. Each named block's deps chain runs first, automatically, in dependency order; a dependency shared by several requested blocks only runs once. --no-deps skips this and runs only the named blocks themselves, in the order given — the CLI equivalent of the web UI's plain "run" button next to "⛓ run chain". If a node has a default block (see "Runnable code fences" above — one explicitly flagged default, or one whose name already matches the node's own id), the trailing name can be dropped: meshfox run tests smoke-test addresses that node's default block directly, instead of meshfox run tests smoke-test <block-name>, tried first as an ordinary address and only falling back to this when that doesn't resolve, so it never changes the meaning of an address that already worked. Output prints line by line as the block produces it, not all at once after it exits — the exit code, printed last, genuinely isn't known any sooner. Ctrl+C kills whichever step is currently running (the whole process group it spawned, not just bash) and stops there; whatever earlier steps in the chain already completed stays cached on disk.
  • meshfox list — print every runnable block as an indented tree, each with its [cache]/[default]/[tty]/[deps: ...]/[env: ...] flags and a ready-to-paste meshfox run <path...> <name> — so you don't have to go spelunking through the file to find out what's runnable. A node whose only block is its default gets a single merged tree line instead of a separate one for the node and another for the block; a node with a default block and other blocks besides gets the node-id shortcut printed on its own header line instead, alongside its other blocks' own lines.
  • meshfox fmt [--force] — fill in missing x/y/w/h via tree-aware auto-layout (--force recomputes all; group nodes are always skipped, their box is derived, never stored).
  • meshfox view [--port] [--no-open] — local web UI, read-only until "Edit" is clicked in the browser. A run's output streams into the browser live, line by line, as it happens, rather than appearing all at once when the block finishes; a running block gets a Kill button, for when one hangs. A tty block instead opens a real interactive terminal panel — see "Interactive (tty) blocks" above.
  • meshfox validate — parse-only validation (single root, no duplicate ids, no dangling edges, type body rules, no dangling/cyclic deps=); no execution, no writes. Exit non-zero on error — usable in CI/pre-commit.
  • meshfox check — run every constraint node's Starlark contract (see "Constraint nodes" above) and report pass/fail per node. Exit non-zero if the file fails to parse or any constraint fails — usable in CI/pre-commit alongside (or instead of) validate.
  • meshfox spec — print this specification.

list/fmt/view/validate/check take the canvas path as an optional positional argument; run takes it as an optional leading argument (recognized by its .md suffix, since node ids never have one). Omit it and any of them auto-discover the single *.canvas.md (or marked *.md) file in the current directory.

Auto-layout

Nobody has to type x/y/w/h by hand. There are two independent auto-layout engines now — one on demand in meshfox fmt, one live in the web UI for whatever's still unpositioned — deliberately not required to agree pixel-for-pixel; each leans on inputs the other doesn't have.

  • On demand: meshfox fmt runs crates/core/src/layout.rs's tree-aware heuristic and actually writes the result into the file (via the usual surgical set_node_meta patch, one node at a time). The root and its direct children ("sections") read top-to-bottom with just a small nudge to the right, same as a document's title followed by its headings — not yet a real indent. From there down it's a classic indented tree view, the same shape as a file tree or a collapsed outline: each section's own content steps fully to the right of its parent, siblings at a given depth stack vertically, and further nesting keeps stepping right from there. Box size is estimated from each node's content (line count, whether it has a code fence, whether it needs a run-button row) rather than being a flat constant, so a stub and a node with a paragraph plus cached output don't come out the same size. It's a simple heuristic, not a publication-quality tree drawer — adversarial trees might still look rough:
  meshfox fmt              # fills in position/size only for nodes missing one
  meshfox fmt --force      # recomputes and overwrites every non-group node

group nodes are always skipped — their box is derived, never stored, whether or not --force is given. Without --force, a node keeps whatever it already has; fmt only fills gaps, so hand-placed (or already-formatted) nodes aren't disturbed by running it again — the second worked example above, for instance, is already fully positioned, so meshfox fmt on it is a no-op.

  • Live in the browser: GET /api/canvas sends exactly what's in the file — no computed suggestion, no suggestedX/etc. over the wire. web/src/autolayout.ts fills in a box client-side for anything still missing a real position, using the same overall tree shape as layout.rs (sections top-to-bottom, deeper nesting branching right, siblings stacked without overlap, a group's box as the bounding box of its resolved members) but with real browser-only inputs instead of a text-length heuristic: root and its direct children share one width, 60% of the viewport; everything deeper gets 40%, uniformly regardless of how much deeper. Viewport width is read once, when the canvas loads — it doesn't recompute on window resize. Height is never estimated: it comes from React Flow's own measurement of each node's actual rendered content, live — the layout self-corrects as soon as a real measurement lands (and again later, e.g. as a running block's output grows). A depth-≥2 node additionally gets a max-height cap so one long block can't drag a whole subtree far from its parent; past the cap it scrolls internally (.mesh-node-body's existing overflow: auto) instead of growing the box further. None of this is ever written to the file just from loading it — same "don't fight the user's own drag" rule as before (see touchedNodeIds in App.tsx), a node only gets its box persisted once it's actually been dragged/resized, or meshfox fmt gives it a real one.

    Edit mode's toolbar has an Auto-layout button that clears every non-group node's stored x/y/w/h in the file outright (POST /api/canvas/clear-layout), reverting the whole document to auto-placed — behind a confirmation dialog, since it can't be undone from the UI. Useful for backing out of a bunch of hand-placed positions and letting the client lay everything out fresh.

Variables

Document-scoped config values a canvas wants from whoever runs it — declared once as <!-- meshfox:var ... --> comments in the root node (this document declares exactly one, INSTALL_PATH, right above "Concept" — invisible here since it's an HTML comment, same as every other bit of meshfox bookkeeping). Declaring one doesn't put it in any block's environment by itself, though — a block has to opt in with its own env= fence attribute to actually reference it (env="$INSTALL_PATH", see "Install" below), and only blocks that do ever resolve or prompt for anything: running any other block in this file never asks about INSTALL_PATH, however many blocks elsewhere might use it. Asked for interactively the first time some block's env= actually needs it, then remembered in a local .meshfox/<filename>.env cache (.gitignored, analogous to CMake's CMakeCache.txt) so it's not asked for again by any block referencing it afterward. meshfox configure walks every declared variable up front regardless of env= usage; meshfox run --set NAME=value supplies one non-interactively (e.g. for CI); the web UI shows a small form in place of a prompt, scoped to just the clicked block's own chain, only for whatever isn't already resolved. See SPEC.md's "Variables" for the full writeup, including secret (never cached, always re-asked) and the int/bool/select types.

Usage

A few commands, run for real against the installed meshfox binary (see "Development" below for building/installing it). The output below is cached from actually invoking it (see "Cached output" above) rather than typed by hand, so it can't quietly drift from what the CLI does — and it doubles as an end-to-end check of the runnable-block feature itself: this section's own blocks are ordinary name=/cache fences, executed the same way any project's would be.

CLI help

Run meshfox spec to print the full format specification (SPEC.md, embedded in the binary at compile time) — not cached here since dumping it verbatim into this section would nest the whole .canvas.md grammar inside an example of the format it's describing.

meshfox -h
exit code: 0

CLI and local web viewer/editor for a meshfox canvas. Run `meshfox spec` for the full .canvas.md format specification.

Usage: meshfox [OPTIONS] [COMMAND]

Commands:
  run        Run one or more named code blocks
  configure  Interactively resolve every declared `meshfox:var` (see SPEC.md's "Variables") and save the answers to the on-disk cache (`.meshfox/<filename>.env`, next to the canvas file) so `run` doesn't have to ask again. Shows each variable's currently-resolved value as the prompt's own default — press Enter to keep it. Secret variables are never cached, so there's nothing for this to save for them; they're skipped here and asked for fresh at run time instead. Requires an interactive terminal
  fmt        Fill in x/y/w/h for nodes that don't have them yet, using a simple tree-aware auto-layout (see `meshfox_core::layout`). Never touches `group` nodes — their box is always derived from their children, never stored — and by default leaves any node's position/size alone once it has one, so hand-placed nodes survive a format
  create     Create a new, empty canvas file: just the `meshfox:canvas` marker followed by a lone root heading (`#`) named after the file itself (its name with a trailing `.canvas.md`/`.md` stripped). Fails if the file already exists — this never overwrites
  view       Start the local web UI: canvas view, run buttons. Opens read-only — running a block is always allowed, but click "Edit" in the browser to unlock dragging, resizing, saving layout, and persisting a `cache`d block's output back into the file
  validate   Validate that a file parses as a meshfox canvas — same checks `run`/`fmt`/`view` already do before touching anything (single root, no duplicate ids, no dangling `meshfox:edge` targets, `group`/ `file`/`link` body rules) — without executing anything or writing the file back. Exits non-zero on a parse error, so it's usable as a pre-commit/CI check
  check      Run every `constraint`-type node's Starlark contract against the document (see `crate::constraint`/SPEC.md's "Constraint nodes") and report which passed. Distinct from `validate`: `validate` checks that the file *parses* as a well-formed canvas; `check` asks whether the document as a whole satisfies whatever rules its own constraint nodes declare (e.g. "every node tagged `table` has exactly one `file` child") — implies `validate` first, since an unparseable file has no constraints to run. Exits non-zero if the file fails to parse or any constraint fails, so it's usable as a pre-commit/CI check alongside (or instead of) `validate`
  list       Print every runnable code block in the canvas as an indented tree, each with a ready-to-paste `meshfox run <path...> <name>` — so you don't have to go spelunking through the file to find out what's runnable. Same raw-file-only scope as `run`/`fmt`/`validate` (no include resolution)
  node       Structural edits to individual nodes in a canvas file: add, move, rename, delete, or set a node's body/position/style/edges — the CLI counterpart to the web UI's Edit-mode node operations (the same `mdcanvas` surgical patches `meshfox view`'s `/api/nodes*` routes use), for scripting/CI or whenever a hand-rewrite would risk getting heading depth, sibling order, or dangling-edge cleanup wrong. Every subcommand validates the fully-patched document still parses before writing it back, same as every other mutating command here
  spec       Print the full .canvas.md format specification (SPEC.md, embedded in this binary at compile time) — the canonical reference for the format, available offline wherever `meshfox` is installed
  help       Print this message or the help of the given subcommand(s)

Options:
      --agent-help  Print usage guidance for AI coding agents (when to prefer `node` subcommands over hand-editing, non-interactive `run`, etc.) and exit
  -h, --help        Print help
  -V, --version     Print version

Agent Usage:
  If you are an AI coding agent, run `meshfox --agent-help` before hand-editing a
  .canvas.md file. It covers when to prefer `meshfox node <verb>` over a raw text
  edit, how to run non-interactively, and other guidance not covered above.
Node commands

meshfox node <op> exposes the same per-node surgical patches (insert_child_node, delete_node, reparent_node, set_node_title, set_node_body, set_node_meta, set_node_edges, reorder_by_position — all in mdcanvas) that back the web UI's Edit-mode operations over its /api/nodes* routes — so a structural change (adding a child at the right heading depth, moving a subtree without breaking its nesting, deleting a node without leaving a dangling meshfox:edge behind) can be scripted or run in CI without going through the browser, and without hand-rewriting Markdown heading levels yourself. Every subcommand takes an optional --canvas <path> (auto-discovered like fmt/validate/list when omitted) and validates the whole patched document still parses before writing it back — the same validate-before-commit shape every mutating server handler already uses. As with any other write in this file, running meshfox validate afterwards is still worth doing: parsing is validated here, but a deletion or a rename can still leave a deps=/env= reference elsewhere dangling, which is validate's job to catch, not node's.

  • add <parent-id> <title> — insert an empty child node, last in the parent's subtree; prints the new (slugged) id
  • rm <node-id> [--keep-children] — delete a node and its subtree, or (with the flag) just the node, promoting its direct children to its former parent
  • mv <node-id> <new-parent-id> — move a node under a new structural parent in one step (the web UI needs two: link, then promote)
  • rename <node-id> <title> — change heading text only; id, heading level, and body untouched
  • body <node-id> [--file <path>] — replace a node's whole body, from a file or stdin
  • meta <node-id> [--x --y --w --h --color --type --display --lang] — set position/size/style; an omitted flag keeps the node's current value; group positions are rejected (their box is always derived, never stored, same as fmt)
  • edges <node-id> [--from <id>]... [--clear] — replace a node's extra (meshfox:edge) parents
  • reorder — resync sibling heading order in the file to match current x/y, the same resync the server runs on every UI save
  • show <node-id> — print a node's parent/children/extra-parents/type/position (read-only)
meshfox node -h
exit code: 0

Structural edits to individual nodes in a canvas file: add, move, rename, delete, or set a node's body/position/style/edges — the CLI counterpart to the web UI's Edit-mode node operations (the same `mdcanvas` surgical patches `meshfox view`'s `/api/nodes*` routes use), for scripting/CI or whenever a hand-rewrite would risk getting heading depth, sibling order, or dangling-edge cleanup wrong. Every subcommand validates the fully-patched document still parses before writing it back, same as every other mutating command here

Usage: meshfox node <COMMAND>

Commands:
  add      Add a new, empty-bodied child node under `parent-id`, as the last item in its existing subtree (`mdcanvas::insert_child_node`) — same as the web UI's "add child" button. No position is set, so it stays unpositioned — auto-placed by whatever's viewing it (the web UI's own client-side layout, or `meshfox fmt`) — until something gives it a real one. Prints the new node's id: a slug of `title`, de-duplicated against every id already in the file
  rm       Delete a node. By default the whole subtree goes with it (`mdcanvas::delete_node`), and any `meshfox:edge from="..."` elsewhere that pointed into the deleted subtree is dropped too, so the file can't be left with a dangling reference. `--keep-children` instead deletes just this node, promoting its direct children (and everything under them, untouched otherwise) to its own former parent (`mdcanvas::delete_node_reparent_children`). Refuses to delete the root either way
  mv       Move a node to a new structural parent (`mdcanvas::reparent_node`). That core function only ever promotes an *existing* extra-parent edge to structural parent — the web UI's two-step dance (drag a new edge onto the node, then promote it) — so this adds the `meshfox:edge from="new-parent-id"` line itself first, making the move a single atomic step from the CLI. Refuses to move the root, or to move a node into itself or one of its own descendants (would make the tree cyclic)
  rename   Rename a node's heading text, leaving its id, heading level, and body untouched (`mdcanvas::set_node_title`) — a node's id is pinned the first time it's written and never follows later title edits
  body     Replace a node's whole Markdown body (`mdcanvas::set_node_body`) — what the web UI's in-node editor would send, if it had one yet (see README's roadmap; for now the UI can reposition and run, not edit text). For a `file`/`link` node the body is its one Markdown link (`[title](target)`); a `group` node's body must stay empty. Reads the new body from `--file`, or from stdin if `--file` is omitted
  meta     Set a node's position/size/style fields (`mdcanvas::set_node_meta`) — `--x`/`--y`/`--w`/`--h` for a manual position/size override (`meshfox fmt` is the usual way to fill these in), `--color`/ `--type`/`--display`/`--lang` for style/type. Any field left unset keeps its current value. `group` nodes never store a position (`fmt` skips them too, deriving their box from their children instead), so `--x`/`--y`/`--w`/`--h` are rejected for one
  edges    Replace a node's whole set of extra incoming edges (`meshfox:edge from="..."` lines, `mdcanvas::set_node_edges`) — the non-structural, non-nesting cross-references JSON Canvas-style graphs use. The given `--from` list (repeatable) *replaces* whatever was already there, it doesn't add to it; `--clear` removes them all
  reorder  Reorder every parent's direct children in the file to match their canvas layout (`mdcanvas::reorder_by_position`, sorted by `y` then `x` among ties) — the same resync the server runs on every save from the web UI, exposed standalone for whenever positions changed by hand (or via `node meta`/`fmt`) and the on-disk heading order should catch up to match what's actually drawn
  show     Print one node's parent, children, extra parents, type, and position/style fields — a read-only lookup, since eyeballing the tree shape directly from the file gets harder the deeper it nests
  help     Print this message or the help of the given subcommand(s)

Options:
  -h, --help  Print help

add then show on a scratch copy, so this doesn't touch the tracked example file:

cp examples/hello.canvas.md /tmp/meshfox-node-demo.canvas.md
meshfox node add --canvas /tmp/meshfox-node-demo.canvas.md tests "Regression Test"
meshfox node show --canvas /tmp/meshfox-node-demo.canvas.md regression-test
rm -f /tmp/meshfox-node-demo.canvas.md
exit code: 0

meshfox node add: added "regression-test" under "tests" in /tmp/meshfox-node-demo.canvas.md
id: regression-test
title: Regression Test
type: text
parent: tests
children: (none)
extra parents: (none)
position: x=? y=? w=? h=?
Running a block

Runs the smoke block in examples/hello.canvas.md for real, updating its cached output in place — the same surgical patch described above, not a simulation:

meshfox run examples/hello.canvas.md tests smoke-test smoke
exit code: 0

==> smoke
hello from meshfox
Sun Jul 26 15:59:41 +04 2026
(exit 0)
Formatting a copy

fmt on a scratch copy, so this doesn't touch the tracked example file:

cp examples/hello.canvas.md /tmp/meshfox-fmt-demo.canvas.md
meshfox fmt /tmp/meshfox-fmt-demo.canvas.md
rm -f /tmp/meshfox-fmt-demo.canvas.md
exit code: 0

meshfox fmt: placed 0 node(s) in /tmp/meshfox-fmt-demo.canvas.md
Interactive (`tty`) blocks

A block flagged tty hands its process a real interactive terminal instead of the captured/streamed output every other block gets — for anything that actually needs to talk to a terminal: an editor, a REPL, ssh. See SPEC.md's "Interactive (tty) blocks" for the full writeup, including why it's mutually exclusive with cache (nothing here to freeze into an output block, so — unlike every other example in this section — there's no cached output shown below).

vim

Run it from a real terminal — meshfox run README.md usage usage-tty vim-demo — and it drops you straight into vim editing that scratch file, same as running vim directly would; :wq (or :q!) hands the terminal back same as it always does. meshfox run checks stdin/stdout are actually a terminal before starting a tty block and errors out otherwise, rather than hanging a script or CI job that happens to reach one. The web UI runs the same block over a real pseudo-terminal instead: clicking "run vim-demo" in meshfox view opens it as a floating terminal panel over the canvas rather than filling in the node's own inline output area.

Static export (experimental)

Renders a canvas's node graph — boxes, tags, cached output, and every structural/meshfox:edge connection — as a plain static HTML/CSS/SVG site (no JS, no live server), through a user-supplied Tera template: every *.tera file in --template is rendered with the canvas's data (context key site) and written to --out at the same relative path minus .tera; everything else in the template directory is copied verbatim (CSS, fonts, images, ...). site-template/ in this repo is a real, working template (used for the example below) that also happens to be a decent way to publish a canvas's README as a project page.

A template's own settings live in an optional template.toml right in its own directory (see site-template/template.toml) rather than as static command-line flags — they're a property of that template, not something to repeat on every invocation: base_url, prefixed onto a relative link/target the command doesn't already copy into --out; and icons, a list of <link rel="..." href="..."> tags (exposed to every template as the icons context key) for the page's own favicon/apple-touch-icon set. template.toml itself is read, never rendered or copied into --out. A template with none gets an empty config — no base_url prefixing, no icon tags — same as before this file existed.

meshfox static -h
exit code: 0

Experimental: export a canvas as a static site. Resolves includes (same as `validate`/`view`), turns the canvas's node tree into a recursive `SiteData` (context key `site`) and hands it to a user-supplied Tera template. A node with no real, authored `x`/`y`/`width`/`height` gets no computed position at all — the template renders it as an ordinary nested HTML element and the *browser* lays it out and sizes it from its real content (no pre-computed/estimated pixels to get wrong); a node that does have all four real values keeps rendering at exactly that authored pixel position. A structural (parent/child) connector between two flow-positioned nodes is drawn in pure CSS (they're always DOM-adjacent); everything else — a `meshfox:edge` cross-reference, or a structural edge touching a real-positioned node — is left for a small non-interactive JS pass in the template to measure and draw. Every `*.tera` file in `--template` (except one whose basename starts with `_`, a partial meant to be `{% import %}`ed rather than rendered standalone) is rendered and written to `--out` at the same relative path minus `.tera`; every other file is copied verbatim (CSS, fonts, ...) — except `template.toml` itself, the template's own config file (optional; a template with none gets an empty `base_url` and no `icons`), read from `--template`'s own directory and never copied to `--out`. A local image referenced from a node's Markdown body is copied alongside the output automatically; a `file`-type node's `display="code"` target is read once and inlined into the HTML directly (nothing left to fetch once static). See `site-template/` in this repo for a working example, including its own `template.toml`

Usage: meshfox static [OPTIONS] --template <TEMPLATE> [CANVAS]

Arguments:
  [CANVAS]  Path to the .canvas.md file. If omitted: auto-discover the single candidate in the current directory

Options:
  -t, --template <TEMPLATE>  Template directory
  -o, --out <OUT>            Output directory. Refused if it already exists and is non-empty, unless `--force` [default: site]
      --force                Overwrite an existing, non-empty `--out` directory
  -h, --help                 Print help

Rendering examples/hello.canvas.md with that template into a scratch directory:

meshfox static examples/hello.canvas.md --template site-template -o /tmp/meshfox-static-demo --force
ls /tmp/meshfox-static-demo
rm -rf /tmp/meshfox-static-demo
exit code: 0

meshfox static: wrote 7 file(s) to /tmp/meshfox-static-demo
apple-touch-icon.png
favicon-16.png
favicon-32.png
favicon.ico
icon-192.png
index.html
style.css
Architecture

Rust workspace + a small browser frontend:

crates/
  core/     canvas model, .canvas.md parsing/rendering (crate::mdcanvas),
            tree derivation, Markdown fence scanning, output-block
            rewriting, executors (bash, ...), auto-layout
  server/   library crate: axum HTTP backend (load/save a canvas file,
            execute a block) + the built web/ UI, embedded at compile time
            via rust-embed. No [[bin]] of its own — meshfox-cli links it.
  cli/      the only binary: `meshfox`. `run`/`fmt` use crates/core
            directly; `view` starts crates/server's backend (with the UI
            baked in) on localhost, read-only until the browser's "Edit"
            button is clicked. One executable, no separate server process
            to install or start.
web/        React + React Flow editor: renders the node/edge graph,
            in-node Markdown editing, run buttons wired to the server API.
            Built once (`npm run build`) and embedded into the `meshfox`
            binary — not shipped or loaded separately at runtime.

core is the shared brain; server wraps it in an HTTP API (and owns the embedded UI); cli is the single front door a user actually runs. Every write to the source file — cached output, saved layout — goes through a surgical patch (mdcanvas::set_node_body / set_node_meta) that touches only the node(s) that actually changed, never a full-document reformat.

Development

Requires Rust (stable, via rustup) and Node.js for the web UI.

crates/server embeds web/dist at compile time (via rust-embed), so build the frontend before building anything that depends on it — a fresh clone has an empty web/dist (just a tracked .gitkeep, see .gitignore) and meshfox view will happily start and serve the API, but its UI route will just say the assets weren't built rather than show anything.

cd web && npm install && npm run build   # do this first — see above
cargo build --workspace                  # build core/server/cli into the one `meshfox` binary
cargo test --workspace                   # run core's unit tests

# run block "smoke" on the node reached via tests -> smoke-test
cargo run -p meshfox-cli -- run examples/hello.canvas.md tests smoke-test smoke

# validate, fmt, and view all take the canvas path the same way
cargo run -p meshfox-cli -- validate examples/hello.canvas.md
cargo run -p meshfox-cli -- fmt examples/hello.canvas.md
cargo run -p meshfox-cli -- view examples/hello.canvas.md   # UI + API on :4590, opens read-only,
                                                              # launches your browser (--no-open to skip)
# (once meshfox is on your PATH: `meshfox view README.md`, `meshfox run
# README.md usage usage-help help`, etc. `run` recognizes a leading path by
# its .md suffix — node ids never have one — so it stays unambiguous
# alongside its own path/block-name arguments; auto-discovery still works
# everywhere too when the path is omitted)

Working on the frontend itself: the embedded copy is a snapshot from your last npm run build, so for live-reloading UI development run cd web && npm run dev (on :5173) in a second terminal alongside meshfox view (on :4590) — Vite proxies /api to :4590, same as before.

Unit tests

cargo test --workspace runs the Rust workspace's own unit/integration tests — crates/core's parsing (mdcanvas), tree derivation, auto-layout, fence-scanning, dependency-resolution, and variable-handling logic is the bulk of the coverage, each in its own #[cfg(test)] module next to the code it exercises, plus a handful of higher-level tests in crates/server/crates/cli (e.g. the tty websocket path). Pure Rust — no Node.js, no built frontend, no browser needed, unlike the end-to-end suite below. name=d so it's runnable like any other block here, deliberately without cache, same reasoning as "End-to-end tests"/"Release build": the log is per-run noise, not something worth freezing into this file. Flagged default since its block name (run) doesn't match its node id (unit-tests):

cargo test --workspace
End-to-end tests

web/e2e/ is a Playwright suite that drives the real UI in a real browser against a real meshfox view — not a mocked frontend — because the bugs this suite exists to catch (a dependency badge clipped by overflow: hidden, a highlight's box-shadow eaten by that same overflow: hidden) were only visible in the genuinely rendered, genuinely laid-out canvas; a component-level test wouldn't have seen either. It runs against three fixture canvases (web/e2e/fixtures/*.canvas.md, one each for dependency-chain UI, scroll/pan interaction, and text selection) — deterministic (no date/timestamps) and separate from examples/hello.canvas.md, so test stability never depends on the documentation example's own content. Every test runs the UI in its default read-only mode (never clicks "Edit"), so nothing in the suite ever writes back into a fixture file. Each suite runs against both Chromium and Firefox (chrome-*/firefox-* projects in playwright.config.ts); a small, explicitly-commented handful of assertions are skipped on Firefox where they hit confirmed Gecko-specific limitations (nested-scrollframe wheel-event chaining; drag-selection anchoring under a CSS transform ancestor) rather than anything wrong in this app's own code.

e2e-prep installs web/'s npm dependencies and Playwright's own Chromium and Firefox copies (separate from any browser already on your system) — both idempotent, so re-running once already done costs nothing. run depends on it (deps="e2e-prep"), so the dependency chain always pulls prep in first — no manual first-time step to remember. Both name=d so they're runnable like any other block here, deliberately without cache, same reasoning as "Release build" below: their logs are per-run noise (install/test timings) rather than something worth freezing into this file. run is also flagged default — the one block per node meshfox run <path> can address without a trailing block name — so meshfox run development e2e-tests runs it (and its e2e-prep dependency) directly, without needing to spell out ... e2e-tests run:

cd web
npm install
npx playwright install chromium firefox
cd web
npm run test:e2e

For interactive debugging instead of a one-shot run, use npm run test:e2e:ui (opens Playwright's UI mode — not runnable here, since it doesn't exit on its own).

playwright.config.ts's webServer starts meshfox view itself (via cargo run, rebuilding only if the Rust side changed) — no server needs to be already running, and no separate npm run build step either, since test:e2e runs it as a pretest:e2e hook. Debug builds of meshfox-server read web/dist fresh off disk on every request (rust-embed's debug-embed feature, which would force compile-time embedding even in a debug build, isn't enabled — see its Cargo.toml), so a frontend-only change just needs npm run build again, not a Rust rebuild, between test runs.

Release build

An optimized, distributable single binary. name=d so it's runnable like any other block here, deliberately without cache — a full release compile is slow and its build log isn't worth freezing into this file on every run:

cd web && npm install && npm run build
cargo build --workspace --release
echo "binary: target/release/meshfox"
Install

Copies the release binary to $INSTALL_PATH — see "Variables" above, where it's declared. env="$INSTALL_PATH" is what actually pulls it into this block's environment: the first run/configure that reaches a block with this env= prompts for it (default /usr/local/bin) and remembers the answer in .meshfox/README.md.env afterward, so this doesn't ask again on repeat installs — and, since no other block in this document declares env= at all, INSTALL_PATH is never resolved or asked about by anything else here. deps="release-build/release-build" (a cross-node reference — see "Runnable code fences" in SPEC.md — since release-build is a block in a different node than this one) means installing always builds fresh first. This block's own name already matches its node's id (install), so it's the node's implicit default block — no explicit default flag needed, unlike e2e-tests' run above, which needed one since its block is named run, not e2e-tests; both mechanisms are demonstrated in this document. Deliberately without cache, same reasoning as "Release build": the log is per-run noise, not something worth freezing into this file:

mkdir -p "$INSTALL_PATH"
cp target/release/meshfox "$INSTALL_PATH/meshfox"
echo "installed to $INSTALL_PATH/meshfox"
License

meshfox is MIT-licensed.

LICENSE
MIT License

Copyright (c) 2026 Maksim Dementev

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

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

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.