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

@primoia/vocall-react

v1.0.0

Published

React/Next.js SDK for Vocall AAP protocol

Readme

Vocall SDK for Next.js / React

Integrate AI-powered voice and chat assistants into React and Next.js applications using the Vocall AAP (Agent-Application Protocol).


Features

  • React-native hooks -- useVocall, useVocallField, and useVocallAction provide reactive state with zero boilerplate.
  • Plug-and-play components -- VocallChat, VocallFab, and VocallStatusPill give you a full chat overlay out of the box.
  • Declarative manifest -- Describe your screens, fields, and actions once; the engine handles the rest.
  • Automatic command execution -- The SDK receives server commands (fill, navigate, click, etc.) and applies them to registered DOM elements.
  • Typewriter fill animation -- Field values are filled character-by-character for a natural feel.
  • Auto-reconnect -- Exponential back-off reconnection with up to 10 retry attempts.
  • Streaming tokens -- Chat responses stream token-by-token via useSyncExternalStore for tear-free rendering.
  • Next.js App Router ready -- All hooks and components are marked 'use client'.

Installation

npm install @vocall/react-sdk

Peer dependencies: react >= 18.0.0 and react-dom >= 18.0.0.


Quick Start

1. Wrap your app with VocallProvider

// app/layout.tsx  (Next.js App Router example)
import { VocallProvider } from '@vocall/react-sdk';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <VocallProvider serverUrl="wss://your-vocall-server/connect">
          {children}
        </VocallProvider>
      </body>
    </html>
  );
}

2. Connect and send messages

'use client';

import { useVocall, useVocallField, ManifestMessage, FieldType } from '@vocall/react-sdk';
import { useEffect, useState } from 'react';

const manifest: ManifestMessage = {
  type: 'manifest',
  app: 'my-app',
  screens: {
    home: {
      id: 'home',
      label: 'Home',
      fields: [
        { id: 'name', type: FieldType.Text, label: 'Name' },
        { id: 'email', type: FieldType.Email, label: 'Email' },
      ],
      actions: [
        { id: 'submit', label: 'Submit' },
      ],
    },
  },
};

export default function HomePage() {
  const { connect, connected, status, messages, sendText } = useVocall();
  const nameField = useVocallField('home', 'name');
  const emailField = useVocallField('home', 'email');

  useEffect(() => {
    connect(manifest);
  }, [connect]);

  return (
    <div>
      <p>Status: {status} | Connected: {String(connected)}</p>

      <input ref={nameField.ref} placeholder="Name" />
      <input ref={emailField.ref} placeholder="Email" />

      <ul>
        {messages.map((msg, i) => (
          <li key={i}><strong>{msg.role}:</strong> {msg.text}</li>
        ))}
      </ul>

      <input
        onKeyDown={(e) => {
          if (e.key === 'Enter') sendText(e.currentTarget.value);
        }}
        placeholder="Ask the assistant..."
      />
    </div>
  );
}

Hooks API

useVocall()

Main hook for interacting with the Vocall client. Must be used inside a <VocallProvider>.

const {
  client,        // VocallClient    -- the underlying client instance
  status,        // VocallStatus    -- current status enum value
  messages,      // ChatMessage[]   -- conversation history (read-only)
  connected,     // boolean         -- true when the WebSocket is open
  sessionId,     // string | null   -- server-assigned session ID
  sendText,      // (text: string) => void
  connect,       // (manifest: ManifestMessage) => void
  disconnect,    // () => void
  clearMessages, // () => void
} = useVocall();

All reactive values (status, messages, connected, sessionId) are backed by useSyncExternalStore for concurrent-safe reads.


useVocallField(screenId, fieldId, options?)

Registers a DOM element as a controllable field. The engine can then fill, clear, focus, highlight, scroll to, enable, or disable it.

const { ref, registered } = useVocallField('invoice', 'recipient_name');

| Return | Type | Description | |-----------|-----------------------------------|-----------------------------------------------| | ref | (el: HTMLElement \| null) => void | Callback ref -- attach to your input element. | | registered | boolean | Whether the field is currently registered. |

Usage:

<input ref={ref} placeholder="Recipient" />

Custom value accessors (for controlled inputs):

const [value, setValue] = useState('');
const { ref } = useVocallField('invoice', 'amount', {
  setValue: (v) => setValue(v),
  getValue: () => value,
});

<input ref={ref} value={value} onChange={(e) => setValue(e.target.value)} />

useVocallAction(screenId, actionId, callback)

Registers a callback that the engine invokes when the server sends a click command for the given action.

useVocallAction('invoice', 'submit', async () => {
  await submitForm();
});

The callback ref is kept stable internally, so the latest closure is always called without re-registering on every render.


Components

VocallChat

A ready-made chat panel with message bubbles, status indicators, and a text input.

import { VocallChat } from '@vocall/react-sdk';

