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

silt-design-mode

v0.1.22

Published

Point at any element in a running UI, say what should change, and an agent makes the change in your source. Works standalone; Silt canvases are optional.

Readme

silt-design-mode

Point at any element in a running UI, say what should change, and an agent makes the change in your source.

Works standalone — no account, no API key. Silt is optional and additive on top.

Quick start

npm i silt-design-mode
npx silt-design-mode setup      # one time: agent skills, and Silt if you want it
npx silt-design-mode start      # the service — leave it running

Two spellings

| | works | | ----------------------------- | --------------------------------------------- | | npx silt-design-mode <verb> | always | | npx silt <verb> | inside a project that depends on this package |

npx silt-design-mode is the one to write down, in docs and in scripts: it needs nothing to already be true.

There is no bare silt to type, on purpose. The agent binary is downloaded and kept current for you — it lives in ~/.silt/bin and is revalidated against the published checksums on every run — but it is an engine this package drives, not a command with its own audience. Every verb is per project, and npx is what makes "this project" mean the directory you are standing in.

npx silt only works from a project that already has this package installed. Elsewhere npm resolves the name to an unrelated abandoned package and fails, so it is not a first-install route.

Then mount it around whatever you want to be able to point at:

import { DesignMode, createLocalSink, revertLocalEdit } from 'silt-design-mode';

<DesignMode
  sendOnSave
  onSend={createLocalSink({ repoRoot })}
  onRevert={revertLocalEdit}
  repoRoot={repoRoot}
  anchor="viewport"
>
  {children}
</DesignMode>;

Two things people get wrong here:

  • anchor defaults to parent, which pins the pill inside the wrapped box — on a page taller than the viewport that puts it at the bottom of the document, invisible until you scroll. Any full page wants anchor="viewport".
  • repoRoot can't be discovered from a browser. Pass it from build-time or server-side code.

npx silt-design-mode start — the service

One service per project, running two halves as one thing: the listener that applies edits to your source, and — only if this machine is linked to Silt — an agent scoped to this project, which is what makes it reachable from a canvas. Unlinked, it says so in one line and runs anyway.

"Project" means the nearest folder up whose package.json declares silt-design-mode — not the node_modules location, not the git root. That folder is the only tree it will edit.

It binds the first free port in 4747–4762, so several projects can run at once. createLocalSink() finds it by sweeping that range; you never pass a port.

Send to Silt has one deterministic priority: the component currently selected in Design Mode, otherwise the latest successfully edited component, otherwise the current stamped page. It pushes that exact identity from your working tree to your most recently updated canvas. The button calls the real MCP silt_push route directly: it does not ask an agent to identify the component, translate the URL into a filename, or choose a canvas. If the target is a private helper in a larger file, the listener exposes it under its exact name only in the throwaway snapshot sent to Silt; your source file and Git index are not changed.

Send a visual snapshot

The arrow beside Send to Silt offers Send component (the existing source/build flow) and Send snapshot. Select an element, then choose Send snapshot to capture its current DOM, author CSS, pseudo-elements, form values, fonts and images. It goes to your latest canvas without onboarding or building the repo. The main send button and its shortcut retain their existing component behavior.

Snapshots use the same canvas frame and bitmap pipeline as component builds, retain the captured theme, and reflow their layout when resized. Media queries, container queries, viewport units and root font sizing retain the source page context. CSS keyframes, animation timing, and CSS hover/focus transitions are preserved within the selected tree. JavaScript-driven interactions are not copied. They carry source identity as metadata, but have no application scripts or editable build source. Activate a snapshot on the canvas, enter Design mode, select a nested element, then drag it out or choose Take out. Extraction and placement are undoable. Older snapshots keep their captured layout; resend them with 0.1.18 or later for reflow.

Passwords are excluded. Accessible canvas/video pixels become images; protected media, embedded frames and unsupported content report a capture error. Assets must be readable by the browser, and the complete request is limited to 64 MB.

This option requires the updated Design Mode listener, Silt web service (the silt_snap tool), and render host. Restart the listener after updating the package. Component sending remains compatible with the existing server.

