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

@ai-matrx/detail

v0.1.170

Published

THE Detail primitive: ONE registration per record type — header, source health strip, fields, associations, history — wrapped once so it shows as a window (the default), a docked side panel, or a page, with the same content in all three. Owns the presenta

Readme

@ai-matrx/detail — the Detail primitive

Arman, 2026-09-17: "a core component that then shows up as a Page, a flexible drawer, and a window panel. The default is the window. … you just make a core component with some core rules, then wrap it in this reusable thing from the package, which then instantly gives you all 3 for free."

ONE registration per record type → three presentations for free, with the same content in all three. Keyboard: Escape closes, the arrows move between records ([ / ] are aliases), and Cmd/Ctrl+Enter saves.

| Presentation | What it is | What the host binds | |---|---|---| | window (the default) | Floating, draggable, minimizable; a bottom sheet on phones | shells.Window — the host's window manager | | docked | A resizable side panel docked to the right edge, no backdrop; a bottom sheet on phones | shells.Docked — the host's side-panel surface | | page | A full route body under the app header — the only presentation that changes the URL; offers the window back | shells.Page — the host's route header + scroll body |

Champions: Notion (side peek / center peek / full page, per-database "open pages in") and Linear (peek, keyboard-first). Bones matched, skin ours.

pnpm add @ai-matrx/detail        # every Matrx repo declares it "latest" (THE LATEST LAW)

Adoption status

