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

@bernierllc/onboarding-chat-ui

v0.1.0

Published

React onboarding chat UI: split-pane chat with side panels, progress checklist, completion and rating, attachment dropzone, auto-greeting, and onboarding SSE hooks.

Downloads

306

Readme

@bernierllc/onboarding-chat-ui

React UI component library for onboarding chat experiences. Provides a full-featured streaming chat interface backed by Server-Sent Events (SSE), goal progress tracking, plugin-driven side panels, and completion handoffs — all wired to @bernierllc/onboarding-config-core configuration objects.

Installation

npm install @bernierllc/onboarding-chat-ui

Peer dependencies:

npm install react react-dom @bernierllc/onboarding-config-core @bernierllc/onboarding-feature-plugin

Usage

import { OnboardingChat } from '@bernierllc/onboarding-chat-ui';
import type { OnboardingConfig } from '@bernierllc/onboarding-config-core';

const config: OnboardingConfig = {
  id: 'my-onboarding',
  name: 'Getting Started',
  persona: {
    tone: 'friendly-expert',
    greeting: "Hi! Let's get you set up.",
    askOneAtATime: true,
  },
  phases: [{ id: 'setup', title: 'Setup', guidance: 'Configure your account.', order: 1 }],
  goals: [
    {
      id: 'add-logo',
      description: 'Add your logo',
      required: true,
      phase: 'setup',
      completionCheck: { type: 'field-present', field: 'logoUrl' },
    },
  ],
  completionCriteria: { requireAllGoals: true },
  handoffs: [{ id: 'dashboard', type: 'route', label: 'Go to Dashboard', order: 1 }],
  features: { enabledPlugins: [], pluginConfig: {} },
};

export default function OnboardingPage() {
  return (
    <OnboardingChat
      chatEndpoint="/api/onboarding/chat"
      sessionId="sess-abc123"
      userId="user-xyz"
      config={config}
      onComplete={(status) => console.log('Onboarding complete', status)}
    />
  );
}

Custom Hooks Usage

Use the low-level hooks directly to build a custom UI:

import { useOnboardingChat, useGoalProgress } from '@bernierllc/onboarding-chat-ui';

function MyCustomChat({ config }) {
  const { messages, send, isStreaming, setupStatus, isComplete, error } = useOnboardingChat({
    chatEndpoint: '/api/chat',
    sessionId: 'sess-1',
    userId: 'user-1',
  });

  const { goalItems, requiredMet, requiredTotal } = useGoalProgress({
    goals: config.goals,
    setupStatus,
  });

  return (
    <div>
      {error && <p>Error: {error.message}</p>}
      <p>{requiredMet}/{requiredTotal} goals complete</p>
      {messages.map(m => <div key={m.id}>{m.role}: {m.content}</div>)}
      {isComplete && <p>All done!</p>}
      <button disabled={isStreaming} onClick={() => send('Hello')}>Send</button>
    </div>
  );
}

API

OnboardingChat Component

Root orchestrator component. Renders the three-column layout: chat pane, progress checklist, and side panel.

| Prop | Type | Required | Description | |------|------|----------|-------------| | chatEndpoint | string | Yes | POST endpoint for chat messages | | config | OnboardingConfig | Yes | Onboarding configuration object | | sessionId | string | Yes | Unique session identifier | | userId | string | Yes | Current user identifier | | statusEndpoint | string | No | Endpoint for goal status polling | | ratingEndpoint | string | No | Endpoint for thumbs up/down ratings | | panelDescriptors | PluginPanelDescriptor[] | No | Plugin panel slot declarations | | panelComponents | Record<string, React.ComponentType> | No | React components keyed by panel slot | | onComplete | (status: SetupStatus \| null) => void | No | Callback when onboarding completes | | fetchFn | typeof fetch | No | Injectable fetch (useful for tests) | | className | string | No | Additional CSS class |

ChatPane Component

The message thread plus input area.

