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

@foisit/angular-wrapper

v3.1.0

Published

A powerful AI assistant library for Angular applications, providing seamless chatbot functionality and intelligent interactions for websites.

Readme

@foisit/angular-wrapper

npm version License: MIT

The AI-Powered Conversational Assistant for Angular Applications

Transform your Angular app into an intelligent, voice-ready platform. Foisit provides a drop-in AI layer that understands natural language, manages multi-step workflows, and executes actions—all with zero backend required.

[!NOTE] > Voice Support Status: Voice recognition and responses are currently in development and will be released in a future update. The current version focuses on high-performance text-based interactions and AI intent matching.


Table of Contents


Features

  • Natural Language Understanding - AI-powered intent matching (proxied securely)
  • Smart Slot Filling - Auto-generates forms for missing parameters
  • Critical Action Protection - Built-in confirmation dialogs for dangerous operations
  • Programmatic UI Triggers - Direct command execution via runCommand()
  • Rich Markdown Rendering - Enhanced response formatting with headings, code, and links
  • Advanced File Validations - Comprehensive client-side file validation with size, type, and dimension checks
  • Premium UI - Glass or solid theme with dark/light mode support
  • Zero Backend Required - Secure proxy architecture keeps API keys server-side
  • Angular Native - Uses Dependency Injection, Signals, and RxJS
  • Type-Safe - Full TypeScript support with comprehensive types
  • Responsive - Works flawlessly on desktop and mobile

Installation

npm install @foisit/angular-wrapper

Peer Dependencies

{
  "@angular/core": "^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0",
  "@angular/common": "^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0"
}

Quick Start

Step 1: Import the Module

For Standalone Apps (Recommended)

// app.config.ts
import { ApplicationConfig, importProvidersFrom } from '@angular/core';
import { AssistantModule } from '@foisit/angular-wrapper';

export const appConfig: ApplicationConfig = {
  providers: [
    importProvidersFrom(
      AssistantModule.forRoot({
        introMessage: 'Welcome! How can I assist you today?',
        enableSmartIntent: true,
        commands: [
          {
            command: 'navigate to profile',
            action: () => console.log('Navigating to profile...'),
          },
        ],
      })
    ),
  ],
};

For Module-Based Apps

// app.module.ts
import { NgModule } from '@angular/core';
import { AssistantModule } from '@foisit/angular-wrapper';

@NgModule({
  imports: [
    AssistantModule.forRoot({
      introMessage: 'Welcome! How can I assist you today?',
      commands: [
        /* your commands */
      ],
    }),
  ],
})
export class AppModule {}

Step 2: Use the Service

// my-component.ts
import { Component } from '@angular/core';
import { AssistantService } from '@foisit/angular-wrapper';

@Component({
  selector: 'app-my-component',
  template: ` <button (click)="openAssistant()">Open Assistant</button> `,
})
export class MyComponent {
  constructor(private assistant: AssistantService) {}

  openAssistant() {
    this.assistant.toggle();
  }
}

Core Concepts

1. Commands

Commands are the building blocks of your assistant. Each command represents an action users can trigger through natural language.

{
  command: 'delete account',
  description: 'Permanently delete user account',
  action: () => this.accountService.delete()
}

2. Parameters (Slot Filling)

Define parameters and Foisit will automatically generate forms to collect them:

{
  command: 'create user',
  description: 'Create a new user account',
  parameters: [
    { name: 'username', label: 'Username', type: 'string', required: true },
    { name: 'email', label: 'Email address', type: 'string', required: true },
    { name: 'age', label: 'Age', type: 'number', required: false }
  ],
  action: (params) => this.userService.create(params)
}

Enterprise-safe param collection controls

  • collectRequiredViaForm (default: true): when enabled, any missing/invalid required params are collected via a form—no conversational guessing.
  • allowAiParamExtraction (default: true): when false, AI-extracted params are ignored; the assistant will always prompt the user for required fields.

Example:

{
  command: 'secure create user',
  description: 'No AI guessing, form-only',
  collectRequiredViaForm: true,
  allowAiParamExtraction: false,
  parameters: [
    { name: 'fullName', type: 'string', required: true },
    { name: 'email', type: 'string', required: true },
    { name: 'age', type: 'number', required: true, min: 18 },
  ],
  action: (params) => this.userService.create(params),
}

Supported Parameter Types:

  • string - Text input
  • number - Numeric input
  • date - Date picker
  • select - Dropdown (static or async options)
    • new: include multiple: true to allow the user to choose several values; the result will be an array.
  • file - File upload input

Field label text

  • By default, the form label uses description (or falls back to name).
  • Set label on a parameter when you want explicit, user-friendly label text.

