JSPM

@pillar-ai/angular

0.1.15
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 4
  • Score
    100M100P100Q61129F
  • License MIT

Angular SDK for Pillar product copilot — AI assistant for SaaS and web apps

Package Exports

  • @pillar-ai/angular
  • @pillar-ai/angular/package.json

Readme

@pillar-ai/angular

Angular SDK for the Pillar open-source AI copilot — embed a product assistant in your Angular app that executes tasks, not just answers questions. GitHub · Docs

npm version npm downloads License: MIT TypeScript

What is Pillar?

Pillar is a product copilot for SaaS and web applications. Users say what they want, and Pillar uses your UI to make it happen — navigating pages, pre-filling forms, and calling your APIs.

For example, a user could ask:

"Close the Walmart deal as won in Salesforce and notify implementation"

"Create a P1 bug in Linear for the checkout crash and add it to this sprint"

"How do I set up a webhook in Stripe?"

Pillar understands the intent, builds a multi-step plan, and executes it client-side with the user's session.

Documentation

View Full Documentation | Angular Quickstart | API Reference

Requirements

  • Angular 17.0.0 or higher
  • @pillar-ai/sdk (installed automatically as a dependency)

Installation

npm install @pillar-ai/angular

Quick Start

1. Initialize Pillar in your app config

// app.config.ts
import { ApplicationConfig, provideAppInitializer, inject } from '@angular/core';
import { PillarService } from '@pillar-ai/angular';

export const appConfig: ApplicationConfig = {
  providers: [
    provideAppInitializer(() => {
      const pillar = inject(PillarService);
      return pillar.init({ productKey: 'your-product-key' });
    }),
  ],
};

2. Use Pillar in your components

// help-button.component.ts
import { Component } from '@angular/core';
import { injectPillar } from '@pillar-ai/angular';

@Component({
  selector: 'app-help-button',
  standalone: true,
  template: `
    <button (click)="toggle()">
      {{ isPanelOpen() ? 'Close Help' : 'Get Help' }}
    </button>
  `,
})
export class HelpButtonComponent {
  private pillar = injectPillar();
  isPanelOpen = this.pillar.isPanelOpen;
  toggle = this.pillar.toggle;
}

API Reference

PillarService

Injectable service that manages the Pillar SDK lifecycle.

import { PillarService } from '@pillar-ai/angular';

@Component({...})
export class MyComponent {
  constructor(private pillarService: PillarService) {}
}

Methods

Method Description
init(config) Initialize the SDK with your product key
open(options?) Open the help panel
close() Close the help panel
toggle() Toggle the help panel
openArticle(slug) Open a specific article
openCategory(slug) Navigate to a category
search(query) Open search with a query
navigate(view, params?) Navigate to a specific view
setTheme(theme) Update the panel theme
setTextSelectionEnabled(enabled) Toggle text selection popover
on(event, callback) Subscribe to SDK events
onTask(taskName, handler) Register a task handler

Signals

Signal Type Description
state WritableSignal<PillarState> Current SDK state
isReady Signal<boolean> Whether SDK is ready
isPanelOpen WritableSignal<boolean> Whether panel is open

injectPillar()

Angular injection function for accessing the Pillar SDK with full functionality.

import { injectPillar } from '@pillar-ai/angular';

@Component({...})
export class MyComponent {
  private pillar = injectPillar();

  handleHelp() {
    this.pillar.open({ view: 'chat' });
  }
}

injectHelpPanel()

Simplified injection function focused on panel controls.

import { injectHelpPanel } from '@pillar-ai/angular';

@Component({...})
export class HelpMenuComponent {
  private panel = injectHelpPanel();

  isOpen = this.panel.isOpen;
  toggle = this.panel.toggle;
  openChat = this.panel.openChat;
}

PillarPanelComponent

Standalone component for custom panel placement.

import { PillarPanelComponent } from '@pillar-ai/angular';

@Component({
  selector: 'app-layout',
  standalone: true,
  imports: [PillarPanelComponent],
  template: `
    <div class="layout">
      <pillar-panel class="help-panel" />
      <main>
        <router-outlet />
      </main>
    </div>
  `,
})
export class LayoutComponent {}

Important: When using PillarPanelComponent, set panel.container: 'manual' in your config:

pillar.init({
  productKey: 'your-product-key',
  config: { panel: { container: 'manual' } }
});

Advanced Usage

Type-Safe Task Handlers

Define your actions and get full TypeScript support:

