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/records-ui

v0.116.17

Published

The thirty-one canonical screens of the AI Matrx record store: grid, generated record form, field editor, table settings, relation picker, peek, views, import/export, custom fields, action inbox, history, comments, forms, charts, documents, record chat, A

Readme

@ai-matrx/records-ui

The platform's primary record UI components. Every component here reads and writes ONLY through @ai-matrx/records, renders with @ai-matrx/design-system's own primitives and data table, and carries @ai-matrx/alchemy's menu where a person reaches for one.

There is no bespoke per-feature record UI in this platform. A feature that needs a grid, a form, a picker or a peek mounts one of these — and any one usage is a few lines.

Bind once

import { RecordsProvider } from "@ai-matrx/records/react";
import { RecordsUiProvider } from "@ai-matrx/records-ui";
import "@ai-matrx/design-system/styles.css";

<RecordsProvider config={{ dataSource: supabase, organizationId, actor: { actor: "user", user_id } }}>
  <RecordsUiProvider value={{ rights: (table) => myRights(table), density: "condensed" }}>
    {children}
  </RecordsUiProvider>
</RecordsProvider>;

rights is the port that decides whether a person sees the administering controls. Unbound, it answers "no" WITH A REASON, and every such control is ABSENT — this package ships no disabled variant of any of them.

Then, a few lines each

import {
  Grid, RecordForm, FieldEditor, TableSettings, RelationPicker,
  Peek, ImportWizard, ExportMenu, CustomFieldsSection,
} from "@ai-matrx/records-ui";

<Grid tableId={id} onOpenRecord={setPeeking} onAddField={openSettings} />           // records as rows, fields as columns
<RecordForm tableId={id} />                                                          // create, generated from the Field records
<RecordForm tableId={id} recordId={recordId} />                                      // edit, with the conflict shown when someone wins
<Peek tableId={id} recordId={recordId} onClose={close} />                            // any custom table's record, no per-table code
<TableSettings tableId={id} />                                                       // rename · reorder · retype · delete
<FieldEditor tableId={id} field={field} />                                           // one Field's type, rules and sensitivity
<ImportWizard tableId={id} onProposeField={propose} />                               // map columns, preview rows, propose the rest
<ExportMenu tableId={id} label="Deals" />                                            // CSV · XLSX · Alchemy
<CustomFieldsSection tableId={entityTableId} recordId={record.id} />                 // THE one line a standard entity page adds

RelationPicker is used by RecordForm automatically for any relation field; mount it directly when a screen needs just the picker.

Views, the queue, history and comments

<ViewBar tableId={id} onActiveView={setView} seed={[{ name: "By stage", subject: id, layout: "kanban", groupField: "stage" }]} />
<ViewSwitcher view={view} />                                                         // grid · kanban · calendar · gallery, one membership Rule
<ActionInbox tableId={id} seed={[...]} />                                            // approvals, assignments and agent proposals, one queue
<HistoryPanel tableId={id} recordId={recordId} />                                    // the timeline IS the record; undo is the store's soft delete
<CommentThread tableId={id} recordId={recordId} />                                   // a comment is a record
<FormBuilder tableId={id} seed={[form]} />  <FormRunner form={form} />               // a form is a view on a real Table
<DashboardCanvas tableId={id} seed={[dashboard]} />  <ChartBlock spec={chart} />     // every block is one `record_aggregate`
<NotifyRuleEditor tableId={id} />                                                    // a subscription is a Rule record over a saved view
<DocTemplate tableId={id} seed={[tpl]} />  <DocRender templateId={t} recordId={r} /> // tokens resolved by the store
<SignBlock tableId={id} recordId={r} render={render} />                              // the signature is checked against the exact bytes

Talking to a record, and letting an agent keep it filled

<RecordChat tableId={id} recordId={recordId} />                                      // the HOST's chat surface, this record as its context
<EnrichPanel tableId={id} recordId={recordId} />                                     // per-field AI fill, freshness and provenance

RecordChat ships no chat UI. AI Matrx has exactly one — matrx-frontend's AgentConversationColumn — and it reads that app's Redux store, so a package that imported it would stop being a package and a second one here would drift within a month. RecordChat builds the part only the record store can build: the record read through the read door, each value carrying its Field's unit and format (the agent is given $48,000, never a bare 48000), the fields the store MASKED named out loud, and the secret and on_demand ones held back with the reason. Then it hands that to the chat port:

<RecordsUiProvider value={{
  chat: (ctx) => {
    const { conversationId } = useAgentLauncher(agentId, {
      surfaceKey: ctx.surfaceKey,
      runtime: { context: Object.fromEntries(ctx.entries.map((e) => [e.key, e.value])) },
    });
    return <AgentConversationColumn conversationId={conversationId} surfaceKey={ctx.surfaceKey} />;
  },
}}>

Resolve the agent through a Mandate, never a pinned UUID. Unbound, the panel is absent with its reason and draws no composer.

EnrichPanel reads which Fields an agent keeps filled from the Fields themselves (source = agent) and how often from review_interval_days, and dates each value off its own envelope — never off the row's updated_at, which would call every field stale because one of them changed. The fill itself goes through the server's records tool, because a write stamped actor agent on behalf of a person can only honestly be made by the server:

<RecordsUiProvider value={{
  enrich: (ask) => fetch("/api/ai/tools/execute", { method: "POST", body: JSON.stringify({
    tool_name: "records", surface: "records-ui:enrich",
    arguments: { action: "record_write", record_id: ask.recordId, values: {/* the agent's answers */} },
  })}).then((r) => r.json()).then((r) => ({ ok: r.ok, message: r.output })),
}}>

