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

@dshkit/design

v0.1.0

Published

A design surface for DeepSeek Harness: the agent writes a self-contained index.html into the directory it is already working in, and this puts it on the screen beside the conversation.

Readme

dshkit-design

A design surface that lives where the agent already is — and leaves its output where the coding surface can pick it up.

Describe the interface you want. The agent writes a self-contained index.html into the workspace directory. This plugin puts it on the screen, beside the conversation, without you leaving.

Status: the host and browser halves are implemented. The browser registers both as a public dshkit.workbench.v1 consumer and as a shell.overlay fallback for hosts without the carrier; it never renders both doorways at once. Run pnpm check for the current type/test/build result.

Why this repository is separate

It is the first of a family. There will be others — a coding surface next — and each one is a demonstration-grade external project: its own repository, its own release, depending on dshkit the way a stranger would.

That separation is the point, not a packaging preference. This package consumes the frozen, types-only @dshkit/sdk; it does not import the carrier's internal registries or manufacture its branded identities. The host mints task and output ids, holds task state while the workbench is inactive, admits state transitions, and stamps notification source and time.

The original implementation deliberately lived only on shell.overlay while the seat contract was still closed. Its measured costs informed that contract. Now this repository is the first external consumer of the public seat. The old overlay face remains only as a no-carrier fallback: when the workbench face mounts it claims presence and the fallback renders nothing, so one plugin never puts two doorways on one screen.

The shape, and the one boundary that carries it

The artifact is a design draft, not an implementation: one file, no build step, no network dependency. Everything else follows from that line — a single self-contained document is what makes a sandboxed preview possible at all, and what keeps this surface from turning into a second frontend toolchain.

The workbench itself is small on purpose: a sandboxed frame, a few viewport widths, a way to get the file out. The work is the agent's. This is the window onto it.

The part that is specific to dsh, and it is load-bearing

The obvious way to build this — the way the reference implementation this is modelled on does it — is to have the browser read the file. In dsh the browser half cannot. The client-facing API surface has no readFile: it can list directories (host.listDirectory, and only under the browse capability), never file contents.

⚠️ Corrected before anything was built on it. That paragraph was first written as "the browser cannot reach the filesystem", and that is false: the browser already knows the directory — session summaries carry cwd, and the shipped workspace UI reads it every day. What it cannot get is contents.

The distinction is not pedantry, it decides the shape. From "the host completes what the browser lacks", letting the browser send the path it already knows reads as the natural design. From the reason that actually survives — knowing a path is not authority to name one — it cannot be written down at all. The host half is not keeping a secret; it holds the only authority.

So this plugin needs a host half, and the two halves have to talk. That path exists and has been measured end to end — a third-party dual-half plugin registering its own endpoint and calling it from the browser, with a per-run nonce proving the answer came from the host and a deliberately-undeclared neighbour method answering 404 as the control.

Which makes this more than a demo. dshkit's stated direction is that what each surface produces flows to the others, and that the carrier of that flow is the workspace directory — a real path shared by every session under it. That claim has been checked against the architecture and has never been walked. This is the first thing that walks it: the agent writes a file, the host half reads the directory it was already working in, the browser half shows it, and a coding session in the same workspace opens the same file with no bus in between.

