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

@psci-labs/chat-ui

v0.3.0

Published

React UI for the Claude Agent SDK chat surface — <ChatProvider>, useChat() hook, and the default tool renderers

Downloads

360

Readme

@psci-labs/chat-ui

React UI for the Claude Agent SDK chat surface — <ChatProvider>, useChat() hook, and the 16 default tool renderers, themed with Tailwind v4 + CSS variables.

pnpm add @psci-labs/chat-ui react react-dom tailwindcss

React 18 or 19 is supported; Tailwind v4 is required — the package ships a styles.css with @theme tokens that only Tailwind v4's CSS-first config understands.

v0.1.0 limitations

  • Code blocks — code, command output, and search results render as plain monospaced <pre>. Shiki-backed syntax highlighting is a planned post-0.1.0 enhancement.
  • AskUserfreeform + yes-no are fully supported. single-select / multi-select modes degrade to freeform (the options are listed above the textarea). Full radio / checkbox UI lands once the runtime exposes an HTTP endpoint for browser-side tool responses.
  • Plan-mode renderer is interactive on ExitPlanMode when the tool is complete and the call is part of the latest message. Three buttons (Accept & Continue, Keep Planning, Accept & Start Fresh) send canned user-message prompts via useChat().send / clearContext — they do NOT call respondToTool (the SDK's plan-mode tools fire-and-complete with no pending response). For historical plans the renderer falls back to a read-only display.

Message rendering

Assistant text and thinking parts render as GitHub-Flavored Markdown by default (bold, italics, lists, blockquotes, tables, links, headings). Fenced code blocks are routed through the same Shiki-backed <CodeBlock> used elsewhere in the UI. External links open in a new tab with rel="noreferrer noopener". User input is left as plain text — markdown in user messages is rarely intentional and renders verbatim.

Minimal usage

import { ChatProvider, Composer, Conversation, defaultToolRenderers } from '@psci-labs/chat-ui';
import '@psci-labs/chat-ui/styles.css';

<ChatProvider
  endpoint="/api/chat"
  threadId={threadId}
  renderers={{
    ...defaultToolRenderers,
    // Bash: MyCustomBashRenderer,
    // 'mcp__sharepoint__list_files': SharePointFiles,
  }}
>
  <Conversation />
  <Composer />
</ChatProvider>;

<ChatProvider> loads history on mount, opens an SSE stream when the user sends a message, and exposes send / cancel / clearContext via useChat(). The wire format matches @psci-labs/chat-runtime 1:1 — no runtime configuration knobs needed.

The runtime returns 409 if a stream is already open for the thread, so the <Composer> auto-disables while status === 'streaming' to keep the UI in step with that contract.

Tool renderer registry

Renderers are keyed on tool name strings ('Read', 'Bash', 'mcp__sharepoint__list_files', ...), not Claude-SDK types. This is deliberate — a future agent harness (opencode, pi) plugs in without changing the renderer contract.

import type { ToolRenderer } from '@psci-labs/chat-ui';

const MyBashRenderer: ToolRenderer = ({ toolCall, isLatest }) => (
  <pre>{JSON.stringify(toolCall.input, null, 2)}</pre>
);

Renderers receive an optional isLatest prop on ToolRendererPropstrue when the tool call belongs to the last message in the conversation. Used by the default plan-mode renderer to gate interactive controls on historical plans; custom renderers can consume it the same way.

Anything not in the registry falls through to DefaultToolRenderer, which prints the tool name, raw input, and result. Useful for new MCP tools you haven't built a renderer for yet.

Apps that want to dispatch manually (e.g. wrap each renderer in a custom card) can use the useToolRenderer(name) escape hatch hook to look up the renderer for a given name without going through <ToolCall>.

Tailwind v4 setup

The package ships a CSS file with @theme tokens. Consumers run it through their own Tailwind v4 build:

/* app's main CSS entry */
@import 'tailwindcss';
@import '@psci-labs/chat-ui/styles.css';
@source "../node_modules/@psci-labs/chat-ui/dist/**/*.{js,cjs}";

/* Optional: override any token to re-theme the chat surface. Last `@theme` wins. */
@theme {
  --color-chat-user-bg: oklch(0.6 0.18 30);
}

Tailwind v4 is declared as a peerDependency — the package will not work in apps that have not adopted Tailwind v4.

Available CSS variables

The @theme block exposes:

  • Base palette--color-background, --color-foreground, --color-card, --color-card-foreground, --color-popover, --color-popover-foreground, --color-primary, --color-primary-foreground, --color-secondary, --color-secondary-foreground, --color-muted, --color-muted-foreground, --color-accent, --color-accent-foreground, --color-destructive, --color-destructive-foreground, --color-border, --color-input, --color-ring
  • Chat surface--color-chat-user-bg/-fg, --color-chat-assistant-bg/-fg, --color-chat-mention-bg/-fg, --color-chat-toolcall-bg/-fg, --color-chat-checkpoint-bg/-fg
  • Radii--radius, --radius-sm, --radius-md, --radius-lg

Dark mode kicks in under either .dark or [data-theme="dark"] — both selectors are honored so the package matches whichever theme-toggle convention the consumer already uses.

cn() utility

Re-exported so customer apps writing custom tool renderers can compose classes the same way the built-in renderers do:

import { cn } from '@psci-labs/chat-ui';

<div className={cn('px-4 py-2', isActive && 'bg-primary')} />;

Internally it's just twMerge(clsx(...)). If a consumer already has its own cn() they prefer, they can keep using it — the built-in renderers don't care which one their callers use.