<VocallChat
  open={chatOpen}
  onClose={() => setChatOpen(false)}
  assistantName="Emma"
  className="my-chat"
  style={{ bottom: 80 }}
/>

| Prop | Type | Default | Description | |-----------------|-----------------------|-----------|-------------------------------------| | open | boolean | -- | Controls visibility. | | onClose | () => void | -- | Called when the user clicks close. | | assistantName | string | "Emma" | Name shown in the header. | | className | string | -- | CSS class for the container. | | style | React.CSSProperties | -- | Inline styles for the container. |


VocallFab

A floating action button that reflects the current connection status through color changes and displays a spinner during thinking and executing states.

import { VocallFab } from '@vocall/react-sdk';

<VocallFab onClick={() => setChatOpen((o) => !o)} />

| Prop | Type | Description | |-------------|-----------------------|----------------------------------| | onClick | () => void | Called when the FAB is clicked. | | className | string | CSS class for the button. | | style | React.CSSProperties | Inline styles for the button. |


VocallStatusPill

A small inline pill that appears only when the assistant is actively working (thinking, executing, speaking, listening, or recording). Hides automatically when idle or disconnected.

import { VocallStatusPill } from '@vocall/react-sdk';

<VocallStatusPill className="my-status" />

| Prop | Type | Description | |-------------|-----------------------|--------------------------------| | className | string | CSS class for the pill. | | style | React.CSSProperties | Inline styles for the pill. |


VocallClient API

The VocallClient class manages the WebSocket connection and the field/action registries. You can access it directly via useVocallClient() or through the client property returned by useVocall().

Constructor

const client = new VocallClient(serverUrl, { token?, visitorId? });

