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-angular

v1.0.1

Published

Angular SDK for Vocall - WebSocket UI automation platform

Readme

Vocall SDK for Angular

Integrate voice and chat-driven UI automation into your Angular applications using the Vocall AAP protocol.


Features

  • Bidirectional WebSocket communication with the Vocall engine
  • Reactive state management via RxJS BehaviorSubject observables
  • Automatic NgZone integration for seamless change detection
  • Screen-scoped field and action registration
  • 14 built-in UI actions (fill, navigate, click, highlight, and more)
  • Typewriter-style animated field filling
  • Automatic reconnection with exponential backoff
  • Persistent visitor identification via localStorage
  • Streaming chat token support for real-time responses
  • Compatible with Angular 18 and 19 (standalone components)

Installation

npm install @vocall/angular-sdk

Peer Dependencies

The SDK requires the following peer dependencies:

{
  "@angular/common": "^18.0.0 || ^19.0.0",
  "@angular/core": "^18.0.0 || ^19.0.0",
  "rxjs": "^7.8.0"
}

Quick Start

Both VocallService and FieldRegistryService are provided in root, so no additional module configuration is needed.

1. Connect to the Vocall server

import { Component, OnInit, OnDestroy, ElementRef, ViewChild } from '@angular/core';
import {
  VocallService,
  ManifestMessage,
  FieldType,
  VocallStatus,
} from '@vocall/angular-sdk';

@Component({
  selector: 'app-my-form',
  standalone: true,
  template: `
    <form>
      <label for="name">Name</label>
      <input #nameInput id="name" type="text" />

      <label for="email">Email</label>
      <input #emailInput id="email" type="email" />

      <button type="button" (click)="onSubmit()">Submit</button>
    </form>

    <p>Status: {{ vocall.status$ | async }}</p>
  `,
})
export class MyFormComponent implements OnInit, OnDestroy {
  @ViewChild('nameInput', { static: true }) nameInput!: ElementRef<HTMLInputElement>;
  @ViewChild('emailInput', { static: true }) emailInput!: ElementRef<HTMLInputElement>;

  constructor(public vocall: VocallService) {}

  ngOnInit(): void {
    const manifest: ManifestMessage = {
      type: 'manifest',
      app: 'my-app',
      screens: {
        form: {
          id: 'form',
          label: 'Contact Form',
          fields: [
            { id: 'name', type: FieldType.Text, label: 'Name', required: true },
            { id: 'email', type: FieldType.Email, label: 'Email', required: true },
          ],
          actions: [
            { id: 'submit', label: 'Submit' },
          ],
        },
      },
    };

    // Register fields
    this.vocall.registerField('form', 'name', {
      element: this.nameInput.nativeElement,
      setValue: (v) => (this.nameInput.nativeElement.value = v),
      getValue: () => this.nameInput.nativeElement.value,
    });

    this.vocall.registerField('form', 'email', {
      element: this.emailInput.nativeElement,
      setValue: (v) => (this.emailInput.nativeElement.value = v),
      getValue: () => this.emailInput.nativeElement.value,
    });

    // Register actions
    this.vocall.registerAction('form', 'submit', () => this.onSubmit());

    // Set callbacks
    this.vocall.onNavigate = (screenId) => console.log('Navigate to:', screenId);
    this.vocall.onToast = (message, level) => console.log(`[${level}] ${message}`);

    // Connect
    this.vocall.connect('ws://localhost:12900/connect', manifest);
  }

  onSubmit(): void {
    console.log('Form submitted');
  }

  ngOnDestroy(): void {
    this.vocall.unregisterScreen('form');
    this.vocall.disconnect();
  }
}

API Reference

VocallService

The primary injectable service. Provided in root -- no module imports required.

Observables

