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

@assemora/studio

v0.2.6

Published

Assemora Studio: the visual application, served beside an application it does not know

Readme

@assemora/studio

Studio: a React SPA and a client of the application layer (SPEC.md §58).

pnpm --filter @assemora/playground dev   # the API, on :4000
pnpm --filter @assemora/studio dev       # Studio, on :5173

Vite proxies /api to the application, so the session cookie is first-party in development exactly as it is in production.

What is built

All of SPEC.md §115: login, navigation, resource CRUD, media, the API Explorer, pages, the block builder, revision history, users, the developer section — and Design, the five groups of theme tokens of SPEC.md §62.

The builder's canvas is an iframe running the application's frontend — the real renderer, its block views, its theme (SPEC.md §59). Studio sends the tree in and gets geometry and clicks back, and draws its selection outline over the frame, so nothing it does changes what the page looks like.

The language it says it in

Studio ships its own words in English, Ukrainian and Russian, and a person chooses which from the account menu — or from the sign-in screen, which is the one screen read before there is an account to have a preference on (ADR-0030).

That is not the language of the content. A deployment's locales decide which rows a listing holds and come from the registry (SPEC.md §131); this decides what the buttons around them say. Both switchers are on the account menu, named apart, because a Ukrainian shop is routinely filled in by somebody who reads English and the reverse is just as ordinary.

  • src/i18n/messages/ — the catalogue, eight slices of one key space. A key holds every language at once, so one written only in English does not compile.
  • src/i18n/translate.tsxuseT() for a sentence, useWoven() for a sentence with something drawn into the middle of it, useDates() for a date the language writes.
  • A message's parameters are read off its English reading: t('entry.savedAt', { when }) does not compile without when. A plural carries three forms and the rule for choosing one is per language — English takes the second at 21, Ukrainian the first.

What the application says — a resource's label, a field's help text, a refusal it wrote — is left in the language it was written in. Studio translates what Studio says.

The rule that shapes everything here

Studio holds no knowledge of any particular application. It asks /api/_introspection what exists and renders that:

  • the navigation's collections are the registry's resources, drawn as the icon each one names — resource(…, { icon: 'utensils' }), or a grid in the editor for a collection made here; a name Studio does not ship draws a document
  • a table's columns, its search box and its sort options are the resource's fields
  • a form is the field list, one input per kind
  • the API Explorer is the registry's routes, with their real schemas
  • the builder's block palette is the registry's blocks, with their nesting rules
  • a block's properties panel is its own fields, drawn by the same inputs as a resource form — plus the seven universal design controls every block has (SPEC.md §61)
  • the backgrounds those controls offer are the colours the generated stylesheet declares — a public artefact, so a person who may edit a block's design needs no permission over the theme — and Design is the five groups of the document that stylesheet is rendered from: Studio decides what no token means (SPEC.md §62)

So a resource() or a block() added to an application appears here with no Studio change at all. Every write goes to a command through the API — never to the database, and never past a policy (SPEC.md §14, §58). That includes every builder operation: undo and redo are revisions.undo and revisions.redo, and an agent can call them.

How it is drawn

design_handoff_studio_redesign/ in the repository root is the visual source of truth, and this application is built from it. What lives where:

  • src/styles.css — the tokens. Every colour is the handoff's hex rather than a generated ramp: the palette is a fixed set of neutrals with one accent, so naming a colour in a perceptual space would only put a rounding error between the design and the screen. Base type is 13/1.4615, so text-base is 13px, text-sm is a 12px caption and text-xs is an 11px overline — not Tailwind's default scale.
  • src/ui/index.tsx — the design system and the forms kit as one small vocabulary: buttons, fields, switch, checkbox, radio, segmented, badges, banners, skeletons. Anything a screen needs that is not here belongs here first.
  • src/ui/overlay.tsx — menus, dialogs, toasts and Picker. Every menu is position: fixed against the viewport and flips above its trigger when the space below is short: the tables and panels are scrollers, and a menu positioned inside one is clipped by it. Picker is the one dropdown that is not a native <select> — the Kind dropdown, where every option is an icon, a machine name and a sentence about what a value of that kind is, and <option> holds text.
  • src/ui/layout.tsx — the shape of a screen: a header that stays, one scroller, and a footer or save bar pinned to the panel.
  • src/app/shell.tsx — the 52px chrome bar, the 240px sidebar and its 56px rail, and the white content panel every screen but one is drawn inside.

The page builder is that one screen. It is a mode rather than a page, so it sits outside the shell — see shellRoute in src/app/router.tsx — and takes the window edge to edge: its own chrome bar, a 280px rail, the page as a sheet on the canvas, and an inspector floating over the canvas that collapses to a pill when nothing is selected.

Four of the handoff's screens are deliberately not built, and each for the same reason: the application has no command behind them. Sign-in draws one of its five states, because @assemora/auth has no second factor, no reset link and no SSO, and no length to attach to "keep me signed in". The Settings screen is not here at all: its ten groups describe an editorial workflow, a plan, builds and deploys that no package declares, and a settings form whose Save reaches nothing is worse than an absent one. The dashboard takes four of the handoff's thirty-five widgets — the ones the Schema Registry can fill — because a chart with no analytics port behind it is a plausible-looking fiction. They arrive with the commands they need.