3. File Parameters

Tip: you can also call overlayManager.addOptions(..., { allowMultiple: true }) or use a select parameter with multiple: true when you want pill‑style choices that support many selections.

Collect files via the built-in form UI and receive them in your command action.

{
  command: 'upload file',
  description: 'Pick a file and return it to the action',
  parameters: [
    {
      name: 'attachment',
      type: 'file',
      required: true,
      accept: ['image/*', 'audio/*', 'video/*'],
      multiple: false,
      // delivery: 'file' | 'base64' (default: 'file')
      delivery: 'file',
    },
  ],
  action: async (params) => {
    const file = params?.attachment as File | undefined;
    if (!file) return { type: 'error', message: 'No file provided.' };
    return {
      type: 'success',
      message: `File received. Name: ${file.name}, Type: ${file.type || 'unknown'}, Size: ${file.size} bytes`,
    };
  },
}

FileParameter supports advanced validations:

  • maxFiles: Maximum number of files allowed (default: 1 for single, 10 for multiple)
  • maxSizeBytes: Maximum size per file in bytes
  • maxTotalBytes: Maximum total size for all files combined
  • maxDurationSec: Maximum duration for media files (audio/video) in seconds
  • maxWidth / maxHeight: Maximum dimensions for images in pixels
  • accept: Allowed MIME types or file extensions (e.g., ['image/*', '.pdf'])
  • multiple: Allow selecting multiple files
  • capture: Hint for mobile devices ('camera' or 'microphone')
  • delivery: How files are delivered to your action ('file' or 'base64')

Files are validated client-side before submission, with clear error messages for violations.

4. Rich Markdown Rendering

Assistant responses support rich markdown formatting for enhanced readability:

  • Headings: # H1 through ###### H6
  • Text Formatting: **bold**, *italic*, ~~strikethrough~~
  • Code: Inline code and code blocks with syntax highlighting
  • Links: [text](url) with safe external linking
  • Lists: Ordered and unordered lists
  • Line breaks and paragraphs

Markdown is automatically rendered in AI responses, while user messages remain plain text.

5. Critical Actions

Protect dangerous operations with automatic confirmation dialogs:

{
  command: 'delete all data',
  critical: true, // Requires confirmation
  description: 'Permanently delete all application data',
  action: async () => {
    await this.dataService.deleteAll();
    return 'All data deleted successfully.';
  }
}

6. Select Parameters (Static)

Provide predefined options:

{
  command: 'set theme',
  parameters: [{
    name: 'theme',
    type: 'select',
    options: [
      { label: 'Light Mode', value: 'light' },
      { label: 'Dark Mode', value: 'dark' },
      { label: 'Auto', value: 'auto' }
    ]
  }],
  action: (params) => this.themeService.set(params.theme)
}

7. Dynamic Select Parameters

Load options from APIs:

{
  command: 'assign to user',
  parameters: [{
    name: 'userId',
    type: 'select',
    getOptions: async () => {
      const users = await this.userService.getAll();
      return users.map(u => ({
        label: `${u.name} (${u.email})`,
        value: u.id
      }));
    }
  }],
  action: (params) => this.taskService.assign(params.userId)
}

API Reference

AssistantService

Injectable service for programmatic control:

Methods

toggle(onSubmit?, onClose?)

Opens or closes the assistant overlay.

// Basic usage
this.assistant.toggle();

// With callbacks
this.assistant.toggle(
  (userInput) => console.log('User said:', userInput),
  () => console.log('Assistant closed')
);
addCommand(command, action?)

Dynamically add or update a command at runtime. Commands added via addCommand take effect immediately in the running application; they are stored in memory for the current session (they are not persisted after a page reload unless you re-register them during app startup).

// Add a simple command
this.assistant.addCommand('refresh data', () => {
  this.dataService.refresh();
  return 'Data refreshed!';
});

// Add a command with full config
this.assistant.addCommand({
  command: 'export report',
  description: 'Export data as PDF',
  parameters: [
    {
      name: 'format',
      type: 'select',
      options: [
        { label: 'PDF', value: 'pdf' },
        { label: 'Excel', value: 'xlsx' },
      ],
    },
  ],
  action: async (params) => {
    await this.reportService.export(params.format);
    return `Report exported as ${params.format}`;
  },
});

Dynamic Updates (Add / Remove / Update commands at runtime) ✅

  • Use addCommand to register a new command or to replace an existing command's behavior (remove first if you want a clean replacement).
  • Use removeCommand(commandPhrase) to unregister a command. This is useful for temporary or context-specific commands.
  • Commands are in-memory only; to persist them across reloads, re-register on app startup (e.g., in an initialization hook).