| Observable | Type | Description | |---|---|---| | status$ | Observable<VocallStatus> | Current engine status (disconnected, idle, thinking, executing, etc.) | | connected$ | Observable<boolean> | Whether the WebSocket connection is active | | messages$ | Observable<ChatMessage[]> | Full chat message history (user, agent, system) | | sessionId$ | Observable<string \| null> | Current session ID assigned by the server | | rawMessage$ | Observable<Record<string, unknown>> | Every raw incoming server message |

Synchronous Getters

| Getter | Type | Description | |---|---|---| | status | VocallStatus | Current status value | | connected | boolean | Current connection state | | messages | ChatMessage[] | Current message array | | sessionId | string \| null | Current session ID | | client | VocallClient \| null | Underlying client instance |

Methods

// Connection
connect(serverUrl: string, manifest: ManifestMessage, options?: { token?: string; visitorId?: string }): void
disconnect(): void

// Messaging
sendText(text: string): void
sendConfirm(seq: number, confirmed: boolean): void
sendResult(seq: number, results: ActionResult[], state?: StateMessage): void
sendState(state: StateMessage): void
clearMessages(): void

// Field & action registration
registerField(screenId: string, fieldId: string, entry: FieldEntry): void
unregisterField(screenId: string, fieldId: string): void
unregisterScreen(screenId: string): void
registerAction(screenId: string, actionId: string, callback: ActionCallback): void
unregisterAction(screenId: string, actionId: string): void

Callbacks

Set these on the service to handle server-initiated UI actions:

// Navigation between screens
vocall.onNavigate = (screenId: string) => { ... };

// Toast/snackbar notifications
vocall.onToast = (message: string, level: string, duration?: number) => { ... };

// Confirmation dialogs
vocall.onConfirm = (seq: number, message: string) => {
  const confirmed = window.confirm(message);
  vocall.sendConfirm(seq, confirmed);
};

// Modal open/close
vocall.onOpenModal = (modalId: string, query?: string) => { ... };
vocall.onCloseModal = () => { ... };

FieldRegistryService

Manages field and action registrations across Angular components. Provided in root.

// Field registration
registerField(screenId: string, fieldId: string, entry: FieldEntry): void
unregisterField(screenId: string, fieldId: string): void
unregisterScreen(screenId: string): void

// Action registration
registerAction(screenId: string, actionId: string, callback: ActionCallback): void
unregisterAction(screenId: string, actionId: string): void

// Lookups
getField(screenId: string, fieldId: string): FieldEntry | undefined
findField(fieldId: string): FieldEntry | undefined
getAction(screenId: string, actionId: string): ActionCallback | undefined
findAction(actionId: string): ActionCallback | undefined

FieldEntry

Each registered field must provide:

interface FieldEntry {
  element: HTMLElement;                 // The DOM element
  setValue: (value: string) => void;    // Programmatic value setter
  getValue: () => string;              // Current value getter
}

VocallStatus

enum VocallStatus {
  Disconnected = 'disconnected',
  Idle         = 'idle',
  Listening    = 'listening',
  Recording    = 'recording',
  Thinking     = 'thinking',
  Speaking     = 'speaking',
  Executing    = 'executing',
}

Manifest Structure

The manifest describes your application's screens, fields, and actions. It is sent to the server immediately after connection.

