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

@wahengchang2023/tedit

v0.0.10

Published

Local visual template editor: design in browser, render PNG headlessly via CLI

Readme

tedit

CI npm version Node.js

tedit is a local, template-driven image generator. Design a layout once in a browser editor, bind the parts that change to named variables, then render the same design into PNGs over and over by swapping a small YAML/JSON data file.

It runs entirely on your machine — the editor is a local server, the renderer is a headless browser, and your projects are just folders. No accounts, no cloud, no upload.

Think of one template as a reusable "frame," and each data file as the content poured into it: market snapshots, quote cards, event banners, product shots — same layout, different content, every time.


The big picture

   ┌──────────────────────────────────────────────────────────────┐
   │  Browser editor  (tedit ui)  —  dark, Figma-style canvas      │
   │  Design freely → bind layers to named variables → save        │
   └──────────────────────────────┬───────────────────────────────┘
                                  │  save
                                  ▼
                   ┌──────────────────────────────┐
                   │        template.json          │   ← single source of truth
                   │  canvas + layers + variable    │
                   │  bindings (one JSON tree)      │
                   └──────────────┬───────────────┘
                                  │
              tedit render  +  data.yaml  (fills the variables)
                                  ▼
                   ┌──────────────────────────────┐
                   │            out.png            │   ← swap data = new content,
                   └──────────────────────────────┘      identical layout

The core guarantee: what you see in the editor is exactly what the CLI renders — pixel for pixel. Both load the same engine bundle and run the same Chromium, so the editor preview and the headless render can't drift apart.


Built for human-in-the-loop AI workflows

tedit is designed for the part of an AI pipeline where a human stays in the loop. The editor and the file format are two views of the same thing, so a person and an agent can work on one template without the result drifting apart.

  • Everything is JSON — an agent can do everything a human can. The template, every layer (text, image, shape, and full HTML/CSS/SVG), and all variable bindings are plain JSON. Anything you'd do by hand in the editor — produce a layout, add an HTML layer, rebind a variable — an LLM can do by emitting or editing JSON. No GUI automation, no screen-scraping.
  • One artifact, edited by both. Humans tweak template.json visually; agents generate or modify it programmatically; both operate on the same file. The visual editor is where a human reviews and corrects what the AI produced.
  • Deterministic, so review actually means something. The editor preview and the CLI render are pixel-identical (same engine bundle, same Chromium). Unlike diffusion image models, the same input always yields the same image — exact text, exact layout, every run. What a human approves in the editor is exactly what ships from the CLI.

A typical loop: an agent drafts template.json (layout + HTML layers + bindings) → a human reviews and adjusts in tedit ui → the pipeline renders many PNGs by swapping data files, every one matching what the human signed off on.

template rendered into data-driven variants

Because an HTML layer is just markup, it's a natural target for an LLM — generate a gradient, a chart, or a badge as <html>, drop it in as a layer, and it renders inside the same canvas:

HTML accepted as an iframe layer


See it

The editor: layers on the left, canvas in the middle, properties + variable binding on the right. Selected layers show their {variable} tag on canvas.

tedit editor

Layers can also be raw HTML/CSS/SVG, edited live with a code panel — useful for gradients, charts, badges, and anything easier to express in markup than to drag by hand.

tedit HTML layer editor

Then the last step is one command — turn that design into a PNG from the terminal:

tedit render ./my-card data.yaml -o out.png

The output is the same canvas you see above, rendered pixel for pixel.


Why tedit?

  • Visual first — compose templates in a local editor instead of hand-writing coordinates.
  • Data driven — bind layers like title, photo, or price and replace them at render time.
  • Repeatable output — render a batch of PNGs from one template by looping over data files.
  • Rich layers — text, images, shapes, and full HTML/CSS/SVG layers in the same canvas.
  • Local by default — projects are plain folders; assets live next to the template.
  • Editor/CLI parity — the editor and the headless renderer share one engine and one Chromium, so what you design is what you render.

Installation

npm install -g @wahengchang2023/tedit
npx playwright install chromium   # Chromium used for headless rendering

The package installs a tedit command on your PATH.

Requires Node.js 20 or newer. Prefer not to install globally? Use npx @wahengchang2023/tedit ... in any command below.


Quick start

Create a project folder and open the editor:

mkdir my-card
cd my-card
tedit ui .