Two live switches sit in front of that call today and the panel names both rather than spinning: the knob custom/code_paths_enabled resolves false, and the records row in tool.binding is is_active = false.

Work: checklists, bookings and a phone with no signal

<ChecklistRunner tableId={id} seed={[{ name: "Onboarding", subject: id, steps: [{ title: "Confirm the address" }] }]} />
<BookingSlots tableId={id} form={bookingForm} availability={{ fromHour: 9, toHour: 17, everyMinutes: 60, days: 5 }} />
<CaptureSheet tableId={id} readingField="amount" noteField="title" />

ChecklistRunner makes every step a real record — assignable, commentable, exportable, historied — and the whole graph is created in ONE act (custom.work_template_instantiate judges it before writing any of it), so a half-made checklist is not a state this store can be in. The states and the moves offered come from custom.work_states(); a move the store does not list as next is not shown greyed out, it is not there.

BookingSlots holds the slot first and takes the details second — taking details first is how two people fill in a form and one of them finds out at the end. One hold per slot is a UNIQUE INDEX in the database, not a check this screen makes, so the loser of a race reads the database's own sentence naming the index that refused it. Holds live in a slots Table the component declares through custom.work_slots_declare; if the store cannot build that index it refuses, and this screen shows the refusal rather than a picker that would sell the same hour twice.

CaptureSheet is the offline capture contract, and both halves matter: the client mints the id the moment a person hits Capture — offline, before the first attempt, never re-minted on retry — and the store dedupes on it, so a reconnecting phone that fires the same capture thirty times writes one record and gets the same id back every time. Bind captureQueue to keep the queue across a reload; unbound, it is in memory for the life of the page and says so.

Outsiders: a portal, a public page, an embed

<PortalShell tableId={id} form={intakeForm} />                                       // their list, their record, their form
<PublicViewPage slug={params.slug} />                                                // a signed-out visitor's page
<EmbedFrame tableId={id} formId={formId} embedUrl="https://app.example/embed" />     // issue the token
const { binding, error } = useEmbedHandshake({ secret, requiredMode: "read" });       // and verify it, inside the iframe

PortalShell asks iam who the person is rather than deciding for itself: "external" means no membership of a non-personal organization, because signup mints everyone a personal one and the other reading makes nobody external. What they reach is iam.external_principal_reach — exactly the resource ids Visibility handed them, one at a time, never "their organization's records".

PublicViewPage renders a publish binding, and a share is not a publish: an address nobody made, one that was revoked, and one whose lane closed all resolve to the same nothing, because an address that answered differently for a revoked binding would be an existence oracle. Every read carries the store's "not scanned" note (D-15) — one function, so the page, the API and the admission row cannot drift into three different admissions. Reaching the published happy path needs a claimed namespace and a verified publisher, which is a person standing behind what goes on the open internet, not something a screen may forge.

EmbedFrame mints a token through custom.anon_token_issue, shows the secret once (only its digest is stored), and states the rules before the refusal: a token carries one decision — read or write, never both — and its origin list is required and matched exactly, so example.com.evil.test fails a check a suffix match would pass.

What every one of them does the same way

  • The store's own sentence. A refusal is never re-worded. "Someone else changed this record while you were working on it" is what the person reads, because it is what the store said, and the conflict comes with it field by field.
  • Absent, never dead. A control a person may not use is not rendered. There is no disabled "+" and no greyed settings panel anywhere in this package.
  • A Field decides its editor. The thirteen parity types come from the store (custom.parity_field_types()), generated into @ai-matrx/records. The unit and the format are on the Field because they change what the value MEANS.
  • Nothing is dropped quietly. An unmapped import column is shown and offered as a field proposal; a list that is not live says so; a door that does not exist yet is named, with the lane that owes it.
  • Dense, one-row headers, light and dark, iOS-safe, through the design system's own tokens — this package sets no colours of its own.

The demo

pnpm demo runs every component against the one live store, headless, on this lane's own port (127.0.0.1:3005), and stands its own table, fields and records up the first time it runs. Its data seam is the server lane the platform uses today, in one transaction per request that opens the product switch, acts as the test identity ([email protected]) through the store's own doors, and sets every switch back before committing — so no other session ever observes one open. pnpm test renders those same pages in jsdom against that server and asserts what a person would see.

It reads the live store deliberately: custom.anon_*, the embed token, the external principal and the publish binding exist only there, and a demo pointed at a rehearsal copy would answer door_absent for a third of this package and call it a finding.

The thirty-one components

| | | | |---|---|---| | Grid | RecordForm | FieldEditor | | TableSettings | RelationPicker | Peek | | ViewSwitcher | ViewBar | ImportWizard | | ExportMenu | CustomFieldsSection | ActionInbox | | ProposalRow | HistoryPanel | CommentThread | | FormBuilder | FormRunner | ChartBlock | | DashboardCanvas | NotifyRuleEditor | DocTemplate | | DocRender | SignBlock | RecordChat | | EnrichPanel | ChecklistRunner | BookingSlots | | CaptureSheet | PortalShell | PublicViewPage | | EmbedFrame | | |

Commands

| Command | What it does | |---|---| | pnpm demo | Every component, live on the rehearsal branch, headless | | pnpm test | The demo pages asserted headlessly | | pnpm build | tsup → dist (esm + cjs + d.ts), then the client boundary stamp | | pnpm check:package | build · exports agreement · publint · tarball canary |