| Prop | Type | Required | Description | |------|------|----------|-------------| | messages | OnboardingMessage[] | Yes | Current message list | | isStreaming | boolean | Yes | Whether an SSE stream is in progress | | onSend | (text: string, attachments?: Attachment[]) => void | Yes | Send handler | | className | string | No | Additional CSS class |

ProgressChecklist Component

Renders goal items with check/incomplete badges, driven by SetupStatus from the SSE stream.

| Prop | Type | Required | Description | |------|------|----------|-------------| | goals | OnboardingGoal[] | Yes | Goals from OnboardingConfig | | status | SetupStatus \| null | Yes | Latest status from SSE stream | | showRequired | boolean | No | Filter to required goals only | | className | string | No | Additional CSS class |

SidePanel Component

Plugin slot container. Renders the component registered for the currently active panel slot.

| Prop | Type | Required | Description | |------|------|----------|-------------| | activePanel | string \| null | Yes | Currently active slot key | | panelComponents | Record<string, React.ComponentType> | Yes | Component map by slot | | fallback | React.ReactNode | No | Rendered when no active panel | | className | string | No | Additional CSS class |

CompletionCard Component

Shown when the onboarding is complete.

| Prop | Type | Required | Description | |------|------|----------|-------------| | onboardingName | string | Yes | Display name of the onboarding flow | | handoffs | HandoffTarget[] | Yes | Available next steps from config | | onHandoffClick | (target: HandoffTarget) => void | Yes | Called when user clicks a handoff | | children | React.ReactNode | No | Optional extra content | | className | string | No | Additional CSS class |

RatingBar Component

Thumbs up / thumbs down rating widget.

| Prop | Type | Required | Description | |------|------|----------|-------------| | onRate | (rating: 'up' \| 'down') => void | Yes | Called with the selected rating | | className | string | No | Additional CSS class |

AttachmentDropzone Component

Drag-and-drop file picker. Converts files to base64 data URLs and calls onAttach.

| Prop | Type | Required | Description | |------|------|----------|-------------| | onAttach | (attachments: Attachment[]) => void | Yes | Called with converted attachments | | accept | string | No | File types for the hidden input (e.g. "image/*") | | maxSizeMB | number | No | Max file size (default: 5 MB) | | className | string | No | Additional CSS class |

useOnboardingChat Hook

Core hook managing SSE streaming, message accumulation, and state transitions.

import { useOnboardingChat } from '@bernierllc/onboarding-chat-ui';

const {
  messages,       // OnboardingMessage[]
  send,           // (text: string, attachments?: Attachment[]) => void
  isStreaming,    // boolean
  toolCallEvents, // ToolCallEvent[]
  lastToolCall,   // ToolCallEvent | null
  setupStatus,    // SetupStatus | null
  isComplete,     // boolean
  error,          // Error | null
} = useOnboardingChat({
  chatEndpoint: '/api/chat',
  sessionId: 'sess-1',
  userId: 'user-1',
  fetchFn: customFetch, // optional, defaults to window.fetch
});

Options:

| Option | Type | Required | Description | |--------|------|----------|-------------| | chatEndpoint | string | Yes | POST endpoint URL | | sessionId | string | Yes | Session ID sent in request body | | userId | string | Yes | User ID sent in request body | | fetchFn | typeof fetch | No | Injectable fetch (testing/SSR) |

useGoalProgress Hook

Derives display-ready goal items from raw goals and live SetupStatus.

import { useGoalProgress } from '@bernierllc/onboarding-chat-ui';

const { goalItems, requiredMet, requiredTotal, isComplete } = useGoalProgress({
  goals: config.goals,
  setupStatus,
});

usePanelRegistry Hook

Determines which plugin panel slot is active based on recent tool call events.

import { usePanelRegistry } from '@bernierllc/onboarding-chat-ui';

const { activePanel } = usePanelRegistry({
  panelDescriptors: config.features.panelDescriptors ?? [],
  toolCallEvents,
});

useAutoGreeting Hook

Fires an initial greeting message after a configurable delay when the message list is empty.

import { useAutoGreeting } from '@bernierllc/onboarding-chat-ui';

