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

handpress

v0.6.2

Published

Edit your hand-written static site in place, in the browser. Saves are git commits.

Readme

Handpress

Let a client edit a hand-written static site — the real pages, in place, in the browser. No CMS, no build step, no database. Every save is a git commit.

test npm license agent skill

➜ Try the demo — no sign-up; it's a real page and you really edit it

📖 Documentation

Editing text in place

visitor   →  your static site, exactly as you built it

editor    →  /admin  →  sign in with Google
          →  clicks the page and types
          →  Save    →  one commit  →  live

Why

Most CMSs ask you to break a design into templates and move the words into a database. That is the right trade for a publication with a thousand posts. It is the wrong trade for a brochure site of ten careful pages, where the layout is the product and the owner wants to fix a date or swap a photograph twice a year.

Handpress keeps the HTML as the source of truth and teaches the browser to edit it.

  • No templating. Your markup is untouched except for a small data-e key per editable element.
  • No database. Files in git. Before Save the editor has its own Undo; after it, git revert. Backups are your repository.
  • No build. One JavaScript file, loaded only for signed-in editors. Visitors download nothing.
  • Clean diffs. A changed sentence is a one-line diff you can review in a pull request.
  • The design can't drift. New entries are copies of existing ones, so a client cannot invent a layout.

What the editor does

| | | |---|---| | Text | Click and type. Bold, italic, links (address, text, new tab). Colour, size and font for selected words or a whole block. Pasted text arrives plain. | | Blocks | Hover any repeating thing — cards, list items, whole sections, buttons — to duplicate, move, delete, style, edit or follow its link. + adds after it: a new paragraph, heading or button, or anything copied or removed, from any page. | | Drafts | Unsaved edits are kept in the browser through Preview, page changes and reloads, with a counter. Undo / Redo. One Save commits every edited page. | | Drop-downs | Click a <select> to edit its options (label and value). | | Images | Click to replace (resized in the browser first) and edit the alt text. | | Video and audio | YouTube embeds take a new link; audio players take a new file (mp3, m4a, ogg). | | Shared blocks | Edit the header or footer once; it is written to every page. | | Page settings | Title, meta description, and the text shown when the page is shared. | | Site data | A form over your JSON data file, for lists the site renders from data. Lists on the page get handles too (move, duplicate, edit, remove, add, add images), and redraw as they change. | | Guide | Tips for each page, with a Show me button; opens by itself the first time a page is edited. | | AI assistant | One panel you can drag anywhere. Pick any block on the page (a heading, a card, a whole section, the empty canvas) and tell it what to do: rewrite the words in place (layout and keys kept), redesign it (layout may change), build new content inside it, or add a block after it. Every result shows as a preview and as HTML you can edit by hand before Apply, and Edit HTML opens any block's own HTML. The AI is given the page's CSS (custom properties, element and class rules in use) and its HTML outline, so it writes with your design. Free models through OpenRouter, with the model picked in the panel (opencode Zen is built in but hidden until it accepts calls from web pages). Your own key, stored encrypted in the browser and sent only to the provider you chose. AI edits undo, draft and Save like any typed change. | | New pages | With newPages on, New page makes an empty page (your head, header and footer around a blank canvas): click the canvas and build it section by section with AI, or start from a starter kit (landing page, studio / portfolio, the bakery example) as is or adapted by the AI. Starting from nothing? npx handpress blank public/index.html, or --kit landing (studio, bakery) for a keyed first draft. |

Duplicating and reordering list items The site data panel

Quick start

npm i -D handpress

npx handpress install ./public                                  # editor.js + admin.html
npx handpress keys "public/*.html" --global "header, footer"     # add the editing keys

Then one line in your site's own JavaScript, so visitors never load the editor:

if (/(?:^|;\s*)ed=1/.test(document.cookie)) {
  var s = document.createElement('script'); s.src = '/editor.js'; s.defer = true;
  document.head.appendChild(s);
}

And a back end. On Cloudflare, point a Worker at src/worker.js:

"vars": {
  "GITHUB_REPO": "you/your-site",
  "GITHUB_BRANCH": "main",
  "SITE_DIR": "public",                            // the repo folder holding the site ("" if it's the root)
  "GOOGLE_CLIENT_ID": "…apps.googleusercontent.com",
  "EDITOR": {
    "pages": ["index.html", "about.html"],        // the only pages that can be edited
    "dataFiles": { "data.js": "Opening times" },   // window.NAME = { …valid JSON… };
    "globalBlocks": ["header", "footer"],          // edited once, written to every page
    "uploadDir": "uploads",
    "forceVisible": "[data-reveal]",               // if you animate things into view
    "newPages": false                              // true: editors may create name.html pages
  }
}
wrangler secret put GITHUB_TOKEN     # fine-grained, this repo only, Contents: read and write
wrangler secret put SESSION_SECRET   # openssl rand -hex 32
wrangler secret put ADMIN_EMAILS     # comma-separated Google addresses allowed to edit

