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

@tinyweb_dev/agent-framework-chat-ui

v0.4.0

Published

Drop-in React 19 chat component for [`tinyweb-agent-framework`](../../) agents. Streams [`AgentEvent`](../protocol/src/events.ts) from any NDJSON endpoint, renders assistant markdown, tool results (image / exec / generic JSON), and context-condense pills

Readme

@tinyweb-agent-framework/chat-ui

Drop-in React 19 chat component for tinyweb-agent-framework agents. Streams AgentEvent from any NDJSON endpoint, renders assistant markdown, tool results (image / exec / generic JSON), and context-condense pills — all with zero runtime CSS dependencies.

Install

bun add @tinyweb-agent-framework/chat-ui
# peer deps
bun add react react-dom @tinyweb-agent-framework/protocol

Then import the stylesheet once (e.g. in your root layout.tsx):

import "@tinyweb-agent-framework/chat-ui/styles.css"

The stylesheet is fully scoped under .tinyweb-chat, so it will not bleed into the host app's styles.

Quick start — 5 lines

import { TinywebChat } from "@tinyweb-agent-framework/chat-ui"
import "@tinyweb-agent-framework/chat-ui/styles.css"

export default function ChatPage() {
  return <TinywebChat endpoint="/api/chat" />
}

Your /api/chat POST handler must return NDJSON AgentEvent objects (one JSON per \n-terminated line). See packages/agent for the agent runtime that emits them.

Custom tool renderer

TinywebChat ships with image, exec, and JSON fallback renderers. To add your own — say, a chart preview for a analytics.query tool — pass toolRenderers:

import { TinywebChat, type ToolRenderer } from "@tinyweb-agent-framework/chat-ui"

const ChartRenderer: ToolRenderer = {
  match: (name) => name === "analytics.query",
  render: ({ result }) => <ChartPreview data={result as ChartData} />,
}

export default function Page() {
  return (
    <TinywebChat
      endpoint="/api/chat"
      toolRenderers={[ChartRenderer]}
    />
  )
}

Renderers are evaluated first-match-wins in array order, and the built-in image / exec / JSON renderers run AFTER your list — so you can override them by matching the same shapes.

Passing extra body / headers

Use extraBody (object or function) when the agent route needs more than just prompt — e.g. session id, owner id, device kind:

<TinywebChat
  endpoint="/api/chat"
  extraBody={() => ({
    sessionId: useSessionStore.getState().id,
    ownerId: useOwnerStore.getState().id,
  })}
  headers={() => ({
    authorization: `Bearer ${useAuthStore.getState().token}`,
  })}
/>

Both extraBody and headers accept a plain object OR a function. The function form is invoked once per turn, so tokens stay fresh.

Props reference

| Prop | Type | Default | |---|---|---| | endpoint | string | — (required) | | extraBody | Record<string, unknown> \| (() => Record<string, unknown>) | undefined | | headers | HeadersInit \| (() => HeadersInit) | undefined | | title | string | "Tinyweb Agent" | | hero | { title?, subtitle? } \| null | default copy, null to hide | | sidebar | ReactNode | undefined (hidden) | | placeholder | string | "Ask me anything…" | | theme | Partial<ChatTheme> | dark preset | | toolRenderers | ToolRenderer[] | [] | | onEvent | (event: AgentEvent) => void | undefined | | onFinish | (event: FinishEvent) => void | undefined | | disabled | boolean | false | | initialMessages | ChatMessage[] | [] |

Advanced — bring your own shell

useAgentChat is exported so you can build a custom layout while reusing the state machine. The hook returns { nodes, status, send, abort, reset }.

import { useAgentChat } from "@tinyweb-agent-framework/chat-ui"

function MyChat() {
  const { nodes, status, send } = useAgentChat({ endpoint: "/api/chat" })
  return /* render however you like */
}

renderMarkdown(src) is also exported if you want to reuse the same XSS-safe markdown parser elsewhere.

Compatibility

  • React: >= 19.0.0 (uses useEffect/useLayoutEffect only — no server components / RSC features).
  • Next.js: 15.x with App Router. <TinywebChat /> is a Client Component ("use client" directive baked in).
  • Bun / Node: source ships as .tsx. Both Bun and Next 15 transpile on the fly; no build step required for now.

Voice call UI

When TinywebChatProps.realtime is provided, the composer renders a Call button that opens RealtimeCallOverlay inside the same chat column. The voice panel features a calm dark navy stage with a central black "void" disc surrounded by three translucent purple halo rings that breathe and react in real time to whoever is talking (mic input vs agent TTS output).

The animation is CSS-only (no Canvas, no WebGL). A single RAF loop inside RealtimeOrb reads the analyser nodes exposed by useRealtimeCall and writes one CSS custom property (--rt-level) plus a data-speaker attribute on the orb root. Children react via pure CSS.

Overlay props

<RealtimeCallOverlay
  tokenEndpoint="/api/realtime/token"
  onClose={() => setOpen(false)}
  hero="How can I help you today?"
  statusLabels={{
    connecting: "Đang kết nối…",
    "active-listening": "Đang lắng nghe",
    "active-speaking": "Đang nói…",
    "active-muted": "Đã tắt mic",
  }}
/>

| Prop | Type | Description | |---|---|---| | hero | string | Greeting line shown under the orb. Defaults to "How can I help you today?". Pass "" to hide. | | statusLabels | Partial<Record<RealtimeStatusLabelKey, string>> | i18n / branding overrides for the top status pill. Beyond the lifecycle CallStatus keys, the "active" bucket exposes three richer sub-state keys: active-listening, active-speaking, active-muted. |

Theming

The voice panel exposes a dedicated set of CSS custom properties under the .tinyweb-chat root. Host apps can override them either directly in their stylesheet or via ChatTheme.realtime:

<TinywebChat
  theme={{
    realtime: {
      bgFrom: "#0a0e2a",
      bgTo: "#050818",
      halo1: "rgba(146, 130, 220, 0.34)",
      halo2: "rgba(146, 130, 220, 0.22)",
      halo3: "rgba(146, 130, 220, 0.14)",
      core: "#06070d",
      accent: "#b9aef0",
      micBg: "#0d1024",
      micFg: "#ffffff",
    },
  }}
/>

| Theme key | CSS variable | Purpose | |---|---|---| | bgFrom | --rt-bg-from | Top of the dark navy gradient. | | bgTo | --rt-bg-to | Bottom of the gradient. | | halo1 | --rt-halo-1 | Innermost halo ring color. | | halo2 | --rt-halo-2 | Middle halo ring color. | | halo3 | --rt-halo-3 | Outer halo ring color. | | core | --rt-core | Inner black "void" disc color. | | accent | --rt-accent | Text accent + ripple stroke color. | | micBg | --rt-mic-bg | Mic FAB background. | | micFg | --rt-mic-fg | Mic icon foreground. |

Accessibility

  • Status pill is an aria-live="polite" region.
  • All icon buttons carry explicit aria-labels.
  • The mic FAB uses aria-pressed to convey muted state.
  • prefers-reduced-motion: reduce is honoured: the orb skips its RAF loop and pins --rt-level at a small static value, and the mic ripple animation is disabled.

See plan 0019 for the full design and animation maths.

Not included

This package is intentionally focused on the chat surface. The device-pilot demo's device pairing UI (Owner / Pair / Devices panel) is not part of v0 — it lives in the upcoming plan 0012-next-chat-example consumer.