In the editor:

  1. Add text, image, shape, or HTML layers.
  2. Select a layer and bind an editable property to a variable name such as title or photo.
  3. Save. tedit writes template.json into the project folder.

Create a data file:

# data.yaml
title: Hello from tedit
photo: images/photo.png

Render a PNG:

tedit render . data.yaml -o out.png

Your generated image is written to out.png.


Try the bundled showcase

From a cloned checkout you can run complete example projects (crypto snapshot, quote card, event banner):

npm install
npm run build
npx playwright install chromium
npm run ui:demo                      # open the demo card in the editor

Render a showcase template with sample data:

npm run tedit -- render examples/showcase/crypto examples/showcase/crypto/crypto-btc.yaml -o examples/showcase/out/btc.png

CLI reference

<project> can be a project folder or a direct path to its template.json.

| Command | Description | | --- | --- | | tedit ui [<project>] [--port <n>] [--no-open] | Start the local editor server and open the browser. | | tedit vars <project> [--json] | List the variables a template defines. | | tedit render <project> [<data>] [-o <out.png>] [--scale <n>] [--strict] | Render a template to PNG with optional JSON/YAML data. |

tedit ui ./my-card                                       # open a project
tedit vars ./my-card                                     # list variables
tedit vars ./my-card --json                              # variables as JSON
tedit render ./my-card ./my-card/data.yaml -o out.png    # render with data
tedit render ./my-card ./my-card/data.yaml -o [email protected] --scale 2   # 2x scale
tedit render ./my-card ./my-card/data.yaml -o out.png --strict       # fail on missing vars

Render behavior

  • No data file → renders with the design-time values stored in the template.
  • A missing variable → falls back to its design-time value and prints a warning.
  • --strict → a missing variable fails the render (exit code 4).
  • On success, render prints only the absolute output path to stdout, so scripts can capture it.

Exit codes

| Code | Meaning | | --- | --- | | 0 | Success | | 1 | General error | | 2 | Invalid arguments | | 3 | Invalid template | | 4 | Missing variable under --strict | | 5 | Asset loading failure |


How a variable becomes pixels

  data.yaml                 template.json
   title: "New title"   ─┐    bindings: [{ var: title, element: txt1, prop: content }]
   photo: ./a.png        │              │
                         ▼              ▼
            ┌───────────────────────────────────────┐
            │ resolver  (pure function, no I/O)      │
            │ writes variable values into the scene  │   missing → design value + warning
            │ remaps image paths to project-relative │   --strict missing → exit 4
            └────────────────────┬──────────────────┘
                                 ▼  resolved scene
            ┌───────────────────────────────────────┐
            │ headless  (Playwright + Chromium)      │
            │ load engine bundle → inject scene      │   waits for fonts + image decode
            │ → wait for render-ready → screenshot   │   locks deviceScaleFactor (--scale)
            └────────────────────┬──────────────────┘
                                 ▼
                              out.png

Project structure

A tedit project is one folder with one template:

my-card/
├── template.json          # required template, created by the editor
├── data.yaml              # optional render data
├── data.json              # optional render data
├── images/                # image assets referenced by the template or data
├── fonts/                 # optional custom fonts
├── project.json           # optional canvas defaults and font registry
└── .tedit/history/        # timestamped template backups created on save

Images resolve relative to the project folder — photo: images/photo.png loads my-card/images/photo.png. template.json and project.json are reserved names.


Data files

tedit accepts YAML or JSON. Keys match the variables you created in the editor.

title: Summer Launch
subtitle: New templates in seconds
photo: images/launch.png
{
  "title": "Summer Launch",
  "subtitle": "New templates in seconds",
  "photo": "images/launch.png"
}

Run tedit vars <project> when you're unsure which keys a template expects.


Fonts

Projects can use built-in Noto Sans TC plus optional custom fonts registered from the project fonts/ directory. If a required font can't be loaded, rendering exits with code 5 instead of silently falling back to a different font.


Recommended workflow

  1. Designtedit ui ./project, then save the template.
  2. Inspecttedit vars ./project to confirm the public variable names.
  3. Render oncetedit render ./project data.yaml -o out.png.
  4. Automate — loop over many data files to generate a batch.
for data in ./project/data/*.yaml; do
  name="$(basename "$data" .yaml)"
  tedit render ./project "$data" -o "./project/out/$name.png"
done

Learn more