Settled boundaries

  • What the host half is allowed to read. Taking a path from the browser is the wrong shape; upstream faced the same choice and took an id instead, resolving the path itself. Ours is narrower than "any file": the host mints ids only by enumerating .design/*.html under the selected session's cwd.
  • How long work survives. The seated workbench uses host-owned tasks: work continues while the pane is inactive in the same dsh run, and exposes cancel/retry/recover according to the host state. Cross-process persistence is not claimed.
  • How completion is announced. A completed run submits through the pane's notification edge and targets the last host-minted output, so a click lands on the concrete draft. A missing target becomes an explanatory state, not a blank frame.
  • Whether the preview polls or is told. It does neither: the visible panel refreshes on open, on an explicit refresh, and when a seated workbench becomes visible again. Background review uses a non-rendering request path, so it does not steal the user's current preview.

What replaces the first host half

Two endpoints, both taking a session lookup parameter and neither declaring a scope:

list(session)      → { id, label, bytes, modifiedAt }[]
read(session, id)  → the document

The ids are minted here. The browser has no way to construct one — every id it holds came back from a list() this half produced. So "read something that was never enumerated" is not a request that gets refused, it is a sentence that cannot be formed. That is a smaller attack surface than validating a name: what has to be right is the enumeration, not a guard.

It is also not an invention. Upstream ships the same shape — agentPreset.list and agentPreset.read, where the read takes an id and the host resolves it itself — so this is a copy of a decision already made, not a new one.

Three things are settled and worth stating because each was nearly got wrong:

  • The directory comes from the session, session.header.cwd, the way upstream's own file search resolves it. cwd is optional there, and when it is absent the answer is a refusal that says so — not a fallback to the process's directory, which would quietly enumerate somewhere the user never meant.
  • No scope on the descriptor. The obvious model to copy declares scope: { context: 'agent' }, which lets the browser omit the id. A neighbour on shell.overlay is a root context, and the client-side identity for a root context is undefined — so every call would throw, while the error text sends whoever reads it looking at cordis contexts. The session id is passed explicitly instead.
  • sandbox="allow-scripts" without allow-same-origin. Granting both is granting neither: a document with both can remove the attribute itself.

Not in the first version, deliberately: no write (the agent has write tools, and a second writer is a second authority), no watch (the panel cannot be told when it is invisible, so a watcher would only spin in the background), and no relaxation of the sandbox.

What being a neighbour costs, measured

The list below is the point of this repository. It is filled in from admission runs against a real harness, never from reading the carrier's source — a cost nobody has paid is not evidence.

First reading, 2026-08-20, one boot with the host driven into an approval wait:

[FAIL] announces-the-wait — the host was waiting (approval) and nothing on the
       declared surface changed — an agent stopped there and this screen says
       so nowhere

⛔ That reading was withdrawn and then re-taken. The instrument that produced it drove the wait by writing pendingInteraction into the host's session list store — which is a projection rebuilt wholesale from the session manager on every notification, so the stimulus could be erased before the screen was read. A surface is quiet when nothing is waiting, and the check called that quiet "an agent stopped there and this screen says so nowhere". The words above are therefore words the instrument could produce on its own, and the qualifier originally recorded here — deterministic, not intermittent — was measured with that instrument and is retracted.

Re-taken 2026-08-20 with the stimulus injected into the manager itself, where a projection carries it instead of erasing it, and with each of the three readings now carrying the host store's own answer taken in the same call:

[FAIL] announces-the-wait — the host was waiting (approval) and nothing on the
       declared surface changed
  [attend] host store while the surface was read: 1 session(s) waiting

Three runs out of three, every one of them with a session genuinely waiting at the moment the screen was read. The finding stands, and the difference from the first version is not the verdict but that this one can tell "the surface said nothing" from "there was nothing to say".

A neighbour has none of the machinery that answers this: reading pendingInteraction across every session, choosing the most consequential of the three statuses, and announcing without seizing. The carrier does all of it and a neighbour inherits none of it, so every demonstration project in this family will fail this check on its first run, and each one will either rebuild that machinery or ship a surface that can hide an agent waiting on the user.

⚠️ Read with its shape: this is the neighbour driven the way the carrier is driven — panel opened, no session staged. A run that also stages a session moves the panel's own contents for reasons that have nothing to do with the wait, and the check correctly declines to attribute that (unknown, not fail).

Everything else the same run measured is green: it installs, boots, seats without shadowing anyone, crashes nothing, and carries a label that survives minification.

⛔ In-place correction, 2026-08-20. The sentence above used to also say gives the screen back when asked, and that run had not measured it. No runner had ever passed --exit, so returns-the-screen came back unknown — "no way out was declared, so whether the user can get back out of this app is unanswered" — and it is a BLOCKING check. For a full-screen app the hole was wider than one line: takes-no-protected-point answers n/a and defers in those words, see returns-the-screen, which is the question that has an answer. So whether the host got its screen back had no reading at all behind it, under a paragraph that said it did.

The way out had been on the close button the whole time (data-dshkit-design-exit, src/client/Stage.tsx). Declared and measured, 2026-08-20:

[ok] returns-the-screen — after [data-dshkit-design-exit] the host has every
     region it needs back (1 body-level node(s) remain, see portal-disclosed)

⚡ Not a ledger entry, and that is the point of writing it down. A neighbour pays nothing here; what was wrong was the instrument's shape and a sentence that ran ahead of it. This ledger's rule was always "only what was run, never read off the carrier's source" — this is the same rule failing in the direction nobody watches, where prose fills a gap a reading left open.

The shape is no longer carried in prose alone. dshkit/scripts/measure-neighbour-cost.mjs holds it, prints it above every reading, and refuses to let a ledger entry be written from a run in which a blocking check applied and was never asked (exit 2, and it names the instrument rather than the neighbour). n/a is deliberately left alone: it means no claim was made, which is an answer.

Second entry, 2026-08-20 — paid while standing tsc up over src/, not inferred from anything:

src/client/Stage.tsx(26,20): error TS2307: Cannot find module
      './stage.module.css' or its corresponding type declarations.

@dshkit/tsdown-client turns a CSS Module into a class map at factory time, so the build works and the type checker does not. The ambient declare module '*.module.css' that makes it check is a file the carrier already has and that a package outside that repository cannot import: the preset publishes the build, not the types the build implies. This repository therefore carries its own copy — and so will every neighbour after it.

Small, and recorded precisely because it is small. The case for opening the seat contract will not be one expensive thing; it will be a list of cheap ones that every neighbour pays over again.

How the browser half is checked, and what the check cannot see

pnpm run check type-checks, runs the tests, and builds. The tests that drive the panel render the real Stage into a real document and assert against the DOM — never against DraftsStore's snapshot, and never against a sentence lifted out of the component into a helper. That is not a style preference. The store has set loading and reading since the first version of this plugin and nothing rendered them: a test asserting status === 'loading' would have been green for the entire life of the defect, because the lie lived in the gap between the store and the screen.

Two pieces of scaffolding exist only so Node can get there. tests/support/ transpiles TSX (Node strips types but has never handled JSX) and installs a jsdom document before React is evaluated. Both files say what they cannot see, and it is worth repeating here:

  • no bundle. Modules are transpiled one at a time; tsdown is what ships. A bundler-only failure is invisible to these tests, which is why the build stays in pnpm run check.
  • no layout. jsdom lays nothing out, so position, z-index and pointer-events — the three properties that have actually bitten this plugin on a real overlay layer — are unobservable. Nothing here is evidence about them; those are measured on a real boot.

Run either half of the panel's own criteria on its own:

node --import ./tests/support/register.mjs --test tests/stage-progress.test.mjs
node --import ./tests/support/register.mjs --test tests/stage-latest-intent.test.mjs

The first pins what the panel says while it is waiting. The second pins who owns the screen when two answers are in the air: the most recently issued request owns every visible field, and a response that no longer holds the ticket writes none of them. The rule is stated where it is implemented, in src/client/drafts.ts.

Conventions

Source, comments and commit messages are English, matching dshkit. Chinese lives in the sibling design repository, not here.

License

MIT

Not affiliated with DeepSeek. dshkit is an independent community project.