Example — register a temporary command and clean it up in ngOnDestroy:

// component.ts
import { OnDestroy } from '@angular/core';

export class MyComponent implements OnDestroy {
  constructor(private assistant: AssistantService) {
    this.assistant.addCommand('temp action', () => 'Temporary action executed');
  }

  ngOnDestroy() {
    // Remove the temporary command when component unmounts
    this.assistant.removeCommand('temp action');
  }
}

Notes:

  • If a command has only optional params, consider returning a form InteractiveResponse to prompt the user when no params are provided.
  • Always remove transient commands in your cleanup lifecycle to avoid leaks and confusing UX.
removeCommand(commandPhrase)

Remove a registered command.

this.assistant.removeCommand('delete account');
getCommands()

Get list of all registered command names.

const commands = this.assistant.getCommands();
console.log('Available commands:', commands);
runCommand(options)

Programmatically trigger a command execution with optional parameters. This allows you to invoke commands directly from your code without user input.

// Basic usage - trigger a command by ID
this.assistant.runCommand({ commandId: 'refresh-data' });

// With parameters
this.assistant.runCommand({
  commandId: 'create-user',
  params: { name: 'John', email: '[email protected]' },
});

// Open overlay and show the command invocation
this.assistant.runCommand({
  commandId: 'export-report',
  params: { format: 'pdf' },
  openOverlay: true,
  showInvocation: true,
});

Options:

  • commandId (required): The ID of the command to run
  • params (optional): Parameters to pass to the command action
  • openOverlay (optional): Whether to open the assistant overlay (default: false)
  • showInvocation (optional): Whether to display the command invocation in the chat (default: false)

Configuration Options

AssistantConfig

interface AssistantConfig {
  // Activation keyword (optional)
  activationCommand?: string;

  // Welcome message shown when assistant opens
  introMessage?: string;

  // Response for unrecognized inputs
  fallbackResponse?: string;

  // Enable AI-powered natural language understanding
  enableSmartIntent?: boolean;

  // Input field placeholder text
  inputPlaceholder?: string;

  // List of commands
  commands: AssistantCommand[];

  // Floating button configuration
  floatingButton?: {
    visible?: boolean;
    tooltip?: string;
    customHtml?: string;
    position?: { bottom: string; right: string };
  };

  // Theme mode: 'glass' (default) or 'solid'
  theme?: 'glass' | 'solid';

  // Custom colors for solid theme (ignored in glass mode)
  themeColors?: ThemeColors;
}

ThemeColors

Custom colors for solid theme mode:

interface ThemeColors {
  background?: string; // Background color (e.g., '#1e1e2e')
  text?: string; // Primary text color (e.g., '#ffffff')
  accent?: string; // Accent color for highlights (e.g., '#89b4fa' or a gradient)
  userBubbleBg?: string; // User message bubble background
  systemBubbleBg?: string; // System message bubble background
  border?: string; // Border color
  checkboxAccent?: string; // Checkbox accent color
  checkboxBorder?: string; // Checkbox border color
  checkboxCheck?: string; // Checkbox checkmark color
}

Theme Customization Example

AssistantModule.forRoot({
  commands: [...],
  // Use solid theme with custom colors
  theme: 'solid',
  themeColors: {
    background: '#1e1e2e',
    text: '#cdd6f4',
    accent: '#89b4fa',
    userBubbleBg: 'rgba(137, 180, 250, 0.2)',
    systemBubbleBg: 'rgba(255, 255, 255, 0.05)',
  },
})

Note: Glass theme (default) uses glassmorphism with blur effects and adapts to light/dark mode via prefers-color-scheme. Solid theme ignores system preferences and uses configured colors.


Advanced Usage

Example 1: Multi-Step Booking System

import { Component } from '@angular/core';
import { AssistantService } from '@foisit/angular-wrapper';

@Component({
  selector: 'app-booking',
  template: `<button (click)="setupBooking()">Enable Booking</button>`,
})
export class BookingComponent {
  constructor(private assistant: AssistantService, private bookingService: BookingService) {}

  setupBooking() {
    this.assistant.addCommand({
      command: 'book appointment',
      description: 'Schedule a new appointment',
      parameters: [
        {
          name: 'service',
          description: 'Type of service',
          type: 'select',
          required: true,
          getOptions: async () => {
            const services = await this.bookingService.getServices();
            return services.map((s) => ({
              label: s.name,
              value: s.id,
            }));
          },
        },
        {
          name: 'date',
          description: 'Preferred date',
          type: 'date',
          required: true,
        },
        {
          name: 'notes',
          description: 'Additional notes',
          type: 'string',
          required: false,
        },
      ],
      action: async (params) => {
        const booking = await this.bookingService.create(params);
        return {
          type: 'success',
          message: `✅ Appointment booked for ${params.date}!`,
        };
      },
    });
  }
}

