npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tosijs-schematic

v0.1.0

Published

Render an agent-surface map (plain records) as a schematic SVG — the affordance grammar as a pure, dependency-free function. No DOM, no framework.

Readme

tosijs-schematic

Render an agent-surface map — plain records describing a UI's wired elements — as a schematic SVG: one shape per element at its true geometry, wearing an explicit affordance grammar so "can I act here?" never needs guessing.

It is a pure function over plain data. No DOM, no framework, no dependencies. The map travels as JSON, so the renderer runs in the page, in a headless embodiment, in a test harness, or on the far side of a wire from an app nobody is viewing.

Not tosijs-schema! That's the JSON-schema validation library. The near-collision is honest cross-fertilization rather than carelessness: schemas become contracts, contracts ride the map, and the map is what this package draws.

Install

npm add tosijs-schematic

Quick start

With tosijs, the map draws itself — describe() output is already the record format:

import { enableAgentInterface } from 'tosijs'
import { schematicSVG } from 'tosijs-schematic'

const agent = enableAgentInterface()
const svg = schematicSVG(agent.describe({ styles: true }))

Without tosijs, emit records yourself — anything that produces them gets the renderer and the grammar:

import { schematicSVG } from 'tosijs-schematic'

const svg = schematicSVG({
  wiring: [
    {
      tag: 'input',
      label: 'quantity',
      value: '3 ⟷ app.qty',
      bounds: { x: 10, y: 10, width: 160, height: 24 },
    },
    {
      tag: 'button',
      text: 'submit',
      on: { click: 'app.submit' },
      bounds: { x: 180, y: 10, width: 80, height: 24 },
    },
  ],
})

The record format (the contract)

One flat record per wired element. Producers may add fields beyond these — bound props ride under their own keys as "value ⟷ path" strings.

| field | type | meaning | | --- | --- | --- | | tag | string | lowercase tag name (required) | | bounds | {x, y, width, height} | page-coordinate geometry — layout is part of the semantics; zero-size or absent = not drawn | | label | string | the accessible name (aria-label, resolved labelledby, <label> association, title, alt) | | placeholder | string | the hint — deliberately distinct from label: an empty input must never read as content | | text | string | textContent, static ("foo") or bound ("foo ⟵ path") | | type | string | input kind when not plain text (checkbox, radio, range, email, …) | | checked | boolean | live toggle state | | focused | boolean | holds keyboard focus right now | | invalid | boolean | live ValidityState (or aria-invalid) says invalid | | required | boolean | the field is required | | disabled | boolean | disabled right now | | contentEditable | boolean | an editable region — treated as an input field | | on | Record<string, string \| string[]> | handlers by event type — a path when nameable, ƒ (or ƒ name) when not | | list | {path, idPath?} | this element renders a collection (drawn as ground, not figure) | | structural | boolean | structure, not affordance (headings, landmarks, containers) | | viewportFixed | boolean | rides the viewport (fixed/sticky) — bounds are screen coordinates | | style | {background, borderColor, color} | computed colors, worn when present | | id, part, role, description | string | identity and explanation, passed through to consumers |

Provenance tokens (exported as BOUND_TO_DOM / BOUND_TWO_WAY): a bound value reads "<shown> <arrow> <path>" means state flows to the DOM (display), means two-way (a user-writable affordance). A plain string with no arrow is a live-but-unbound value.

The grammar

Every rule is geometry or ASCII — hard-won: a single exotic glyph can tofu an entire caption run under a rasterizer, and text glyphs are mush at checkbox sizes.

| you see | it means | | --- | --- | | bold outline | wired to act (has handlers) | | badge, bottom-right | editable here (two-way binding, or contenteditable) | | caption ending * | required | | red corner flag, top-left | invalid right now — live ValidityState, the same truth :invalid styles | | filling a box / dot in a circle | checkbox / radio state, live | | italic caption | placeholder hint — not content | | faded | disabled right now (beats bold: a disabled button is not an affordance) | | double outline | keyboard focus — where the user is | | faint dotted | structure — including list containers (their items are the affordances) | | 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] |

Captions tell the truth in priority order: a held value wins (as label: value when both are known), an empty control falls back to its hint, then label, then text. Containers holding other drawn boxes get no text-derived caption — their children speak.

API

| export | what | | --- | --- | | schematicSVG(description, options?) | the renderer — returns an SVG string; each <g> carries data-record="<i>" linking back to description.wiring[i] (the image as index) | | 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) | | boundsOf(element) | an element's page-coordinate bounds — the natural within argument | | BOUND_TO_DOM, BOUND_TWO_WAY | the provenance tokens |

Options: pad, minLabelHeight, maxCaption, fontSize; within (a page-coordinate rect — spatial scoping: the viewBox is the region); index: true (stamp record indexes); decorate (below).

Plugins (EXPERIMENTAL)

decorate is the extension seam: called once per drawn record, just before its <g> closes, with the record, its resolved geometry, and an emit function. The corner slots already spoken for: top-left invalid flag, top-right index, bottom-right badge, outline focus/emphasis. Claim empty real estate:

schematicSVG(map, {
  decorate({ record, x, y, width, emit }) {
    if (record.style && contrastRatio(record.style) < 4.5) {
      emit(`<text x="${x + width / 2}" y="${y - 2}" font-size="6"
        text-anchor="middle">contrast!</text>`)
    }
  },
})

The first real plugins will shape the successor API — if you build one, open an issue.

Provenance

Extracted from the tosijs one user interface work: one source of truth for state, UI, and AI, where the agent's map derives from what the framework already knows. The grammar here was debugged against real rasterizers and real assistive-tech semantics — see the tosijs docs for the living demos (a todo list whose map redraws itself, and a kitchen-sink truth page).

License

Apache-2.0