@pillar-ai/angular 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,695 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, NgZone, ApplicationRef, EnvironmentInjector, signal, computed, createComponent, Injectable, input, effect, ViewChild, ChangeDetectionStrategy, Component, DestroyRef } from '@angular/core';
3
+ import { Pillar } from '@pillar-ai/sdk';
4
+
5
+ /**
6
+ * PillarService
7
+ * Angular service that initializes and manages the Pillar SDK
8
+ */
9
+ class PillarService {
10
+ ngZone = inject(NgZone);
11
+ appRef = inject(ApplicationRef);
12
+ environmentInjector = inject(EnvironmentInjector);
13
+ // Internal state
14
+ _pillar = signal(null);
15
+ cleanupFns = [];
16
+ cardRefs = new Map();
17
+ onTaskCallback = null;
18
+ registeredCards = null;
19
+ // Public signals
20
+ state = signal('uninitialized');
21
+ isReady = computed(() => this.state() === 'ready');
22
+ isPanelOpen = signal(false);
23
+ /**
24
+ * Get the Pillar SDK instance.
25
+ */
26
+ getInstance() {
27
+ return this._pillar();
28
+ }
29
+ /**
30
+ * Initialize the Pillar SDK.
31
+ * Call this in your app's initialization (e.g., APP_INITIALIZER).
32
+ *
33
+ * @param initConfig - Configuration options
34
+ */
35
+ async init(initConfig) {
36
+ const { productKey, helpCenter, config, onTask, cards } = initConfig;
37
+ // Support both productKey (new) and helpCenter (deprecated)
38
+ const resolvedKey = productKey ?? helpCenter;
39
+ if (helpCenter && !productKey) {
40
+ console.warn('[Pillar Angular] "helpCenter" is deprecated. Use "productKey" instead.');
41
+ }
42
+ // Store callbacks for later use
43
+ this.onTaskCallback = onTask ?? null;
44
+ this.registeredCards = cards ?? null;
45
+ try {
46
+ // Pillar is a singleton - check if already initialized
47
+ const existingInstance = Pillar.getInstance();
48
+ if (existingInstance) {
49
+ // Reuse existing instance (preserves chat history, panel state, etc.)
50
+ this._pillar.set(existingInstance);
51
+ this.state.set(existingInstance.state);
52
+ this.subscribeToEvents(existingInstance);
53
+ this.registerCards(existingInstance);
54
+ return;
55
+ }
56
+ // Initialize new instance
57
+ const instance = await Pillar.init({
58
+ productKey: resolvedKey,
59
+ ...config,
60
+ });
61
+ this.ngZone.run(() => {
62
+ this._pillar.set(instance);
63
+ this.state.set(instance.state);
64
+ });
65
+ this.subscribeToEvents(instance);
66
+ this.registerCards(instance);
67
+ }
68
+ catch (error) {
69
+ console.error('[Pillar Angular] Failed to initialize:', error);
70
+ this.ngZone.run(() => {
71
+ this.state.set('error');
72
+ });
73
+ throw error;
74
+ }
75
+ }
76
+ /**
77
+ * Subscribe to SDK events and sync to Angular signals.
78
+ */
79
+ subscribeToEvents(instance) {
80
+ // Panel open/close events
81
+ const unsubOpen = instance.on('panel:open', () => {
82
+ this.ngZone.run(() => {
83
+ this.isPanelOpen.set(true);
84
+ });
85
+ });
86
+ this.cleanupFns.push(unsubOpen);
87
+ const unsubClose = instance.on('panel:close', () => {
88
+ this.ngZone.run(() => {
89
+ this.isPanelOpen.set(false);
90
+ });
91
+ });
92
+ this.cleanupFns.push(unsubClose);
93
+ // State change events
94
+ const unsubReady = instance.on('ready', () => {
95
+ this.ngZone.run(() => {
96
+ this.state.set('ready');
97
+ });
98
+ });
99
+ this.cleanupFns.push(unsubReady);
100
+ const unsubError = instance.on('error', () => {
101
+ this.ngZone.run(() => {
102
+ this.state.set('error');
103
+ });
104
+ });
105
+ this.cleanupFns.push(unsubError);
106
+ // Task execution events
107
+ if (this.onTaskCallback) {
108
+ const callback = this.onTaskCallback;
109
+ const unsubTask = instance.on('task:execute', (task) => {
110
+ this.ngZone.run(() => {
111
+ callback(task);
112
+ });
113
+ });
114
+ this.cleanupFns.push(unsubTask);
115
+ }
116
+ }
117
+ /**
118
+ * Register custom card components.
119
+ */
120
+ registerCards(instance) {
121
+ if (!this.registeredCards)
122
+ return;
123
+ Object.entries(this.registeredCards).forEach(([cardType, CardComponent]) => {
124
+ const unsubscribe = instance.registerCard(cardType, (container, data, callbacks) => {
125
+ // Create an Angular component dynamically
126
+ const componentRef = createComponent(CardComponent, {
127
+ environmentInjector: this.environmentInjector,
128
+ });
129
+ // Set inputs
130
+ componentRef.setInput('data', data);
131
+ componentRef.setInput('onConfirm', callbacks.onConfirm);
132
+ componentRef.setInput('onCancel', callbacks.onCancel);
133
+ componentRef.setInput('onStateChange', callbacks.onStateChange);
134
+ // Attach to the application
135
+ this.appRef.attachView(componentRef.hostView);
136
+ // Append to container
137
+ container.appendChild(componentRef.location.nativeElement);
138
+ this.cardRefs.set(container, componentRef);
139
+ // Return cleanup function
140
+ return () => {
141
+ const ref = this.cardRefs.get(container);
142
+ if (ref) {
143
+ this.appRef.detachView(ref.hostView);
144
+ ref.destroy();
145
+ this.cardRefs.delete(container);
146
+ }
147
+ };
148
+ });
149
+ this.cleanupFns.push(unsubscribe);
150
+ });
151
+ }
152
+ /**
153
+ * Open the help panel.
154
+ */
155
+ open(options) {
156
+ this._pillar()?.open(options);
157
+ }
158
+ /**
159
+ * Close the help panel.
160
+ */
161
+ close() {
162
+ this._pillar()?.close();
163
+ }
164
+ /**
165
+ * Toggle the help panel.
166
+ */
167
+ toggle() {
168
+ this._pillar()?.toggle();
169
+ }
170
+ /**
171
+ * Open a specific article.
172
+ */
173
+ openArticle(slug) {
174
+ this._pillar()?.open({ article: slug });
175
+ }
176
+ /**
177
+ * Open a specific category.
178
+ */
179
+ async openCategory(slug) {
180
+ this._pillar()?.navigate('category', { slug });
181
+ }
182
+ /**
183
+ * Perform a search.
184
+ */
185
+ search(query) {
186
+ this._pillar()?.open({ search: query });
187
+ }
188
+ /**
189
+ * Navigate to a specific view.
190
+ */
191
+ navigate(view, params) {
192
+ this._pillar()?.navigate(view, params);
193
+ }
194
+ /**
195
+ * Update the panel theme at runtime.
196
+ */
197
+ setTheme(theme) {
198
+ this._pillar()?.setTheme(theme);
199
+ }
200
+ /**
201
+ * Enable or disable the text selection "Ask AI" popover.
202
+ */
203
+ setTextSelectionEnabled(enabled) {
204
+ this._pillar()?.setTextSelectionEnabled(enabled);
205
+ }
206
+ /**
207
+ * Subscribe to SDK events.
208
+ */
209
+ on(event, callback) {
210
+ const pillar = this._pillar();
211
+ if (!pillar) {
212
+ return () => { };
213
+ }
214
+ // Wrap callback to run in Angular zone
215
+ const wrappedCallback = (data) => {
216
+ this.ngZone.run(() => callback(data));
217
+ };
218
+ return pillar.on(event, wrappedCallback);
219
+ }
220
+ /**
221
+ * Register a task handler.
222
+ */
223
+ onTask(taskName, handler) {
224
+ const pillar = this._pillar();
225
+ if (!pillar) {
226
+ return () => { };
227
+ }
228
+ // Wrap handler to run in Angular zone
229
+ const wrappedHandler = (data) => {
230
+ this.ngZone.run(() => handler(data));
231
+ };
232
+ return pillar.onTask(taskName, wrappedHandler);
233
+ }
234
+ /**
235
+ * Define a tool with metadata and handler.
236
+ * Returns an unsubscribe function to remove the tool.
237
+ *
238
+ * @example
239
+ * ```typescript
240
+ * const unsubscribe = pillarService.defineTool({
241
+ * name: 'add_to_cart',
242
+ * description: 'Add a product to the shopping cart',
243
+ * inputSchema: {
244
+ * type: 'object',
245
+ * properties: {
246
+ * productId: { type: 'string' },
247
+ * quantity: { type: 'number' },
248
+ * },
249
+ * required: ['productId', 'quantity'],
250
+ * },
251
+ * execute: async ({ productId, quantity }) => {
252
+ * await this.cartService.add(productId, quantity);
253
+ * return { content: [{ type: 'text', text: 'Added to cart' }] };
254
+ * },
255
+ * });
256
+ *
257
+ * // Later, to unregister:
258
+ * unsubscribe();
259
+ * ```
260
+ */
261
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
262
+ defineTool(schema) {
263
+ const pillar = this._pillar();
264
+ if (!pillar) {
265
+ console.warn('[Pillar Angular] Cannot define tool - SDK not initialized');
266
+ return () => { };
267
+ }
268
+ // Wrap execute to run in Angular zone
269
+ const wrappedSchema = {
270
+ ...schema,
271
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
272
+ execute: async (input) => {
273
+ return this.ngZone.run(() => schema.execute(input));
274
+ },
275
+ };
276
+ return pillar.defineTool(wrappedSchema);
277
+ }
278
+ /**
279
+ * Mount the panel to a specific container element.
280
+ * Used for manual panel placement with PillarPanelComponent.
281
+ */
282
+ mountPanelTo(container) {
283
+ this._pillar()?.mountPanelTo(container);
284
+ }
285
+ /**
286
+ * Cleanup resources.
287
+ * Note: We intentionally don't call Pillar.destroy() here to preserve
288
+ * conversation history across route changes. Call Pillar.destroy()
289
+ * explicitly if you need to fully reset the SDK.
290
+ */
291
+ ngOnDestroy() {
292
+ // Run all cleanup functions
293
+ this.cleanupFns.forEach((cleanup) => cleanup());
294
+ this.cleanupFns.length = 0;
295
+ // Destroy all card component refs
296
+ this.cardRefs.forEach((ref) => {
297
+ this.appRef.detachView(ref.hostView);
298
+ ref.destroy();
299
+ });
300
+ this.cardRefs.clear();
301
+ }
302
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PillarService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
303
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PillarService, providedIn: 'root' });
304
+ }
305
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PillarService, decorators: [{
306
+ type: Injectable,
307
+ args: [{ providedIn: 'root' }]
308
+ }] });
309
+
310
+ /**
311
+ * PillarPanelComponent
312
+ * Renders the Pillar help panel at a custom location in the DOM
313
+ */
314
+ /**
315
+ * Renders the Pillar help panel at a custom location in the DOM.
316
+ * Use this when you want to control where the panel is rendered instead of
317
+ * having it automatically appended to document.body.
318
+ *
319
+ * **Important**: When using this component, set `panel.container: 'manual'` in your
320
+ * Pillar configuration to prevent automatic mounting.
321
+ *
322
+ * @example
323
+ * ```typescript
324
+ * // app.config.ts
325
+ * function initPillar() {
326
+ * const pillar = inject(PillarService);
327
+ * return () => pillar.init({
328
+ * productKey: 'your-product-key',
329
+ * config: { panel: { container: 'manual' } }
330
+ * });
331
+ * }
332
+ *
333
+ * // layout.component.ts
334
+ * @Component({
335
+ * selector: 'app-layout',
336
+ * standalone: true,
337
+ * imports: [PillarPanelComponent],
338
+ * template: `
339
+ * <div class="layout">
340
+ * <app-sidebar />
341
+ * <pillar-panel class="help-panel-container" />
342
+ * <main>
343
+ * <router-outlet />
344
+ * </main>
345
+ * </div>
346
+ * `,
347
+ * })
348
+ * export class LayoutComponent {}
349
+ * ```
350
+ */
351
+ class PillarPanelComponent {
352
+ containerRef;
353
+ pillarService = inject(PillarService);
354
+ hasMounted = false;
355
+ effectRef = null;
356
+ /**
357
+ * Optional class to add to the container element.
358
+ * Use host binding for styling instead if possible.
359
+ */
360
+ containerClass = input('');
361
+ ngAfterViewInit() {
362
+ // Use effect to react to isReady signal changes
363
+ this.effectRef = effect(() => {
364
+ const isReady = this.pillarService.isReady();
365
+ if (isReady && !this.hasMounted && this.containerRef?.nativeElement) {
366
+ this.pillarService.mountPanelTo(this.containerRef.nativeElement);
367
+ this.hasMounted = true;
368
+ }
369
+ });
370
+ }
371
+ ngOnDestroy() {
372
+ // Effect is automatically cleaned up when component is destroyed
373
+ // Panel cleanup is handled by PillarService
374
+ this.effectRef?.destroy();
375
+ }
376
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PillarPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
377
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: PillarPanelComponent, isStandalone: true, selector: "pillar-panel", inputs: { containerClass: { classPropertyName: "containerClass", publicName: "containerClass", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "containerRef", first: true, predicate: ["container"], descendants: true, static: true }], ngImport: i0, template: '<div #container data-pillar-panel-container></div>', isInline: true, styles: [":host{display:block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
378
+ }
379
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PillarPanelComponent, decorators: [{
380
+ type: Component,
381
+ args: [{ selector: 'pillar-panel', standalone: true, template: '<div #container data-pillar-panel-container></div>', changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}\n"] }]
382
+ }], propDecorators: { containerRef: [{
383
+ type: ViewChild,
384
+ args: ['container', { static: true }]
385
+ }] } });
386
+
387
+ /**
388
+ * injectPillar Function
389
+ * Angular-idiomatic injection helper for accessing Pillar SDK
390
+ */
391
+ /**
392
+ * Angular injection function to access the Pillar SDK.
393
+ * Use this in components, directives, or services to interact with Pillar.
394
+ *
395
+ * Must be called within an injection context (constructor, field initializer, or inject()).
396
+ *
397
+ * @example Basic usage (untyped)
398
+ * ```typescript
399
+ * @Component({
400
+ * selector: 'app-help-button',
401
+ * standalone: true,
402
+ * template: `
403
+ * <button (click)="toggle()">
404
+ * {{ isPanelOpen() ? 'Close Help' : 'Get Help' }}
405
+ * </button>
406
+ * `,
407
+ * })
408
+ * export class HelpButtonComponent {
409
+ * private pillar = injectPillar();
410
+ * isPanelOpen = this.pillar.isPanelOpen;
411
+ * toggle = this.pillar.toggle;
412
+ * }
413
+ * ```
414
+ *
415
+ * @example Type-safe onTask with action definitions
416
+ * ```typescript
417
+ * import { actions } from '@/lib/pillar/actions';
418
+ *
419
+ * @Component({...})
420
+ * export class MyComponent implements OnInit, OnDestroy {
421
+ * private pillar = injectPillar<typeof actions>();
422
+ * private unsubscribe?: () => void;
423
+ *
424
+ * ngOnInit() {
425
+ * // TypeScript knows data has the correct shape
426
+ * this.unsubscribe = this.pillar.onTask('add_new_source', (data) => {
427
+ * console.log(data.url); // ✓ Typed!
428
+ * });
429
+ * }
430
+ *
431
+ * ngOnDestroy() {
432
+ * this.unsubscribe?.();
433
+ * }
434
+ * }
435
+ * ```
436
+ */
437
+ function injectPillar() {
438
+ const service = inject(PillarService);
439
+ // Create a type-safe wrapper around pillar.onTask
440
+ const onTask = (taskName, handler) => {
441
+ // Cast handler to match the SDK's expected type
442
+ // The runtime behavior is the same, this is just for type narrowing
443
+ return service.onTask(taskName, handler);
444
+ };
445
+ return {
446
+ pillar: () => service.getInstance(),
447
+ state: service.state,
448
+ isReady: service.isReady,
449
+ isPanelOpen: service.isPanelOpen,
450
+ open: service.open.bind(service),
451
+ close: service.close.bind(service),
452
+ toggle: service.toggle.bind(service),
453
+ openArticle: service.openArticle.bind(service),
454
+ openCategory: service.openCategory.bind(service),
455
+ search: service.search.bind(service),
456
+ navigate: service.navigate.bind(service),
457
+ setTheme: service.setTheme.bind(service),
458
+ setTextSelectionEnabled: service.setTextSelectionEnabled.bind(service),
459
+ on: service.on.bind(service),
460
+ onTask,
461
+ };
462
+ }
463
+
464
+ /**
465
+ * injectHelpPanel Function
466
+ * Angular injection helper for panel-specific controls
467
+ */
468
+ /**
469
+ * Angular injection function for panel-specific controls.
470
+ * Provides a simplified API focused on panel operations.
471
+ *
472
+ * Must be called within an injection context (constructor, field initializer, or inject()).
473
+ *
474
+ * @example
475
+ * ```typescript
476
+ * @Component({
477
+ * selector: 'app-help-menu',
478
+ * standalone: true,
479
+ * template: `
480
+ * <div>
481
+ * <button (click)="toggle()">
482
+ * {{ isOpen() ? 'Close' : 'Help' }}
483
+ * </button>
484
+ * <button (click)="openChat()">Ask AI</button>
485
+ * </div>
486
+ * `,
487
+ * })
488
+ * export class HelpMenuComponent {
489
+ * private panel = injectHelpPanel();
490
+ * isOpen = this.panel.isOpen;
491
+ * toggle = this.panel.toggle;
492
+ * openChat = this.panel.openChat;
493
+ * }
494
+ * ```
495
+ */
496
+ function injectHelpPanel() {
497
+ const service = inject(PillarService);
498
+ const openSearch = (query) => {
499
+ if (query) {
500
+ service.search(query);
501
+ }
502
+ else {
503
+ service.open({ view: 'search' });
504
+ }
505
+ };
506
+ const openChat = () => {
507
+ service.navigate('chat');
508
+ };
509
+ return {
510
+ isOpen: computed(() => service.isPanelOpen()),
511
+ open: service.open.bind(service),
512
+ close: service.close.bind(service),
513
+ toggle: service.toggle.bind(service),
514
+ openArticle: service.openArticle.bind(service),
515
+ openCategory: service.openCategory.bind(service),
516
+ openSearch,
517
+ openChat,
518
+ };
519
+ }
520
+
521
+ /**
522
+ * injectPillarTool Function
523
+ * Angular-idiomatic injection helper for registering Pillar tools
524
+ *
525
+ * Register one or more tools with co-located metadata and handlers.
526
+ * Tools are registered when called and automatically unregistered
527
+ * when the component is destroyed.
528
+ *
529
+ * @example Single tool
530
+ * ```typescript
531
+ * import { Component } from '@angular/core';
532
+ * import { injectPillarTool } from '@pillar-ai/angular';
533
+ *
534
+ * @Component({
535
+ * selector: 'app-cart-button',
536
+ * standalone: true,
537
+ * template: `<button>Cart</button>`,
538
+ * })
539
+ * export class CartButtonComponent {
540
+ * constructor() {
541
+ * injectPillarTool({
542
+ * name: 'add_to_cart',
543
+ * description: 'Add a product to the shopping cart',
544
+ * inputSchema: {
545
+ * type: 'object',
546
+ * properties: {
547
+ * productId: { type: 'string', description: 'Product ID' },
548
+ * quantity: { type: 'number', description: 'Quantity to add' },
549
+ * },
550
+ * required: ['productId', 'quantity'],
551
+ * },
552
+ * execute: async ({ productId, quantity }) => {
553
+ * await cartApi.add(productId, quantity);
554
+ * return { content: [{ type: 'text', text: 'Added to cart' }] };
555
+ * },
556
+ * });
557
+ * }
558
+ * }
559
+ * ```
560
+ *
561
+ * @example Multiple tools
562
+ * ```typescript
563
+ * import { Component } from '@angular/core';
564
+ * import { injectPillarTool } from '@pillar-ai/angular';
565
+ *
566
+ * @Component({
567
+ * selector: 'app-billing-page',
568
+ * standalone: true,
569
+ * template: `<div>Billing Content</div>`,
570
+ * })
571
+ * export class BillingPageComponent {
572
+ * constructor() {
573
+ * injectPillarTool([
574
+ * {
575
+ * name: 'get_current_plan',
576
+ * description: 'Get the current billing plan',
577
+ * execute: async () => ({ plan: 'pro', price: 29 }),
578
+ * },
579
+ * {
580
+ * name: 'upgrade_plan',
581
+ * description: 'Upgrade to a higher plan',
582
+ * inputSchema: {
583
+ * type: 'object',
584
+ * properties: { planId: { type: 'string' } },
585
+ * required: ['planId'],
586
+ * },
587
+ * execute: async ({ planId }) => {
588
+ * await billingApi.upgrade(planId);
589
+ * return { content: [{ type: 'text', text: 'Upgraded!' }] };
590
+ * },
591
+ * },
592
+ * ]);
593
+ * }
594
+ * }
595
+ * ```
596
+ */
597
+ /**
598
+ * Register one or more Pillar tools with co-located metadata and handlers.
599
+ *
600
+ * The tools are registered when this function is called and automatically
601
+ * unregistered when the component is destroyed. Must be called within
602
+ * an injection context (constructor, field initializer, or inject()).
603
+ *
604
+ * @param schemaOrSchemas - Single tool schema or array of tool schemas
605
+ */
606
+ function injectPillarTool(
607
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
608
+ schemaOrSchemas) {
609
+ const service = inject(PillarService);
610
+ const destroyRef = inject(DestroyRef);
611
+ // Normalize to array for consistent handling
612
+ const schemas = Array.isArray(schemaOrSchemas)
613
+ ? schemaOrSchemas
614
+ : [schemaOrSchemas];
615
+ // Track unsubscribe functions
616
+ const unsubscribes = [];
617
+ // Register all tools
618
+ schemas.forEach((schema) => {
619
+ const unsub = service.defineTool(schema);
620
+ unsubscribes.push(unsub);
621
+ });
622
+ // Cleanup on destroy
623
+ destroyRef.onDestroy(() => {
624
+ unsubscribes.forEach((unsub) => unsub());
625
+ });
626
+ }
627
+ /** @deprecated Use injectPillarTool instead */
628
+ const injectPillarAction = injectPillarTool;
629
+
630
+ /**
631
+ * @pillar-ai/angular - Angular bindings for Pillar SDK
632
+ *
633
+ * @example
634
+ * ```typescript
635
+ * // app.config.ts
636
+ * import { ApplicationConfig, APP_INITIALIZER, inject } from '@angular/core';
637
+ * import { PillarService } from '@pillar-ai/angular';
638
+ *
639
+ * function initPillar() {
640
+ * const pillar = inject(PillarService);
641
+ * return () => pillar.init({ productKey: 'your-product-key' });
642
+ * }
643
+ *
644
+ * export const appConfig: ApplicationConfig = {
645
+ * providers: [
646
+ * { provide: APP_INITIALIZER, useFactory: initPillar, multi: true },
647
+ * ],
648
+ * };
649
+ *
650
+ * // help-button.component.ts
651
+ * import { Component } from '@angular/core';
652
+ * import { injectPillar } from '@pillar-ai/angular';
653
+ *
654
+ * @Component({
655
+ * selector: 'app-help-button',
656
+ * standalone: true,
657
+ * template: `
658
+ * <button (click)="toggle()">
659
+ * {{ isPanelOpen() ? 'Close Help' : 'Get Help' }}
660
+ * </button>
661
+ * `,
662
+ * })
663
+ * export class HelpButtonComponent {
664
+ * private pillar = injectPillar();
665
+ * isPanelOpen = this.pillar.isPanelOpen;
666
+ * toggle = this.pillar.toggle;
667
+ * }
668
+ *
669
+ * // Custom panel placement example:
670
+ * // layout.component.ts
671
+ * import { Component } from '@angular/core';
672
+ * import { PillarPanelComponent } from '@pillar-ai/angular';
673
+ *
674
+ * @Component({
675
+ * selector: 'app-layout',
676
+ * standalone: true,
677
+ * imports: [PillarPanelComponent],
678
+ * template: `
679
+ * <div class="layout">
680
+ * <pillar-panel class="custom-panel" />
681
+ * <main>Your content</main>
682
+ * </div>
683
+ * `,
684
+ * })
685
+ * export class LayoutComponent {}
686
+ * ```
687
+ */
688
+ // Service
689
+
690
+ /**
691
+ * Generated bundle index. Do not edit.
692
+ */
693
+
694
+ export { PillarPanelComponent, PillarService, injectHelpPanel, injectPillar, injectPillarAction, injectPillarTool };
695
+ //# sourceMappingURL=pillar-ai-angular.mjs.map