| Parameter | Type | Description | |-------------|----------|------------------------------------------------------------| | serverUrl | string | WebSocket URL of the Vocall engine (e.g. wss://host/connect). | | token | string | Optional authentication token. | | visitorId | string | Optional visitor ID. Auto-generated and persisted in localStorage if omitted. |

Core Methods

| Method | Description | |------------------------------------------------------|------------------------------------------------------| | connect(manifest: ManifestMessage) | Opens the WebSocket and sends the manifest. | | disconnect() | Closes the connection intentionally (no reconnect). | | sendText(text: string) | Sends a user chat message to the engine. | | sendConfirm(seq: number, confirmed: boolean) | Responds to an ask_confirm action. | | sendResult(seq: number, results, state?) | Sends command execution results back to the engine. | | sendState(state: StateMessage) | Sends current UI state to the engine. | | clearMessages() | Clears the local chat history. | | destroy() | Disconnects and removes all listeners. |

State Getters

| Property | Type | Description | |-------------|---------------------------|----------------------------------------------| | status | VocallStatus | Current engine status. | | connected | boolean | Whether the WebSocket is open. | | messages | readonly ChatMessage[] | Conversation history. | | sessionId | string \| null | Server-assigned session ID. | | visitorId | string | Persistent visitor identifier. |

Callbacks

Set these on the client instance to handle navigation, modals, toasts, and confirmation dialogs in your application:

client.onNavigate  = (screenId) => router.push(`/${screenId}`);
client.onToast     = (message, level, duration) => showToast(message, level);
client.onConfirm   = (seq, message) => showConfirmDialog(seq, message);
client.onOpenModal = (modalId, query) => openModal(modalId);
client.onCloseModal = () => closeModal();

Field and Action Registration

These methods are called automatically by useVocallField and useVocallAction, but are available for manual use:

| Method | Description | |--------------------------------------------------------|--------------------------------------------| | registerField(screenId, fieldId, entry) | Registers a field element. | | unregisterField(screenId, fieldId) | Removes a field registration. | | registerAction(screenId, actionId, callback) | Registers an action callback. | | unregisterAction(screenId, actionId) | Removes an action registration. | | unregisterScreen(screenId) | Removes all fields and actions for a screen. |


Manifest Structure

The manifest describes your application's screens, fields, and actions to the Vocall engine. It is sent automatically upon connection.

import { ManifestMessage, FieldType } from '@vocall/react-sdk';

const manifest: ManifestMessage = {
  type: 'manifest',
  app: 'my-erp',
  version: '1.0.0',
  currentScreen: 'dashboard',
  user: {
    name: 'Jane Doe',
    email: '[email protected]',
    org: 'Acme Corp',
    role: 'admin',
  },
  persona: {
    name: 'Emma',
    role: 'ERP Assistant',
    instructions: 'Help the user manage invoices and clients.',
  },
  context: {
    locale: 'en-US',
    tenant: 'acme',
  },
  screens: {
    dashboard: {
      id: 'dashboard',
      label: 'Dashboard',
      route: '/dashboard',
      fields: [
        { id: 'search', type: FieldType.Text, label: 'Search' },
      ],
      actions: [
        { id: 'refresh', label: 'Refresh Data' },
        { id: 'export', label: 'Export CSV', requiresConfirmation: true },
      ],
      modals: [
        { id: 'client_picker', label: 'Select Client', searchable: true },
      ],
    },
    invoice: {
      id: 'invoice',
      label: 'New Invoice',
      route: '/invoices/new',
      fields: [
        { id: 'recipient', type: FieldType.Autocomplete, label: 'Recipient', required: true },
        { id: 'amount', type: FieldType.Currency, label: 'Amount', min: 0 },
        { id: 'due_date', type: FieldType.Date, label: 'Due Date', required: true },
        { id: 'notes', type: FieldType.Textarea, label: 'Notes', maxLength: 500 },
        {
          id: 'status',
          type: FieldType.Select,
          label: 'Status',
          options: [
            { value: 'draft', label: 'Draft' },
            { value: 'sent', label: 'Sent' },
          ],
        },
      ],
      actions: [
        { id: 'save_draft', label: 'Save Draft' },
        { id: 'send', label: 'Send Invoice', destructive: true, requiresConfirmation: true },
      ],
    },
  },
};

Field Types

The FieldType enum defines the supported field types in the manifest:

| Value | Description | |----------------|-------------------------------------------------| | text | Plain text input. | | number | Numeric input. | | currency | Currency/monetary value input. | | date | Date picker. | | datetime | Date and time picker. | | email | Email address input. | | phone | Phone number input. | | masked | Input with a custom mask pattern. | | select | Dropdown select (provide options). | | autocomplete | Searchable select / autocomplete. | | checkbox | Boolean checkbox. | | radio | Radio button group. | | textarea | Multi-line text area. | | file | File upload. | | hidden | Hidden field (not visible to the user). |


UI Actions

The Vocall engine can send 14 UI actions to the client. These are executed automatically by the SDK when commands arrive over the WebSocket.

| Action | Target | Description | |----------------|-----------|------------------------------------------------------| | navigate | screen | Navigate to a different screen. | | fill | field | Set a field's value (with optional typewriter animation). | | clear | field | Clear a field's value. | | select | field | Set the selected value of a select/radio field. | | click | action | Invoke a registered action callback. | | highlight | field | Scroll to and visually highlight a field. | | focus | field | Move focus to a field. | | scroll_to | field | Scroll a field into view. | | show_toast | -- | Display a toast notification. | | ask_confirm | -- | Prompt the user for confirmation. | | open_modal | modal | Open a modal dialog. | | close_modal | -- | Close the currently open modal. | | enable | field | Enable a disabled input. | | disable | field | Disable an input. |

Actions are classified as sequential (navigate, click, open_modal, close_modal, ask_confirm, show_toast) or parallel (all others). Sequential actions execute one at a time in order; parallel actions execute concurrently for performance. All actions include automatic retry logic with up to 3 attempts.


Demo

A working demo application is available in the demo/ directory. It demonstrates the full integration including provider setup, field binding, action registration, and the chat overlay components.


Testing

The SDK includes a comprehensive test suite with ~156 tests covering protocol types, WebSocket client, React hooks, context provider, and components.

Running Tests

npm test                # Run all tests
npm test -- --coverage  # Run with coverage report

Test Structure

| File | Tests | What it covers | |---|---|---| | src/protocol/__tests__/types.test.ts | 30 | Protocol type enums, message interfaces, field types | | src/client/__tests__/vocall-client.test.ts | 94 | WebSocket connection, reconnection, command execution, streaming | | src/hooks/__tests__/use-vocall.test.tsx | 10 | useVocall hook, state reactivity | | src/hooks/__tests__/use-vocall-field.test.tsx | 7 | useVocallField hook, DOM element binding | | src/hooks/__tests__/use-vocall-action.test.tsx | 4 | useVocallAction hook, callback registration | | src/context/__tests__/vocall-provider.test.tsx | 11 | VocallProvider context, client lifecycle |

Test Configuration


Development

Building the SDK

npm install
npm run build

Running the Demo

The demo application is in the demo/ directory:

cd demo
npm install
npm run dev        # http://localhost:3000

The demo connects to a Vocall server at ws://localhost:12900/connect. See the workspace README for local server setup.

Docker

When running from the workspace, the demo is available as a Docker container:

# From the workspace root
make serve-all     # Starts engine + all demos
# Next.js demo at http://localhost:21003

Related

This SDK is part of the Primoia Vocall Workspace.

| Resource | Link | |---|---| | Workspace (setup, Docker) | primoia-vocall-workspace | | Angular SDK | vocall-sdk-angular | | Vue SDK | vocall-sdk-vue | | Kotlin SDK | vocall-sdk-kotlin | | Swift SDK | vocall-sdk-swift | | Flutter SDK | jarvis-sdk-flutter |


License

Proprietary. All rights reserved.