Source stamps — strongly recommended

Without this, an annotation tells the agent what you pointed at. With it, the annotation carries the file and line it lives on, and the agent stops searching. On a real codebase that's about a minute saved per edit.

It can't be done from the browser — React's element stack on a modern bundler never passes through the file the JSX is in. The compiler knows, so the build stamps it.

npx silt-design-mode setup offers this as its last step, so most people never run anything else. To add it to a project that is already set up, or to redo it:

npx silt-design-mode wire

Both show the diff and ask before touching your config, and both take --yes. Neither writes without a terminal to answer that prompt — scripting it means passing --yes.

npx silt-design-mode doctor tells you whether it's on. If you'd rather do it by hand:

Next.js

// next.config.mjs
import { withDesignMode } from 'silt-design-mode/next';

export default withDesignMode({/* your config */});

Vite — list it before react(); the React plugin is also enforce: 'pre' and its Fast Refresh preamble would shift every line number.

import designMode from 'silt-design-mode/vite';

export default defineConfig({ plugins: [designMode(), react()] });

Anything else on webpack: point a rule at silt-design-mode/loader.

Both wrappers are development-only by default and cost ~2 ms per file. If a file uses syntax we can't parse, stamping is skipped — it will never break your build, and never emit a line number it isn't sure of. A non-JSX host (.astro, .vue, .erb) can't be stamped at all.

The parser ships with the package, so there's nothing to install and the syntax gate on writes is always on. If your repo has its own @babel/parser, that copy is preferred — it's the one whose version matches what your build accepts.

Editing tiers

Each annotation takes one of two paths. With a provider key in the environment (ANTHROPIC_API_KEY, OPENAI_API_KEY, XAI_API_KEY, GROQ_API_KEY) a bounded visual change is a single fast round-trip. Without one — or when the change needs real comprehension — it delegates to your own coding agent, which is slower and understands more. Override the runner with DM_RUNNER.

Every edit is parse-gated before it's written, and every applied edit can be reverted from its pin.

Silt (optional)

npx silt-design-mode status      # what's linked, and to which instance
npx silt-design-mode login       # link this project (opens a browser)
npx silt-design-mode send        # share this agent session to a canvas
npx silt-design-mode push <entry>  # render a local component onto a canvas
npx silt-design-mode pull <name>   # cherry-pick a pushed version onto your branch
npx silt-design-mode mcp         # register Silt's MCP server with your coding agent
npx silt-design-mode logout      # sign out AND revoke the key (--all for every project)
npx silt-design-mode doctor      # health check + detected agents
npx silt-design-mode uninstall   # undo all of it — see below

npx silt-design-mode setup runs the ones you need for a first-time setup and skips whatever is already done.

Each project gets its own revocable key, listed in Silt as <project> — <host>, so revoking one repo leaves the others working. Keys live in your OS config dir, never in the repo. A project with no key of its own borrows the machine one and says so; npx silt-design-mode login there gives it its own.

logout revokes the key, it does not just forget it. The credential presents itself to Silt and is killed by its own authority, so nothing is left live behind you and there is no Settings visit to remember. If Silt cannot be reached, you are still logged out locally and the key is named on screen as still active so you can revoke it by hand — being offline is never a reason to be stuck logged in.

One thing to know either way: an agent already holding an open connection keeps the key in memory until it stops. It is a foreground process, so Ctrl-C in the terminal running npx silt-design-mode start ends it immediately.

Uninstalling

npx silt-design-mode uninstall   # then: npm rm silt-design-mode

It undoes what installing did — the agent skills, the AGENTS.md block, the build-plugin wiring in your next.config/vite.config, the annotation inbox — and signs out, revoking the key and dropping the MCP registration.

It asks nothing, because you typed it. What protects you instead is that it only deletes what it can prove it wrote: a skill file you edited is kept and named, and so is one from an older release, since the two are indistinguishable (--force deletes those too). The AGENTS.md block is identified by a marker pair and a sentinel string, so markers you typed yourself around your own notes are left alone. A config that has drifted beyond a clean reverse is skipped with instructions rather than guessed at.