// lib/pillar/actions.ts
import { defineActions } from '@pillar-ai/sdk';

export const actions = defineActions({
  invite_member: {
    description: 'Invite a team member',
    data: {
      email: { type: 'string', description: 'Email address' },
      role: { type: 'string', description: 'Member role' },
    },
  },
});
// my.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { injectPillar } from '@pillar-ai/angular';
import { actions } from './lib/pillar/actions';

@Component({...})
export class MyComponent implements OnInit, OnDestroy {
  private pillar = injectPillar<typeof actions>();
  private unsubscribe?: () => void;

  ngOnInit() {
    // TypeScript knows the exact shape of data
    this.unsubscribe = this.pillar.onTask('invite_member', (data) => {
      console.log(data.email); // ✓ Typed as string
      console.log(data.role);  // ✓ Typed as string
    });
  }

  ngOnDestroy() {
    this.unsubscribe?.();
  }
}

Inline UI with Render Components

For inline_ui tools, use the render prop to display custom Angular components in the chat. The AI agent provides data directly to the render component — no execute function needed:

// invite-card.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-invite-card',
  standalone: true,
  imports: [FormsModule],
  template: `
    <div class="invite-card">
      <h3>Invite Team Members</h3>
      <input [(ngModel)]="email" placeholder="Email address" />
      <button (click)="confirm()">Send Invite</button>
      <button (click)="cancel()">Cancel</button>
    </div>
  `,
})
export class InviteCardComponent {
  @Input() data: any;
  @Output() onConfirm = new EventEmitter<Record<string, unknown>>();
  @Output() onCancel = new EventEmitter<void>();

  email = '';

  confirm() {
    this.onConfirm.emit({ email: this.email });
  }

  cancel() {
    this.onCancel.emit();
  }
}
// invite-tools.component.ts
import { Component } from '@angular/core';
import { injectPillarTool } from '@pillar-ai/angular';
import { InviteCardComponent } from './invite-card.component';

@Component({
  selector: 'app-invite-tools',
  standalone: true,
  template: '',
})
export class InviteToolsComponent {
  constructor() {
    injectPillarTool({
      name: 'invite_member',
      description: 'Invite a new team member via email',
      type: 'inline_ui',
      inputSchema: {
        type: 'object',
        properties: {
          suggestedEmail: { type: 'string', description: 'Pre-fill email if mentioned' },
        },
      },
      render: InviteCardComponent,
    });
  }
}

The render component receives:

  • data (via @Input()) — Data provided by the AI agent (extracted from the conversation via inputSchema)
  • onConfirm (via @Output()) — emit when the user confirms/completes the action
  • onCancel (via @Output()) — emit when the user cancels

Note: The cards config on pillar.init() is deprecated. Use the render prop on injectPillarTool() instead for better co-location and type safety.

Theme Synchronization

Sync Pillar's theme with your app's dark mode:

@Component({...})
export class ThemeToggleComponent {
  private pillar = injectPillar();

  toggleDarkMode(isDark: boolean) {
    this.pillar.setTheme({ mode: isDark ? 'dark' : 'light' });
  }
}

Subscribe to Events

@Component({...})
export class AnalyticsComponent implements OnInit, OnDestroy {
  private pillar = injectPillar();
  private unsubscribes: (() => void)[] = [];

  ngOnInit() {
    this.unsubscribes.push(
      this.pillar.on('panel:open', () => {
        analytics.track('help_panel_opened');
      }),
      this.pillar.on('task:execute', (task) => {
        analytics.track('task_executed', { name: task.name });
      })
    );
  }

  ngOnDestroy() {
    this.unsubscribes.forEach(unsub => unsub());
  }
}

Configuration Options

interface PillarInitConfig {
  /** Your product key from app.trypillar.com */
  productKey: string;

  /** Additional SDK configuration */
  config?: {
    panel?: {
      /** Panel placement: 'auto' | 'manual' */
      container?: string;
      /** Use Shadow DOM for style isolation */
      useShadowDOM?: boolean;
    };
    theme?: {
      /** Theme mode: 'light' | 'dark' | 'auto' */
      mode?: string;
      /** Custom colors */
      colors?: {
        primary?: string;
        // ... other color options
      };
    };
    // ... other options
  };

  /** Global task handler */
  onTask?: (task: TaskExecutePayload) => void;

  /** Custom card components */
  cards?: Record<string, Type<any>>;
}

License

MIT