Create a Google OAuth client (Web application), list your site's origin under Authorized JavaScript origins, deploy, and visit /admin.

Not on Cloudflare? The PHP half ships with it, and saves publish instantly because the files are right there:

npx handpress install ./public_html --php     # api.php + Apache templates + sample settings

Step-by-step guide for cPanel hosting · notes for other hosts

How it works

| | | |---|---| | src/annotate.mjs | Run over your pages once. Adds data-e keys so the editor can match an element in the live page with the same element in the file. | | src/editor.js | The browser half. Fetches the page's source, keeps it as a second DOM, and applies each edit to both. | | src/worker.js | The server half. Google sign-in, a strict allowlist, one commit per save. |

The two-DOM trick is the heart of it. A live page has been rearranged by your own JavaScript: lists rendered, counters animated, classes toggled. Saving what is on screen would bake all of that into the file. So the editor edits the source by key and writes that; the live page is only a preview.

Every change is a small operation kept in the browser until Save, so unsaved edits survive Preview, other pages and reloads (they are replayed on load), and Undo / Redo are just dropping and restoring the last one. Save replays each edited page's operations onto the current file and commits everything in one go.

Security

The trust boundary is the admin list: anyone on it can change any editable file, and editing HTML means they could insert a script. Keep it short.

Everything else is closed by default:

  • Any path not in pages, dataFiles or uploadDir is refused for reads and writes, including for a signed-in admin.
  • Sign-in needs a Google ID token that Google validates, plus a match on the admin list. There is no password to guess.
  • The session cookie is HttpOnly, Secure, SameSite=Strict — which is also why no CSRF token is needed — and writes must be JSON, which a cross-site form cannot send.
  • Every save carries the hash each file was loaded at. If someone saved in between, the whole save is refused rather than overwriting their work.
  • Uploads are limited by extension and directory. On a host that executes files, also deny execution in the uploads directory (see docs/other-hosts.md), and test it.
  • With newPages, editors may also create top-level name.html pages (never admin.html). A new file cannot overwrite an existing one: a file sent without a hash is refused if it exists.
  • AI keys never reach your server. They are kept in the browser, encrypted with AES-GCM under a key the browser generates and stores as non-extractable (IndexedDB), so a copy of the browser's storage (a synced profile, a backup, an extension reading localStorage) yields only ciphertext, useless on any other machine. Requests go straight from the browser to the chosen provider. Script running on the page itself could still use the key; that is the same trust boundary as above, so keep the admin list short.
  • The only outside hosts are accounts.google.com (the sign-in button), oauth2.googleapis.com (checking the sign-in token) and, for the Worker, api.github.com (reading files and committing saves; GITHUB_API overrides it). The AI assistant, when an editor uses it, calls openrouter.ai from the editor's browser only. The PHP half writes to disk and never calls GitHub. There is no telemetry.

Working on Handpress

npm install
npm test            # sessions, the allowlist, the file checks, the keying script
npm run example     # the sample bakery site on wrangler dev
npm run demo:build  # regenerate demo/ from the example

npm run test:browser drives the example in a real browser — the guide, list handles on the data-drawn markets with a live redraw, copying a section, text and shared-block edits, duplicate, undo / redo, a reload mid-draft, the data file, save — against a stand-in GitHub, then checks what was committed. It needs playwright-core, a local Chrome, and the two processes named at the top of test/browser.mjs.

Using it with an AI assistant

Handpress ships an agent skill — when it fits, how to install it, the failure modes, and what to verify before handing a site to its owner:

npx skills add bikramtuladhar/handpress          # Claude Code, Cursor, Codex, OpenCode, and ~75 more
npx skills use bikramtuladhar/handpress@handpress | claude    # or use it without installing

Or copy skills/handpress/SKILL.md to .claude/skills/handpress/SKILL.md yourself — it is a plain Markdown file with front matter.

Status and limits

Early, but not theoretical: it runs a real client site, edited by its owner.

Deliberately missing: server-side drafts (unsaved edits live in one browser), approval workflow, multiple simultaneous editors (a second concurrent save is refused, not merged), and any notion of content types. If you need those, use a CMS.

MIT licensed. Issues and pull requests welcome.