Example 2: E-Commerce Product Search

this.assistant.addCommand({
  command: 'search products',
  parameters: [
    { name: 'query', type: 'string', required: true },
    {
      name: 'category',
      type: 'select',
      required: false,
      options: [
        { label: 'Electronics', value: 'electronics' },
        { label: 'Clothing', value: 'clothing' },
        { label: 'Books', value: 'books' },
      ],
    },
    {
      name: 'minPrice',
      type: 'number',
      required: false,
    },
  ],
  action: async (params) => {
    const results = await this.productService.search(params);
    this.router.navigate(['/products'], {
      queryParams: { q: params.query },
    });
    return `Found ${results.length} products matching "${params.query}"`;
  },
});

Example 3: Form Validation with Error Handling

this.assistant.addCommand({
  command: 'create account',
  parameters: [
    { name: 'email', type: 'string', required: true },
    { name: 'password', type: 'string', required: true },
    { name: 'age', type: 'number', required: true },
  ],
  action: async (params) => {
    // Validation
    if (params.age < 18) {
      return {
        type: 'error',
        message: '❌ You must be 18 or older to create an account.',
      };
    }

    if (!params.email.includes('@')) {
      return {
        type: 'error',
        message: '❌ Please provide a valid email address.',
      };
    }

    // Create account
    try {
      await this.authService.register(params);
      return {
        type: 'success',
        message: '✅ Account created successfully! You can now log in.',
      };
    } catch (error) {
      return {
        type: 'error',
        message: `❌ Registration failed: ${error.message}`,
      };
    }
  },
});

TypeScript Support

Full Type Definitions

// Type-safe command definition
const myCommand: AssistantCommand = {
  command: 'update settings',
  description: 'Update user preferences',
  parameters: [
    {
      name: 'theme',
      type: 'select',
      required: true,
      options: [
        { label: 'Light', value: 'light' },
        { label: 'Dark', value: 'dark' },
      ],
    },
  ],
  action: async (params: { theme: string }): Promise<InteractiveResponse> => {
    await this.settingsService.update(params.theme);
    return {
      type: 'success',
      message: `Theme updated to ${params.theme}`,
    };
  },
};

Best Practices

1. Command Naming

Good:

  • "create user"
  • "delete account"
  • "export report"

Avoid:

  • "CreateUser" (not natural)
  • "usr_del" (not descriptive)
  • "do the thing" (too vague)

2. Descriptions

Always provide clear descriptions for AI matching:

{
  command: 'reset password',
  description: 'Reset the user password and send recovery email',
  // AI can match: "forgot my password", "can't log in", etc.
}

3. Error Handling

Return user-friendly error messages:

action: async (params) => {
  try {
    await this.api.call(params);
    return { type: 'success', message: '✅ Done!' };
  } catch (error) {
    return {
      type: 'error',
      message: `❌ Something went wrong: ${error.message}`,
    };
  }
};

4. Use Signals for Reactive State

import { signal } from '@angular/core';

export class MyComponent {
  theme = signal<'light' | 'dark'>('light');

  constructor(private assistant: AssistantService) {
    this.assistant.addCommand('toggle theme', () => {
      const newTheme = this.theme() === 'light' ? 'dark' : 'light';
      this.theme.set(newTheme);
      return `Theme switched to ${newTheme}`;
    });
  }
}

Testing

Unit Testing Commands

import { TestBed } from '@angular/core/testing';
import { AssistantService } from '@foisit/angular-wrapper';

describe('AssistantService', () => {
  let service: AssistantService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [AssistantModule.forRoot({ commands: [] })],
    });
    service = TestBed.inject(AssistantService);
  });

  it('should add and execute command', () => {
    let executed = false;
    service.addCommand('test', () => {
      executed = true;
    });

    // Trigger command execution
    service.toggle();
    // Test execution...

    expect(executed).toBe(true);
  });
});

Related Packages


Troubleshooting

Assistant not appearing

Ensure AssistantModule.forRoot() is imported in your app configuration and double-tap the floating button.

Commands not executing

Check browser console for errors. Ensure action functions are returning values or promises.

TypeScript errors

Make sure you're using Angular 17+ and have @angular/core installed.


License

MIT © Foisit


Contributing

Contributions are welcome! Please read our Contributing Guide first.


Support


Made by the Foisit Team