--all signs out of every project on the machine, not just this one.

Left in place deliberately: the cached binary in ~/.silt/bin (machine-wide, shared by every repo — silt uninstall removes that and the credentials dir), and node_modules, since the command cannot remove the package it is running from.

SILT_API_URL picks the instance (default https://silt.app). The silt-design-mode-beta command — silt-beta for short — targets beta.silt.app, which is a separate account and separate data. Because keys are per project, different projects can point at different instances.

Registering the MCP server

setup does this for you. mcp is the same step on its own, and --scope, --client and --no-sign-in are its only flags:

npx silt-design-mode mcp                    # every project on this machine (default)
npx silt-design-mode mcp --scope local      # this project only, private config
npx silt-design-mode mcp --scope project    # committed .mcp.json, for the team
npx silt-design-mode mcp --client cursor    # …or codex; default is claude
npx silt-design-mode mcp remove             # --all for every scope

--scope project never stores a key or a helper, on purpose: that scope writes a committed .mcp.json, a long-lived key in git is an incident, and a helper would carry one machine's absolute path to a binary nobody else has. It registers for OAuth instead. The other two scopes live in the client's own private config.

No login is needed — OAuth in the browser works on an unlinked machine. When the project is linked, registration gives the client two ways to authenticate, and it needs both:

  • a headersHelper pointing at silt mcp headers, which reads the credential store on every connect. This is what makes a key rotation a non-event — a re-login is picked up by itself and re-running mcp is harmless.
  • a static Authorization header at the default scope, because Claude Code runs a locally-scoped helper only after you have accepted the trust prompt for that folder. A project you just created has not been trusted, so without the static header the very first connection arrives with no credential at all, fails, and is remembered as needing browser sign-in.

At --scope user there is no trust gate and the server is visible from every directory, so that scope gets the helper alone and no copy of the key.

One consequence worth knowing: at the default scope the client's own config holds a copy of your key (private to you, not in git). silt logout and silt uninstall both clear it for you — they drop the registration before deleting the credential store, which is the only order in which they can still work out its name. A registration made against a different instance, or on another machine, is the case they cannot reach: run npx silt-design-mode mcp remove --all there.

A direct mcp run also triggers the client's own sign-in after registering, so the browser opens while you're still at the keyboard rather than ambushing you after a restart. --no-sign-in skips it, and an older client without that subcommand — or no terminal at all — is detected and skipped quietly.

setup deliberately does not sign in: it sets SILT_MCP_NO_SIGN_IN=1, an env var rather than the flag, because this host runs whatever daemon is published and an unknown flag exits 2 before the command runs where an unknown env var is ignored. Inside setup, sign-in could only fire when the project has no usable credential — meaning the login step just failed, and whatever stopped that would stop this too. Run npx silt-design-mode mcp on its own if you want the browser step.

Whichever route: the client reads its server list at startup, so the tools appear only after it restarts (or you run /mcp).

Agent skills

Installing the package writes three skill files into your repo, so the coding agent you already use knows the tool exists and how to drive it: one for setting it up and running it, one for applying a batch of annotations, and one for reaching Silt itself — which canvas tools exist and when to call them instead of the shell. Claude Code also gets a /silt-send command for sharing the session. npx silt-design-mode init --force rewrites them after an upgrade.

An agent only opens a skill it decides to open, and the rules that keep it off the wrong surface are needed before that decision. So a short block also goes into AGENTS.md, which every major agent loads at startup: what this package is, that Silt is reached through silt_* tools rather than the shell, and that an annotation already carries the file and line to edit. Only the text between the <!-- BEGIN:silt-design-mode --> and <!-- END:silt-design-mode --> markers is ours — put your own notes outside them and they are left alone, upgrade after upgrade. There is no --force for this one, because the rest of the file is yours. When the installer cannot be certain which bytes are its own — the markers do not form one clean pair, or they do but the text between them was not written by the installer — it says so and changes nothing. Same when it cannot read or write the file at all. Refusing is the only safe answer: a repair pass would guess, and the span it guessed about is the span most likely to hold your writing.

License

See LICENSE.