{"_id":"tosijs-floorplan","_rev":"3-5a2f47453037a4c33baf4f5b7d4095c2","name":"tosijs-floorplan","dist-tags":{"latest":"0.5.0"},"versions":{"0.3.0":{"name":"tosijs-floorplan","version":"0.3.0","keywords":["floorplan","schematic","svg","agent","accessibility","affordance","wireframe","tosijs","webmcp"],"author":"Tonio Loewald","license":"Apache-2.0","_id":"tosijs-floorplan@0.3.0","maintainers":[{"name":"tonioloewald","email":"tonio@loewald.com"}],"dist":{"shasum":"055d83a68c6c2ae38a4895921d9338f945435037","tarball":"https://registry.npmjs.org/tosijs-floorplan/-/tosijs-floorplan-0.3.0.tgz","fileCount":7,"integrity":"sha512-pqfVBEugcZ9yhdCf81mzhPE1G0Q8cRXDNoJl41cioeYhHU9Wc9aQZDI36iuxtWs/9jiVLbqRwcFlVk/2TvsoQw==","signatures":[{"sig":"MEUCIQCu7rdgBlAogLTMpEhZzjnPI2u0LNTMjGaM/8gpnLLy7wIgBe6hn94TcOB5dSsdMYOPxwibUdrRsskl1yxzGjdzI+A=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":105492},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","module":"dist/index.js","shasum":"055d83a68c6c2ae38a4895921d9338f945435037","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"test":"bun test","build":"bun build src/index.ts --outdir dist --format esm && tsc -p tsconfig.json","prepublishOnly":"bun test && bun run build"},"_npmUser":{"name":"tonioloewald","email":"tonio@loewald.com"},"_integrity":"sha512-pqfVBEugcZ9yhdCf81mzhPE1G0Q8cRXDNoJl41cioeYhHU9Wc9aQZDI36iuxtWs/9jiVLbqRwcFlVk/2TvsoQw==","repository":{"url":"git+https://github.com/tonioloewald/tosijs-floorplan.git","type":"git"},"_npmVersion":"10.8.3","description":"Render an agent-surface map (plain records) as a floorplan SVG — the affordance grammar as a pure, dependency-free function. No DOM, no framework. Formerly tosijs-schematic.","directories":{},"_nodeVersion":"24.3.0","_hasShrinkwrap":false,"devDependencies":{"typescript":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/tosijs-floorplan_0.3.0_1786263741465_0.9570296697795762","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"tosijs-floorplan","version":"0.4.0","keywords":["floorplan","schematic","svg","agent","accessibility","affordance","wireframe","tosijs","webmcp"],"author":"Tonio Loewald","license":"Apache-2.0","_id":"tosijs-floorplan@0.4.0","maintainers":[{"name":"tonioloewald","email":"tonio@loewald.com"}],"dist":{"shasum":"4cf27f7f06b3ceb21e6f640927971445a34c2d82","tarball":"https://registry.npmjs.org/tosijs-floorplan/-/tosijs-floorplan-0.4.0.tgz","fileCount":8,"integrity":"sha512-bV/U3iR1fKYdXq6MaIW1DLFNDe2jIj+vzfMY5RQzqnZTfakPWTZfRx4jySi6Qu+F8dkCtvnWmJNOkMeL+SpLPg==","signatures":[{"sig":"MEYCIQDrB6zuolgam4HAM+ku5+Lsi9dXMwAut3x3Ay/bo10ZVwIhAN7QKnyJ0Y3uzC7Wfn8dKbILbYn/DEko2bubxBcurtKE","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIHtl68ly+kejbmQ2NsF1A4i1BgS5QuHjowEYoPYNi5n+AiEA14K+h9yOFN6wW9rqz0ei5IrA1Dn7gMHs+Wsqh7S0m84=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":132536},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","module":"dist/index.js","shasum":"4cf27f7f06b3ceb21e6f640927971445a34c2d82","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"test":"bun test","build":"bun build src/index.ts --outdir dist --format esm && tsc -p tsconfig.json","stability":"bun tools/byte-stability.ts","typecheck":"tsc -p tsconfig.json --noEmit --emitDeclarationOnly false","prepublishOnly":"bun test && bun run build"},"_npmUser":{"name":"tonioloewald","email":"tonio@loewald.com"},"_integrity":"sha512-bV/U3iR1fKYdXq6MaIW1DLFNDe2jIj+vzfMY5RQzqnZTfakPWTZfRx4jySi6Qu+F8dkCtvnWmJNOkMeL+SpLPg==","repository":{"url":"git+https://github.com/tonioloewald/tosijs-floorplan.git","type":"git"},"_npmVersion":"10.8.3","description":"Render an agent-surface map (plain records) as a floorplan SVG — the affordance grammar as a pure, dependency-free function. No DOM, no framework. Formerly tosijs-schematic.","directories":{},"_nodeVersion":"26.3.0","_hasShrinkwrap":false,"devDependencies":{"typescript":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/tosijs-floorplan_0.4.0_1788717172289_0.5840910550824052","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"_id":"tosijs-floorplan@0.5.0","dist":{"shasum":"4b1b2be20cdf1486c5399bedc741a0339d1d2b0b","tarball":"https://registry.npmjs.org/tosijs-floorplan/-/tosijs-floorplan-0.5.0.tgz","fileCount":8,"integrity":"sha512-5+OlRBk0cjJ5x2T/GqPjUee7cJcfrIDBRkkCsEIbjrjviEaRGfyWi9ppZk7LHRa2wA66Q3Ocfl7Ex86KH9cLkw==","signatures":[{"sig":"MEUCIQDV1Ndph7QL05y4YA/bM22TUfvdSR2br0C8C6J4RuTLeQIgSMBiDQS4wqQDg8eQP+x7jbD5BW9fqtsT2S0ZcxS6998=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIAW6ZvT5AgV7XJiFh8MIRL2X+xy4M70iHd3ro4ELgjmhAiEAnC7uzkrymP8QFXb+yb1+OKWFdHuyDlkhoYGBGfKl2cU="}],"unpackedSize":149725},"main":"dist/index.js","name":"tosijs-floorplan","type":"module","types":"dist/index.d.ts","author":"Tonio Loewald","module":"dist/index.js","shasum":"4b1b2be20cdf1486c5399bedc741a0339d1d2b0b","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"license":"Apache-2.0","scripts":{"test":"bun test","build":"bun build src/index.ts --outdir dist --format esm && tsc -p tsconfig.json","stability":"bun tools/byte-stability.ts","typecheck":"tsc -p tsconfig.json --noEmit --emitDeclarationOnly false","prepublishOnly":"bun test && bun run build"},"version":"0.5.0","_npmUser":{"name":"tonioloewald","email":"tonio@loewald.com"},"keywords":["floorplan","schematic","svg","agent","accessibility","affordance","wireframe","tosijs","webmcp"],"_integrity":"sha512-5+OlRBk0cjJ5x2T/GqPjUee7cJcfrIDBRkkCsEIbjrjviEaRGfyWi9ppZk7LHRa2wA66Q3Ocfl7Ex86KH9cLkw==","repository":{"url":"git+https://github.com/tonioloewald/tosijs-floorplan.git","type":"git"},"_npmVersion":"10.8.3","description":"Render an agent-surface map (plain records) as a floorplan SVG — the affordance grammar as a pure, dependency-free function. No DOM, no framework. Formerly tosijs-schematic.","directories":{},"maintainers":[{"name":"tonioloewald","email":"tonio@loewald.com"}],"_nodeVersion":"26.3.0","_hasShrinkwrap":false,"devDependencies":{"typescript":"^7.0.2"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/tosijs-floorplan_0.5.0_1789290803426_0.8840420538965563"}}},"time":{"created":"2026-08-09T08:22:21.397Z","modified":"2026-09-13T09:13:23.660Z","0.3.0":"2026-08-09T08:22:21.605Z","0.4.0":"2026-09-06T17:52:52.365Z","0.5.0":"2026-09-13T09:13:23.519Z"},"author":"Tonio Loewald","license":"Apache-2.0","keywords":["floorplan","schematic","svg","agent","accessibility","affordance","wireframe","tosijs","webmcp"],"repository":{"url":"git+https://github.com/tonioloewald/tosijs-floorplan.git","type":"git"},"description":"Render an agent-surface map (plain records) as a floorplan SVG — the affordance grammar as a pure, dependency-free function. No DOM, no framework. Formerly tosijs-schematic.","maintainers":[{"name":"tonioloewald","email":"tonio@loewald.com"}],"readme":"# tosijs-floorplan\n\nRender an **agent-surface map** — plain records describing a UI's wired\nelements — as a floorplan SVG: one shape per element at its true geometry,\nwearing an explicit **affordance grammar** so \"can I act here?\" never needs\nguessing. Like a floorplan of a building, it documents a *real, live*\nstructure — where the doors are, which ones open.\n\nIt is a **pure function over plain data**. No DOM, no framework, no\ndependencies. The map travels as JSON, so the renderer runs in the page, in\na headless embodiment, in a test harness, or on the far side of a wire from\nan app nobody is viewing.\n\n> **Formerly `tosijs-schematic`** (deprecated on npm at 0.2.0; renamed\n> before its first external consumer shipped). The old name near-collided\n> with [tosijs-schema](https://github.com/tonioloewald/tosijs-schema), the\n> JSON-schema validation library, and confused readers in practice — an\n> honest hazard of real cross-fertilization: schemas become *contracts*,\n> contracts ride the *map*, and the map is what this package draws. The\n> **exported API keeps its names** (`schematic()`, `SchematicRecord`, …):\n> the drawing is still a schematic in the common-noun sense, and the record\n> format is a multi-producer contract mid-adoption.\n\n## Install\n\n```\nnpm add tosijs-floorplan\n```\n\n## Quick start\n\nWith [tosijs](https://tosijs.net), the map draws itself — `describe()`\noutput is already the record format:\n\n```js\nimport { enableAgentInterface } from 'tosijs'\nimport { schematicSVG } from 'tosijs-floorplan'\n\nconst agent = enableAgentInterface()\nconst svg = schematicSVG(agent.describe({ styles: true }))\n```\n\nWithout tosijs, emit records yourself — anything that produces them gets\nthe renderer and the grammar:\n\n```js\nimport { schematicSVG } from 'tosijs-floorplan'\n\nconst svg = schematicSVG({\n  wiring: [\n    {\n      tag: 'input',\n      label: 'quantity',\n      value: '3 ⟷ app.qty',\n      bounds: { x: 10, y: 10, width: 160, height: 24 },\n    },\n    {\n      tag: 'button',\n      text: 'submit',\n      on: { click: 'app.submit' },\n      bounds: { x: 180, y: 10, width: 80, height: 24 },\n    },\n  ],\n})\n```\n\n## The record format (the contract)\n\nOne flat record per wired element. Producers may add fields beyond these —\n**bound props ride under their own keys** as `\"value ⟷ path\"` strings.\n\n| field | type | meaning |\n| --- | --- | --- |\n| `tag` | `string` | lowercase tag name (required) |\n| `ref` | `string` | a durable, actionable handle from the producer (survives re-renders — an agent can *act* on it, where an index only *looks up*) |\n| `flags` | `{kind, label, severity?}[]` | computed verdicts about the element (contrast ratios, target sizes, …) |\n| `image` | `string` | data-URL snapshot of inline media, drawn in place — pixels a pure renderer can't obtain |\n| `bounds` | `{x, y, width, height}` | page-coordinate geometry — layout is part of the semantics; zero-size or absent = not drawn |\n| `label` | `string` | the accessible **name** (aria-label, resolved labelledby, `<label>` association, title, alt) |\n| `placeholder` | `string` | the hint — deliberately distinct from `label`: an empty input must never read as content |\n| `text` | `string` | textContent, static (`\"foo\"`) or bound (`\"foo ⟵ path\"`) |\n| `href` | `string` | a link's destination — distinct from `text` (\"the link *says* X\" is not \"the link *goes to* Y\"). Captions fall back to it only when nothing else names the element; it always rides the **legend** |\n| `value` | `string` | a filled control's value, distinct from `label`/`placeholder` — static (`\"3\"`) or bound (`\"3 ⟷ app.qty\"`); the fact that distinguishes an empty form from a filled one |\n| `type` | `string` | input kind when not plain text (`checkbox`, `radio`, `range`, `email`, …) |\n| `checked` | `boolean` | live toggle state |\n| `focused` | `boolean` | holds keyboard focus right now |\n| `invalid` | `boolean` | live ValidityState (or aria-invalid) says invalid |\n| `required` | `boolean` | the field is required |\n| `disabled` | `boolean` | disabled right now |\n| `contentEditable` | `boolean` | an editable region — treated as an input field |\n| `interactive` | `boolean` | the producer's **assertion** that this element can be acted on — for producers that cannot introspect handlers (React delegates at a root; vanilla `addEventListener` is not enumerable from page script). Asserting is truth-telling; fabricating `on` to unlock the styling would be a lie in the payload. A binding framework never needs it |\n| `editable` | `boolean` | the producer's assertion that text goes in here — the DOM-side counterpart of `contentEditable` / a two-way binding |\n| `secret` | `boolean` | the producer **withheld** facts about this element (a secret-marked region: a token lives in the destination, so neither `label` nor `href` is published). **Fail-closed on the renderer side**: a secret record's label/text/value/placeholder/href/**image** never reach the drawing or the legend even if a producer bug leaves them in the record, and any truthy `secret` scrubs (malformed errs toward withholding) — it draws `<tag> [withheld]` and its legend entry says `redacted: true` (structural records included) — \"no destination\" and \"destination withheld\" are different facts |\n| `on` | `Record<string, string \\| string[]>` | handlers by event type — a path when nameable, `ƒ` (or `ƒ name`) when not |\n| `list` | `{path, idPath?}` | this element renders a collection (drawn as *ground*, not figure) |\n| `structural` | `boolean` | structure, not affordance (headings, landmarks, containers) |\n| `viewportFixed` | `boolean` | rides the viewport (fixed/sticky) — bounds are screen coordinates |\n| `style` | `{background, borderColor, color}` | computed colors, worn when present |\n| `id`, `part`, `role`, `description` | `string` | identity and explanation, passed through to consumers |\n\n**Provenance tokens** (exported as `BOUND_TO_DOM` / `BOUND_TWO_WAY`): a bound\nvalue reads `\"<shown> <arrow> <path>\"` — `⟵` means state flows to the DOM\n(display), `⟷` means two-way (a user-writable affordance). A plain string\nwith no arrow is a live-but-unbound value. The **structural arrow is the\nLAST one in the string** — the surface appends it, so consumers must split\nat the last occurrence, and an arrow token buried inside the data confers\nnothing (the renderer parses defensively: it neutralizes interior arrows to\n`<->` / `<-` in every drawn text run and in the legend's *display* fields —\n`caption` and `value` are always neutralized — and never scans\nidentity/name fields (`tag`, `id`, `part`, `role`, `label`, `placeholder`,\n`type`, `description`, `href`, `ref`, `image`) for bindings at all, since\nthe surface never appends an arrow to those). Two legend fields are\n**verbatim, deliberately**: `href` is an opaque destination — rewriting\nbytes inside a URL corrupts the one fact an agent acts on — and `flags`\nare copied as the producer computed them. Consumers must never parse\nprovenance from either (they are in the never-scanned set; an arrow there\nis data), and must not forward them into a caption-style text run without\nneutralizing first. **Producers whose record\ncontent derives from untrusted sources — any DOM extractor reading page\ncontent — MUST neutralize both tokens inside data at the source**, as\ntosijs ≥ 1.8.0 does. This is normative because of an honest residual: a\nforged arrow in *suffix* position on a bindable field (`\"data ⟷ fake.path\"`\nas the entire text) is structurally indistinguishable from a real binding —\nrenderer-side defense ends where the format's own syntax begins. And the\nbindable set is **open by design** (bound props ride under their own keys),\nso the never-scanned list is a denylist over an open key set: any key a\nproducer invents is bindable, and arrows in it are trusted as structure.\nThe defense is narrowed, not closed — producer-side neutralization remains\nthe actual perimeter (issue #11). The capability scan (below) likewise\ncounts an arrow in **any position** within a bindable field: under the\nformat's own last-occurrence parse, \"contains an arrow\" and \"has a\nstructural arrow\" are equivalent for a lone token, so a position check\nwould add no security — only the #11 perimeter does.\n\n**Producers that cannot introspect handlers** (React's synthetic delegation,\nAngular's compiler output, vanilla `addEventListener` — none enumerable from\npage script) assert the affordance instead: `interactive` / `editable`, per\nrecord. When a map draws affordance-shaped boxes but **no** record carries\naffordance evidence (no `on`, `href`, `contentEditable`, two-way binding, or\nassertion) **and none carries capability evidence either** — a handler, an\nassertion, or a provenance arrow in a bindable field *anywhere in the map*,\ndisplay-only `⟵` included, proves the producer can see wiring (#10: a\nread-only dashboard from a binding framework is a sighted map of an inert\npage, not a blind map) — the result carries a `note`, and the svg's\n`<desc>` repeats it,\nbecause \"nothing here is actionable\" and \"the producer couldn't tell\" are\ndifferent statements, and a consumer must never mistake the second for the\nfirst. Two caveats pin the semantics: **partial evidence does not establish\nthe rest** — on a map where some records carry evidence, a record without\nany still means *unknown*, not *inert* (the note only marks the total-blindness\ncase; non-introspecting producers should assert per actable record, not rely\non the note); and **only `interactive: true` / `editable: true` are signal** —\n`false` is indistinguishable from absent and cannot veto evidence the record\nitself carries (`on`, `href`, a binding). \"Introspected and found nothing\"\ncurrently has no encoding; propose one via issue before relying on it.\n\n**The picture is not the whole payload.** The renderer is *allowed to omit*:\ncaptions and badges below legibility thresholds move to the legend, keyed by\nthe stamped index/ref, and facts that never draw well (`href` above all)\nlive there always. A consumer of the raster is expected to hold\n`schematic().legend` alongside it — the image says *where* and *which*; the\nlegend says *what*.\n\n## The grammar\n\nEvery rule is **geometry or ASCII** — hard-won: a single exotic glyph can\ntofu an entire caption run under a rasterizer, and text glyphs are mush at\ncheckbox sizes.\n\n| you see | it means |\n| --- | --- |\n| **bold outline** | wired to act — handlers, a destination (`href`: a link IS an affordance), or the producer's `interactive` assertion |\n| `↔` badge, bottom-right | editable here (two-way binding, or contenteditable) |\n| caption ending `*` | required |\n| **red corner flag**, top-left | invalid *right now* — live ValidityState, the same truth `:invalid` styles |\n| `✕` filling a box / dot in a circle | checkbox / radio state, live |\n| *italic caption* | placeholder hint — **not** content |\n| faded | disabled right now (beats bold: a disabled button is not an affordance) |\n| double outline | keyboard focus — where the user is |\n| faint dotted | structure — including list *containers* (their items are the affordances) |\n| number, top-right, on a white backdrop | the record's index (`index: true`) — the raster form of `data-record`: read it off the image, look up `wiring[n]` |\n| a `ref` (e.g. `@42`), top-right | the producer's **durable, actionable handle** — takes the index slot when present, survives re-renders, rides the group as `data-ref` |\n| colored bars, left edge | computed **verdicts** (`flags`): WCAG contrast failures and friends — error red, warn amber, info gray, first flag's label shown |\n| pixels inside a box | embedded media (`image`: a data URL) — the producer's snapshot of inline `<svg>`/`<canvas>`, drawn in place |\n| a bare box wearing only a stamped number | too small to label legibly — its caption, badges and flags live in the **legend**, matched by that number |\n| single amber bar, left edge | an interactive element below `targetSize` (default 24×24, WCAG 2.5.8; set 44/48 for the touch-target bar) — a usability defect in its own right; the measurement is in the legend |\n| footer strip: \"N elements with details in legend\" | the image's confession that it isn't the whole map — fetch `schematic().legend` (a machine-readable `<desc>` says the same) |\n\nThe target-size audit honours WCAG 2.5.8's **inline exception** as far as\npure geometry can: a link is exempt when it has text **and its box is wider\nthan tall** — the shape text layout produces (flagging prose links would\nfire on every paragraph — a check that cries wolf gets ignored, taking the\nreal findings with it). Icon links stay flagged: an `<a>` wrapping an\n`<svg>` with no text, and equally a **square** icon link that happens to\ncarry a label or a glyph — a 16×16 box was not sized by its text, whatever\nthe text is (the text-only rule exempted exactly the header-row-of-icons\ncase the check was built for; haltija's issue #2 caught it). A producer\nwith DOM access computes the exception *properly* (computed display +\nparent text nodes) and ships the finding via `flags` — that is the\n**intended path** for DOM producers. A producer flag whose `kind` is one\nof the **target-claim kinds** (`TARGET_FLAG_KINDS`, case-insensitive:\n`target`, `target-size`, `targetsize`, `target_size`, `smalltarget`)\n**supersedes** the drawn audit, so the two never double-mark — an explicit\nset, because a substring match let `target-ok` stand the audit down (#8);\nnew target-claim kinds are added to `TARGET_FLAG_KINDS` via an issue here,\nthe same additive path as any other format change, and the set is\nread-only by contract.\nSupersession is a *drawing* concern and therefore **opt-in**:\n`targetSizeFinding` ignores producer flags by default (an audit wants the\ngeometry verdict regardless of what got drawn); `schematic()` passes\n`honorProducerFlags: true`. Hidden is not small: zero-size records are\nnever undersized (#9). Both rules are exported (`isInteractive`,\n`targetSizeFinding`) so audits share this implementation instead of\nkeeping a drifting copy — and as of 0.5.0 they reproduce an audit's\nverdicts without consumer-side normalization (#7/#8/#9/#13).\n\nCaptions tell the truth in priority order: a held **value** wins (as\n`label: value` when both are known), an empty control falls back to its\n*hint*, then label, then text. Containers holding other drawn boxes get no\ntext-derived caption — their children speak. Captions **wrap** when the box\naffords more than one line — a paragraph that wraps on the real page has\nthe same vertical room here — and only truncate (with `…`) when the\ngeometry genuinely runs out.\n\n## API\n\n| export | what |\n| --- | --- |\n| `schematic(description, options?)` | the renderer's primary form — returns `{ svg, legend, note? }`: the drawing, the metadata it could not legibly carry (cramped/truncated/undersized records, keyed by index/ref), and — when no record carries affordance evidence — the note saying so. **Pair every raster with its legend.** |\n| `schematicSVG(description, options?)` | `schematic().svg` — the string-only form; each `<g>` carries `data-record=\"<i>\"` linking back to `description.wiring[i]` (the image as index) |\n| `isInteractive(record)` | \"can I act here?\" — the single implementation (handlers, `href`, `contentEditable`, a structural two-way binding, or the producer's assertion; ground never). Exported so audits consume it instead of keeping a drifting copy |\n| `targetSizeFinding(record, targetSize?, {honorProducerFlags?})` | the WCAG 2.5.8 rule with the settled exemptions (toggles, text-sized links, zero-size; producer-flag supersession only when honoured — the renderer's setting, not an audit's) — the measured legend fact, or `null` |\n| `TARGET_FLAG_KINDS` | the flag kinds that claim to *be* a target-size finding and may supersede the drawn audit |\n| `rasterizeSVG(svg, {scale})` | SVG → PNG Blob for vision encoders (browser canvas; under bun/node use `@resvg/resvg-js` — rasterize at 2× so labels OCR cleanly) |\n| `boundsOf(element)` | an element's page-coordinate bounds — the natural `within` argument |\n| `BOUND_TO_DOM`, `BOUND_TWO_WAY` | the provenance tokens |\n\n**Options**: `pad`, `minLabelHeight`, `maxCaption`, `fontSize`; `within`\n(a page-coordinate rect — spatial scoping: the viewBox *is* the region);\n`index: true` (stamp record indexes); `targetSize` (the undersized-audit\nfloor: 24 default, 44/48 for touch, 0 off); `legendNote: false` (suppress\nthe footer strip); `decorate` (below).\n\n## Plugins (EXPERIMENTAL)\n\n`decorate` is the extension seam: called once per drawn record, just before\nits `<g>` closes, with the record, its resolved geometry, and an `emit`\nfunction. The corner slots already spoken for: **top-left** invalid flag,\n**top-right** index, **bottom-right** `↔` badge, **outline** focus/emphasis.\nClaim empty real estate:\n\n```js\nschematicSVG(map, {\n  decorate({ record, x, y, width, emit }) {\n    if (record.style && contrastRatio(record.style) < 4.5) {\n      emit(`<text x=\"${x + width / 2}\" y=\"${y - 2}\" font-size=\"6\"\n        text-anchor=\"middle\">contrast!</text>`)\n    }\n  },\n})\n```\n\nThe first real plugins will shape the successor API — if you build one,\nopen an issue.\n\n## Provenance\n\nExtracted from the [tosijs](https://tosijs.net) *one user interface* work:\none source of truth for state, UI, and AI, where the agent's map derives\nfrom what the framework already knows. The grammar here was debugged\nagainst real rasterizers and real assistive-tech semantics — see the tosijs\ndocs for the living demos (a todo list whose map redraws itself, and a\nkitchen-sink truth page).\n\n## License\n\nApache-2.0\n","readmeFilename":"README.md"}