matrx-frontend installs this package from npm at latest (adopted 2026-09-30); its former in-repo copy and the byte-parity guard that kept the two even are deleted. A fix to the primitive is made here, released, and reaches the frontend by version. The not-found state renders design-system's ErrorActions slot, so a host's error actions (the frontend's Alchemy menu) appear there without a host-specific body.

| Entry | Ships | "use client" | |---|---|---| | @ai-matrx/detail | the record-type contract, the presentation setting shape and its per-type override, the list-context trim with the final-URL byte budget, the deep-link spelling of every presentation, the row → fields formatter | no — pure, legal in a Server Component | | @ai-matrx/detail/react | the ports and provider, the core, the keyboard model, the loader and the health resolver, the one opener, the header halves, the body, the setting pane, the three presentations, the header geometry constants | yes | | @ai-matrx/detail/testing | the stub-port seat: makePortsWith(fn), mount, instance, clickByLabel, three stub shells, FILE_TYPE — the seat the package proves itself with, for a host to prove its binding with | yes |

The contract — one registration per record type

import type { DetailRecordType } from "@ai-matrx/detail";

const task: DetailRecordType = {
  type: "task", label: "Task", icon: TaskIcon, accent: null,
  entityToken: "task",          // doors · associations · history key off this; null = none
  load: (id, signal) => …,      // null = no single source → renders from the seed, and says so
  title: (row, seed) => …,
  fields: (row) => […],
  health?: (row, ctx) => …,     // the source health strip for a SYNCED record (sync or async)
  associationTokens?: […],      // null hides the section; omitted → the host default minus self
  history?: true,               // default: when entityToken is set
  extraSections?: (row, ctx) => […],
  Frame?: ({ ctx, children }) => …,   // wraps the body: surface runtime, right-click menu, …
};

Fixed sections, in order: source health strip (synced records only) · about · fields · associations (AssociationCardGrid under a PrimaryEntityProvider, from @ai-matrx/associations) · history (row versions) · extras · the "how details open" pane. A section that does not apply is absent, never an empty box.

The one opener: useOpenDetail()({ type, id, presentation?, seed?, list? }). Without presentation it asks the host's resolvePresentation(type) — the person's ui.detail.default_presentation, with ui.detail.presentation_by_type read first for that type. A resolver that raises is announced (the host's notify.error, naming the key) and the window opens. Nothing navigates away except page.

Binding the ports

Everything app-shaped arrives through DetailHostPorts (@ai-matrx/detail/react). Providers nest and merge: bind the light ports ONCE at boot so useOpenDetail works everywhere, and let each presentation's lazily loaded entry add its own shell and the record-type map right above the presentation, so the window shell never reaches a boot bundle. A missing port throws, naming itself.

| Port | What the host injects | Required | |---|---|---| | resolveType(type) | THE record-type map — one registry, never a second | per presentation entry | | usePresentationSetting(type) · resolvePresentation(type) · warmPresentation(type) | the settings ladder's answer for ui.detail.default_presentation and ui.detail.presentation_by_type (React face, awaited face, cache warmer) | yes | | reReadPresentation(type) | a FRESH ladder read after a write, naming the rung that answers now | optional | | savePresentation({ presentation, forType, clear? }) | the ONE writer of the setting and its per-type map entry; a refusal comes back as { ok: false, reason } | optional — absent ⇒ the pane is absent | | open · close | the in-place presentations (window / docked) on the host's overlay system | yes | | navigate.pageHref · toPage · back · canGoBack · toRecordHome | the page route; canGoBack answers "did THIS tab push the page" and toRecordHome replaces the page with the record's home — never a raw back() into an empty history | yes | | shells.Window · Docked · Page | the chrome; the package fills title, titleNode, actions, children, onClose (+ onBack for the page, + list for the window's deep link) | per presentation entry | | doors.RecordDoors · RefCell · tokenFromColumnName · isUuidValue · hasDoor | the host's entity doors (open / new tab / peek); hasDoor decides whether the absent state may claim "the controls above still open it" | yes | | associations.defaultTokens · canAnchor | which tokens the associations section shows and which may anchor one | yes | | history.list(token, id, signal) | row versions | yes | | notify.error · success · copyText | the host's toast and clipboard | yes | | reconnectSource(ref, source) | the same Reconnect the connector rows offer, for a synced record's strip | optional | | remedy.typeMap | where the developer goes to register a type — the console remedy names it | optional |

The knobs the package READS THROUGH PORTS and never resolves itself: ui.detail.default_presentation (enum window|docked|page, organization → user), ui.detail.presentation_by_type (json map, same ladder), ui.detail.list_context_max_ids (organization; the host resolves it and passes the number into encodeListQuery / detailListToUrlArgs, which clamp it to DETAIL_LIST_CONTEXT_MAX_IDS_CEILING). Their rows are seeded by matrx-frontend's migrations.

Deep links

Window and docked deep links ride the host's ONE panel-URL mechanism under the type key detail, instance type.id, arg as: ?panels=detail:task.<uuid>:as-window / :as-docked, carrying the list the record was opened from (l / i / lt). The page is the host's route (/detail/<type>/<id> in matrx-frontend) with ?l=type.id,…&i=<n>&lt=<total>. Both spellings are cut by ONE trim (trimListContext) against ONE budget — 8,000 characters of the FINAL serialized URL, with what the rest of the address costs reserved — and the record says when the list was trimmed. . : , inside a type token or an id are escaped, so an external id such as sc-domain:example.com round-trips as the same record.

Keyboard

useDetailKeyboard, bound on every slot the presentation fills (header and body): Escape closes from any control except an open menu / select / combobox and a text field with uncommitted edits; ArrowUp/Down/Left/Right move between records only when the body is not the scroll target, [ / ] always move; Cmd/Ctrl+Enter saves what a section registered — and says there is nothing to save when nothing did.

Testing a host binding

import { makePortsWith, mount, instance, StubPageShell } from "@ai-matrx/detail/testing";
const makePorts = makePortsWith(jest.fn);   // or vi.fn — the seat takes the runner's factory
const ports = makePorts({ shells: { Page: StubPageShell } });
const m = mount(<DetailPagePresentation data={instance()} />, ports);

Every port is a mock, so a test asserts what the core ASKED THE HOST TO DO — which is where every recorded defect of this primitive lived.

What is deliberately NOT here

  • The host's window manager, side panel and route header — they are shells, bound as ports.
  • The settings ladder, the toast, the clipboard, the router, the entity registry — ports.
  • An icon library: the seventeen glyphs the primitive draws are inlined SVGs (Lucide-derived, ISC). A record type's own icon is host data on its registration.
  • Tailwind CSS generation: the components are styled with utility classes. Register the built package with the consumer's Tailwind scan (@source "../node_modules/@ai-matrx/detail/dist"; in matrx-frontend's globals.css, the design-system precedent).

Implementation truth, file map and change history: FEATURE.md beside this file. Packaging law: common-docs/policies/typescript-package-standard.md.