useAutoGreeting({
  messages,
  send,
  greeting: config.persona.greeting,
  delayMs: 500, // default
});

OnboardingChatProvider / useOnboardingChatContext

Share chat state across a component subtree without prop drilling.

import { OnboardingChatProvider, useOnboardingChatContext } from '@bernierllc/onboarding-chat-ui';

function App() {
  return (
    <OnboardingChatProvider value={chatState}>
      <MyPanel />
    </OnboardingChatProvider>
  );
}

function MyPanel() {
  const { messages, isStreaming } = useOnboardingChatContext();
  return <div>{isStreaming ? 'Thinking...' : `${messages.length} messages`}</div>;
}

OnboardingUIError

All errors thrown by this package are instances of OnboardingUIError with a code field and optional cause chain.

import { OnboardingUIError } from '@bernierllc/onboarding-chat-ui';

if (error instanceof OnboardingUIError) {
  console.error(error.code, error.message, error.cause);
}

| Code | Cause | |------|-------| | CHAT_REQUEST_FAILED | Non-2xx HTTP response from chat endpoint | | NO_RESPONSE_BODY | Successful HTTP response but body is null | | STREAM_ERROR | Exception thrown during streaming (network failure, parse error) | | FILE_READ_ERROR | FileReader error inside AttachmentDropzone |

Integration Documentation

Logger Integration

This package uses @bernierllc/logger for structured error logging. All caught errors are logged at the error level with full context before propagating to the caller. The logger auto-detects the service environment; no configuration is required.

// Internal usage — no setup needed by consumers
import { logger } from '@bernierllc/logger';
logger.error('OnboardingUIError during chat stream', error, { context });

NeverHub Integration

This package supports optional NeverHub integration for real-time event tracking and session telemetry. When @bernierllc/neverhub-adapter is available in the environment and NeverHubAdapter.detect() returns true, the OnboardingChat component registers itself and emits lifecycle events. Core functionality works regardless of NeverHub availability (graceful degradation).

// NeverHub auto-detection pattern (internal)
import { NeverHubAdapter } from '@bernierllc/neverhub-adapter';

if (await NeverHubAdapter.detect()) {
  const adapter = new NeverHubAdapter();
  await adapter.register({ type: 'onboarding-chat-ui', sessionId });
}

To enable NeverHub in your app, install and configure the adapter before rendering OnboardingChat. No additional props are required.

SSE Stream Protocol

The backend chat endpoint must respond with Content-Type: text/event-stream. Each event is a newline-delimited JSON frame:

data: {"type":"delta","delta":"Hello "}\n\n
data: {"type":"delta","delta":"world!"}\n\n
data: {"type":"tool_call","toolName":"preview_site","args":{"url":"https://example.com"}}\n\n
data: {"type":"status","status":{...GoalProgress...}}\n\n
data: {"type":"complete"}\n\n

| Event type | Payload fields | Effect | |------------|---------------|--------| | delta | delta: string | Appends to the current assistant message | | tool_call | toolName: string, args: unknown | Pushes to toolCallEvents, updates lastToolCall, activates side panel | | status | status: GoalProgress | Updates setupStatus and goal checklist | | complete | — | Sets isComplete: true, calls onComplete callback |

Testing Integration

The package provides fetchFn prop/option for full test isolation without mocking globals:

import { useOnboardingChat } from '@bernierllc/onboarding-chat-ui';
import { renderHook, act } from '@testing-library/react';

const mockFetch: typeof fetch = async () => ({
  ok: true,
  status: 200,
  body: { getReader: () => myCustomReader },
} as unknown as Response);

const { result } = renderHook(() =>
  useOnboardingChat({ chatEndpoint: '/api/chat', sessionId: 's', userId: 'u', fetchFn: mockFetch })
);

Browser Requirements

This package is browser-only. It uses fetch, ReadableStream, TextDecoder, and FileReader. It does not import Node.js built-ins.

License

Copyright (c) 2025 Bernier LLC. Limited-use license — see package header.