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

@miadi/annotate-core

v0.2.0

Published

Annotate a document by block or by exact words, signed, and hand the review on, without a framework: the vocabulary, the annotator's behaviour as plain functions, quote and author resolution, an HTML-string renderer, a vanilla DOM mount with the iOS-safe

Readme

@miadi/annotate-core

Annotate a document by block or by exact words, sign each mark, see everyone's marks, and hand the review on. No framework, no dependencies.

In the Miadi stack, this is where a person's judgment on agent work is recorded. A reviewer marks the parts of a reply or a document that matter, each mark says who made it, and the finished review can be handed back to the agent that wrote the text.

Works with:

  • @miadi/annotate-ui: the React host over the same core. It renders the same markup, and a parity test holds the two together.
  • @miadi/tide: fetches an agent's reply from a terminal pane and delivers the review back to it.

Entry points:

  • @miadi/annotate-core: the vocabulary, the annotator's state model, resolveQuote, resolveAuthor, and renderAnnotatorHtml, an HTML-string renderer
  • @miadi/annotate-core/dom: mountAnnotator for a page without a framework, plus captureSelection, autoGrow and authorMemory
  • @miadi/annotate-core/http: httpAnnotateTransport, a transport over routes the host names
  • @miadi/annotate-core/styles.css: the stylesheet. Colours come from the theme as CSS custom properties.

Nothing here holds a token or knows a URL of its own.

A page without a framework

<link rel="stylesheet" href="/vendor/annotate-core/annotator.css">
<div id="annotator"></div>
<script type="module">
  import { mountAnnotator } from "/vendor/annotate-core/dom.js";
  import { httpAnnotateTransport } from "/vendor/annotate-core/http.js";

  const transport = httpAnnotateTransport({
    urls: {
      load: "/api/annotate/load",
      mark: "/api/annotate/mark",
      marks: (doc) => `/api/annotate/marks?doc=${encodeURIComponent(doc)}`,
    },
  });
  const annotator = mountAnnotator(document.getElementById("annotator"), {
    transport,
    authorKey: "my-app.annotate.author",
  });
  annotator.load();
</script>

dist/ imports its siblings with relative .js paths, so serving the package's dist/ directory as static files is enough (express.static(dirname + "/dist")).

The transport

interface AnnotateTransport {
  load(): Promise<AnnotateDocument>;           // { id, text, blocks, reopened?, note? }
  mark(request: MarkRequest): Promise<MarkOutcome>;
  marks(): Promise<StoredMark[]>;
  deliver?(options: { submit: boolean }): Promise<DeliverOutcome>;
}

Write your own, or use httpAnnotateTransport. It sends { doc, ...request } to the mark route and treats a JSON body carrying error as a failure. Without deliver, the deliver bar is not rendered.

Selecting words on a phone

iOS clears a text selection as soon as anything outside it is touched, so a button cannot read the selection when it is tapped. captureSelection records it while it is made, keeps it for graceMs (default 1500) after it collapses, and returns a QuoteSelector (exact, plus up to 32 characters of prefix and suffix from the block). The mount wires it for you. Pass graceMs to change the window.

On the server

  • resolveQuote(text, selector) returns the one range the selector names, or { ambiguous: n }, or { missing: true }. It never guesses.
  • resolveAuthor({ signed, asserted, typed, entered }) applies one precedence: a signed token, then an identity the host vouches for, then a valid typed author. A signed request whose body names someone else comes back with refused, unless it says entered: true: then the typed name is kept and the signer is recorded as the one who carried it. Evidence (tokens, headers) stays with the host.
  • The decision carries basis (signed, asserted or typed: how the author was known) and by (whose request carried the mark, or null), kept apart the way a commit keeps its author apart from its committer. A host stores them on the mark as author_basis, author_by and author_by_name; the annotator shows a typed name as "Ava (entered by William)" or "X (not verified)". Signed in, a person reaches the typed fields with "enter another name", and the mark waits until both fields are filled.

Theme and labels

theme takes colours and a monospace stack (DEFAULT_THEME is dark, with a gold accent). labels takes every visible string. Functions such as deliver: (n) => ... receive their counts. Every text colour of the default theme reads at 4.5:1 on every surface, and a test holds it there.