const manifest: ManifestMessage = {
  type: 'manifest',
  app: 'my-application',
  version: '1.0.0',
  currentScreen: 'dashboard',
  user: {
    name: 'John Doe',
    email: '[email protected]',
    org: 'Acme Corp',
    role: 'admin',
  },
  persona: {
    name: 'Assistant',
    role: 'helper',
    instructions: 'Help the user fill out forms.',
  },
  screens: {
    dashboard: {
      id: 'dashboard',
      label: 'Dashboard',
      route: '/dashboard',
      fields: [
        {
          id: 'search',
          type: FieldType.Text,
          label: 'Search',
          placeholder: 'Search...',
        },
      ],
      actions: [
        { id: 'refresh', label: 'Refresh' },
        { id: 'export', label: 'Export', requiresConfirmation: true },
      ],
      modals: [
        { id: 'details', label: 'Details', searchable: true },
      ],
    },
    form: {
      id: 'form',
      label: 'Registration Form',
      route: '/register',
      fields: [
        { id: 'full_name', type: FieldType.Text, label: 'Full Name', required: true },
        { id: 'birth_date', type: FieldType.Date, label: 'Birth Date' },
        { id: 'country', type: FieldType.Select, label: 'Country', options: [
          { value: 'us', label: 'United States' },
          { value: 'br', label: 'Brazil' },
        ]},
        { id: 'notes', type: FieldType.Textarea, label: 'Notes', maxLength: 500 },
        { id: 'agree', type: FieldType.Checkbox, label: 'I agree to terms' },
      ],
      actions: [
        { id: 'submit', label: 'Submit' },
        { id: 'cancel', label: 'Cancel', destructive: true },
      ],
    },
  },
};

FieldDescriptor Properties

| Property | Type | Description | |---|---|---| | id | string | Unique field identifier within the screen | | type | FieldType | Field type (see below) | | label | string | Human-readable label | | required | boolean | Whether the field is required | | mask | string | Input mask pattern (for masked fields) | | placeholder | string | Placeholder text | | options | SelectOption[] | Options for select/autocomplete fields | | source | string | Data source identifier | | min | number | Minimum value | | max | number | Maximum value | | maxLength | number | Maximum character length | | readOnly | boolean | Whether the field is read-only |

Field Types

The FieldType enum defines all supported field types:

| Type | Value | Description | |---|---|---| | Text | text | Standard text input | | Number | number | Numeric input | | Currency | currency | Currency/monetary input | | Date | date | Date picker | | Datetime | datetime | Date and time picker | | Email | email | Email address input | | Phone | phone | Phone number input | | Masked | masked | Input with mask pattern | | Select | select | Dropdown select | | Autocomplete | autocomplete | Autocomplete/typeahead input | | Checkbox | checkbox | Checkbox toggle | | Radio | radio | Radio button group | | Textarea | textarea | Multi-line text area | | File | file | File upload input | | Hidden | hidden | Hidden field |

UI Actions

The Vocall engine can send 14 UI actions to the client. These are executed automatically by the SDK when a command message is received.

| Action | Target | Description | |---|---|---| | navigate | screen | Navigate to a different screen | | fill | field | Fill a field with a value (supports typewriter animation) | | clear | field | Clear a field's value | | select | field | Set a select/dropdown value | | click | action | Trigger a registered action callback | | highlight | field | Scroll to and visually highlight a field | | focus | field | Focus 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 current modal | | enable | field | Enable a disabled field | | disable | field | Disable a field |

Actions are categorized internally as sequential (navigate, click, open_modal, close_modal, ask_confirm, show_toast) or parallel (all others). Sequential actions run in order; parallel actions execute concurrently for performance.

Demo

A demo application is available in the demo/ directory of this repository. It provides a working example of field registration, action handling, and WebSocket communication with the Vocall engine.

Testing

The SDK includes a comprehensive test suite with ~180 tests covering protocol types, field registry, WebSocket client, and the Angular service layer.

Running Tests

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

Test Structure

| File | Tests | What it covers | |---|---|---| | src/lib/protocol/types.spec.ts | 29 | Protocol type enums, message interfaces, field types | | src/lib/services/field-registry.service.spec.ts | 29 | Field and action registration, lookups, screen management | | src/lib/client/vocall-client.spec.ts | 79 | WebSocket connection, reconnection, command execution, streaming | | src/lib/services/vocall.service.spec.ts | 43 | Angular service wrapper, RxJS observables, NgZone integration |

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 start          # http://localhost:4200

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
# Angular demo at http://localhost:21002

Related

This SDK is part of the Primoia Vocall Workspace.

| Resource | Link | |---|---| | Workspace (setup, Docker) | primoia-vocall-workspace | | Next.js SDK | vocall-sdk-nextjs | | 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.