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

@iloveagents/foundry-web-ui

v0.35.0

Published

React agent UI core for Foundry UI — chat, composer, AG-UI adapter for assistant-ui, tool cards, panels, sidebar, theme runtime, and UI stores.

Readme

@iloveagents/foundry-web-ui

React UI core for Foundry UI apps.

web-ui is the package that renders the actual agent experience: chat UI, composer, AG-UI adapter, message rendering, tool-call cards, side panels, sidebar, theme runtime, context pins, attachment handling, and UI stores. It depends on @iloveagents/foundry-web-primitives for leaf primitives (Button, Input, Field, Checkbox, Textarea, cn).

Install

pnpm add @iloveagents/foundry-web-ui @iloveagents/foundry-web-primitives

Usage

import {
  AGUIRuntimeProvider,
  Sidebar,
  ChatContent,
  ToolPanel,
  useAppStore,
  usePageTools,
} from "@iloveagents/foundry-web-ui";
import { cn } from "@iloveagents/foundry-web-primitives";

This package publishes built ESM JavaScript and .d.ts declarations. Host apps must include Tailwind v4 @source directives so classes used inside package components are generated:

@import "@iloveagents/foundry-web-ui/styles.css";
@source "../node_modules/@iloveagents/foundry-web-primitives/dist";
@source "../node_modules/@iloveagents/foundry-web-ui/dist";

Lists

Every list renders through one DataTable family (TanStack Table underneath): AND-of-tokens search, faceted filters with live counts (OR within a facet, AND across facets, array cells count per element, empty cells as "None"), sortable headers, column visibility, row selection, optional paging, optional grouping, and a URL codec that keeps q / sort / f_<column> / hide / page / size.

const table = useDataTable({ data: rows, columns, getRowId: (row) => row.id, pageSize: 20 });

<DataTableToolbar table={table} facets={[{ columnId: "status" }, { columnId: "priority" }]} />
<DataTable table={table} onRowClick={(row) => open(row.original)} />
<DataTablePagination table={table} />

Give a column filterFn: facetFilterFn (or the string "facet" in untyped code) to use it as a facet; meta.facetLabels names its values, meta.label names the column in the View menu, and DataTableColumnHeader makes a header sortable. Pass state + onStateChange (with parseDataTableState / mergeDataTableUrlState) to keep the whole list state in the URL.

A column says where it earns its place

const column: ColumnDef<Risk> = {
  id: "worst_case",
  accessorKey: "worst_case",
  meta: {
    breakpoint: "lg", // the narrowest LIST width it needs (not the window)
    showIn: "focus", // only when the list has the screen to itself
    pinned: "left", // frozen against the left edge while the rest scrolls
    defaultHidden: true, // in the Columns menu, off until asked for*
    align: "right",
  },
};

Widths are measured on the table itself, so a list beside an open detail pane sheds exactly the columns a narrow window would and focus mode brings them back — no media queries in the host. DATA_TABLE_MIN_WIDTH publishes the bands (sm 448 · md 672 · lg 896 · xl 1024 · 2xl 1280) for a host that derives them from a schema.

* useDataTable applies defaultHidden to its own initial state. A list whose state lives elsewhere (the URL) passes defaultHiddenColumns(columns) into that layer's defaults instead — a hidden-only URL records nothing when a column is shown, so re-applying the default on every render would undo the user the moment they bring one out.

Freezing is a run from the left edge: every column up to the last pinned one comes along, so a leading checkbox never slides out from under its rows.

That gating is render-time and deliberately not TanStack columnVisibility (which belongs to the user and rides in the URL), so anything that must agree with what is on screen reads onRenderedColumnsChange rather than column.getIsVisible().

Numbers and dates you can tick

meta.facetBuckets turns a raw value into the keys a person actually filters by; counts, OR-within, AND-across, the URL and the agent tools work on those keys unchanged.

const probability: ColumnDef<Risk> = {
  id: "probability",
  accessorKey: "probability",
  filterFn: facetFilterFn,
  meta: {
    facetBuckets: (value) => [Number(value) >= 0.5 ? "high" : "low"],
    facetLabels: { high: "50 % or more", low: "under 50 %" },
  },
};

Dates get the buckets every list wants: ageBucket (today · week · month · older · never), dueState (overdue · soon · later · undated) and relativeAge for the cell. parseDateish underneath reads ISO and the dotted European form and refuses 03/04/2026 — March in one country, April in another — rather than guessing.

Selecting, opening, and the keyboard

A click selects (onRowClick), Enter or a double click opens (onRowOpen) — the desktop-list split, so brushing a row never opens a pane by surprise. selectionColumn() adds the tick boxes; DataTableSelectionBar is the one place bulk actions live (and the selection a chat agent reads). onActiveRowChange + activeRowId walk the list with ↑/↓/Home/End, Escape clears, and the active row stays scrolled into view.

useAppStore.setContextItem(key, item | null) keeps one context chip per key, so a list can keep the chat in step with its selection instead of piling up stale chips.

Room to scroll

DataTable takes maxHeight to give a list its own viewport (sticky header, both axes) instead of running down the page; inside a focused DataTableFrame it fills the layer. spacer (default) keeps columns at their natural width with the slack at the end, the way a spreadsheet looks.

Extension Points

Feature modules (like SPACES) plug into @iloveagents/foundry-web-ui via registries — no direct imports needed:

  • NavItem.dnd — drag-and-drop behavior on sidebar items
  • Panel renderer registry — custom content renderers for the side panel
  • Citation handler — handles [n] citation clicks in markdown
  • Tool UI — custom rendering for agent tools via makeAssistantToolUI

See AGENTS.md for architecture details and import boundary rules.