@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.
- package/LICENSE +21 -0
- package/README.md +357 -0
- package/dist/LICENSE +21 -0
- package/dist/README.md +357 -0
- package/dist/esm2022/lib/inject-help-panel.mjs +59 -0
- package/dist/esm2022/lib/inject-pillar-tool.mjs +111 -0
- package/dist/esm2022/lib/inject-pillar.mjs +79 -0
- package/dist/esm2022/lib/pillar-panel.component.mjs +80 -0
- package/dist/esm2022/lib/pillar.service.mjs +308 -0
- package/dist/esm2022/lib/types.mjs +5 -0
- package/dist/esm2022/pillar-ai-angular.mjs +5 -0
- package/dist/esm2022/public-api.mjs +67 -0
- package/dist/fesm2022/pillar-ai-angular.mjs +695 -0
- package/dist/fesm2022/pillar-ai-angular.mjs.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/lib/inject-help-panel.d.ts +34 -0
- package/dist/lib/inject-pillar-tool.d.ts +89 -0
- package/dist/lib/inject-pillar.d.ts +96 -0
- package/dist/lib/pillar-panel.component.d.ts +58 -0
- package/dist/lib/pillar.service.d.ts +126 -0
- package/dist/lib/types.d.ts +119 -0
- package/dist/public-api.d.ts +77 -0
- package/package.json +73 -0
package/dist/README.md
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# @pillar-ai/angular
|
|
2
|
+
|
|
3
|
+
Angular SDK for the [Pillar](https://trypillar.com) open-source AI copilot — embed a product assistant in your Angular app that executes tasks, not just answers questions. [GitHub](https://github.com/pillarhq/pillar) · [Docs](https://trypillar.com/docs)
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@pillar-ai/angular)
|
|
6
|
+
[](https://www.npmjs.com/package/@pillar-ai/angular)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
|
|
10
|
+
## What is Pillar?
|
|
11
|
+
|
|
12
|
+
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.
|
|
13
|
+
|
|
14
|
+
For example, a user could ask:
|
|
15
|
+
|
|
16
|
+
> "Close the Walmart deal as won in Salesforce and notify implementation"
|
|
17
|
+
|
|
18
|
+
> "Create a P1 bug in Linear for the checkout crash and add it to this sprint"
|
|
19
|
+
|
|
20
|
+
> "How do I set up a webhook in Stripe?"
|
|
21
|
+
|
|
22
|
+
Pillar understands the intent, builds a multi-step plan, and executes it client-side with the user's session.
|
|
23
|
+
|
|
24
|
+
## Requirements
|
|
25
|
+
|
|
26
|
+
- Angular 17.0.0 or higher
|
|
27
|
+
- `@pillar-ai/sdk` (installed automatically as a dependency)
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install @pillar-ai/angular
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
### 1. Initialize Pillar in your app config
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
// app.config.ts
|
|
41
|
+
import { ApplicationConfig, APP_INITIALIZER, inject } from '@angular/core';
|
|
42
|
+
import { PillarService } from '@pillar-ai/angular';
|
|
43
|
+
|
|
44
|
+
function initPillar() {
|
|
45
|
+
const pillar = inject(PillarService);
|
|
46
|
+
return () => pillar.init({ productKey: 'your-product-key' });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const appConfig: ApplicationConfig = {
|
|
50
|
+
providers: [
|
|
51
|
+
{ provide: APP_INITIALIZER, useFactory: initPillar, multi: true },
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 2. Use Pillar in your components
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// help-button.component.ts
|
|
60
|
+
import { Component } from '@angular/core';
|
|
61
|
+
import { injectPillar } from '@pillar-ai/angular';
|
|
62
|
+
|
|
63
|
+
@Component({
|
|
64
|
+
selector: 'app-help-button',
|
|
65
|
+
standalone: true,
|
|
66
|
+
template: `
|
|
67
|
+
<button (click)="toggle()">
|
|
68
|
+
{{ isPanelOpen() ? 'Close Help' : 'Get Help' }}
|
|
69
|
+
</button>
|
|
70
|
+
`,
|
|
71
|
+
})
|
|
72
|
+
export class HelpButtonComponent {
|
|
73
|
+
private pillar = injectPillar();
|
|
74
|
+
isPanelOpen = this.pillar.isPanelOpen;
|
|
75
|
+
toggle = this.pillar.toggle;
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## API Reference
|
|
80
|
+
|
|
81
|
+
### PillarService
|
|
82
|
+
|
|
83
|
+
Injectable service that manages the Pillar SDK lifecycle.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import { PillarService } from '@pillar-ai/angular';
|
|
87
|
+
|
|
88
|
+
@Component({...})
|
|
89
|
+
export class MyComponent {
|
|
90
|
+
constructor(private pillarService: PillarService) {}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### Methods
|
|
95
|
+
|
|
96
|
+
| Method | Description |
|
|
97
|
+
|--------|-------------|
|
|
98
|
+
| `init(config)` | Initialize the SDK with your product key |
|
|
99
|
+
| `open(options?)` | Open the help panel |
|
|
100
|
+
| `close()` | Close the help panel |
|
|
101
|
+
| `toggle()` | Toggle the help panel |
|
|
102
|
+
| `openArticle(slug)` | Open a specific article |
|
|
103
|
+
| `openCategory(slug)` | Navigate to a category |
|
|
104
|
+
| `search(query)` | Open search with a query |
|
|
105
|
+
| `navigate(view, params?)` | Navigate to a specific view |
|
|
106
|
+
| `setTheme(theme)` | Update the panel theme |
|
|
107
|
+
| `setTextSelectionEnabled(enabled)` | Toggle text selection popover |
|
|
108
|
+
| `on(event, callback)` | Subscribe to SDK events |
|
|
109
|
+
| `onTask(taskName, handler)` | Register a task handler |
|
|
110
|
+
|
|
111
|
+
#### Signals
|
|
112
|
+
|
|
113
|
+
| Signal | Type | Description |
|
|
114
|
+
|--------|------|-------------|
|
|
115
|
+
| `state` | `WritableSignal<PillarState>` | Current SDK state |
|
|
116
|
+
| `isReady` | `Signal<boolean>` | Whether SDK is ready |
|
|
117
|
+
| `isPanelOpen` | `WritableSignal<boolean>` | Whether panel is open |
|
|
118
|
+
|
|
119
|
+
### injectPillar()
|
|
120
|
+
|
|
121
|
+
Angular injection function for accessing the Pillar SDK with full functionality.
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import { injectPillar } from '@pillar-ai/angular';
|
|
125
|
+
|
|
126
|
+
@Component({...})
|
|
127
|
+
export class MyComponent {
|
|
128
|
+
private pillar = injectPillar();
|
|
129
|
+
|
|
130
|
+
handleHelp() {
|
|
131
|
+
this.pillar.open({ view: 'chat' });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### injectHelpPanel()
|
|
137
|
+
|
|
138
|
+
Simplified injection function focused on panel controls.
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
import { injectHelpPanel } from '@pillar-ai/angular';
|
|
142
|
+
|
|
143
|
+
@Component({...})
|
|
144
|
+
export class HelpMenuComponent {
|
|
145
|
+
private panel = injectHelpPanel();
|
|
146
|
+
|
|
147
|
+
isOpen = this.panel.isOpen;
|
|
148
|
+
toggle = this.panel.toggle;
|
|
149
|
+
openChat = this.panel.openChat;
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### PillarPanelComponent
|
|
154
|
+
|
|
155
|
+
Standalone component for custom panel placement.
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import { PillarPanelComponent } from '@pillar-ai/angular';
|
|
159
|
+
|
|
160
|
+
@Component({
|
|
161
|
+
selector: 'app-layout',
|
|
162
|
+
standalone: true,
|
|
163
|
+
imports: [PillarPanelComponent],
|
|
164
|
+
template: `
|
|
165
|
+
<div class="layout">
|
|
166
|
+
<pillar-panel class="help-panel" />
|
|
167
|
+
<main>
|
|
168
|
+
<router-outlet />
|
|
169
|
+
</main>
|
|
170
|
+
</div>
|
|
171
|
+
`,
|
|
172
|
+
})
|
|
173
|
+
export class LayoutComponent {}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Important**: When using `PillarPanelComponent`, set `panel.container: 'manual'` in your config:
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
pillar.init({
|
|
180
|
+
productKey: 'your-product-key',
|
|
181
|
+
config: { panel: { container: 'manual' } }
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Advanced Usage
|
|
186
|
+
|
|
187
|
+
### Type-Safe Task Handlers
|
|
188
|
+
|
|
189
|
+
Define your actions and get full TypeScript support:
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
// lib/pillar/actions.ts
|
|
193
|
+
import { defineActions } from '@pillar-ai/sdk';
|
|
194
|
+
|
|
195
|
+
export const actions = defineActions({
|
|
196
|
+
invite_member: {
|
|
197
|
+
description: 'Invite a team member',
|
|
198
|
+
data: {
|
|
199
|
+
email: { type: 'string', description: 'Email address' },
|
|
200
|
+
role: { type: 'string', description: 'Member role' },
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
// my.component.ts
|
|
208
|
+
import { Component, OnInit, OnDestroy } from '@angular/core';
|
|
209
|
+
import { injectPillar } from '@pillar-ai/angular';
|
|
210
|
+
import { actions } from './lib/pillar/actions';
|
|
211
|
+
|
|
212
|
+
@Component({...})
|
|
213
|
+
export class MyComponent implements OnInit, OnDestroy {
|
|
214
|
+
private pillar = injectPillar<typeof actions>();
|
|
215
|
+
private unsubscribe?: () => void;
|
|
216
|
+
|
|
217
|
+
ngOnInit() {
|
|
218
|
+
// TypeScript knows the exact shape of data
|
|
219
|
+
this.unsubscribe = this.pillar.onTask('invite_member', (data) => {
|
|
220
|
+
console.log(data.email); // ✓ Typed as string
|
|
221
|
+
console.log(data.role); // ✓ Typed as string
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
ngOnDestroy() {
|
|
226
|
+
this.unsubscribe?.();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Custom Card Components
|
|
232
|
+
|
|
233
|
+
Render custom Angular components for inline UI actions:
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
// invite-card.component.ts
|
|
237
|
+
import { Component, input } from '@angular/core';
|
|
238
|
+
import type { CardComponentProps } from '@pillar-ai/angular';
|
|
239
|
+
|
|
240
|
+
@Component({
|
|
241
|
+
selector: 'app-invite-card',
|
|
242
|
+
standalone: true,
|
|
243
|
+
template: `
|
|
244
|
+
<div class="invite-card">
|
|
245
|
+
<h3>Invite Team Members</h3>
|
|
246
|
+
<input [(ngModel)]="email" placeholder="Email address" />
|
|
247
|
+
<button (click)="confirm()">Send Invite</button>
|
|
248
|
+
<button (click)="cancel()">Cancel</button>
|
|
249
|
+
</div>
|
|
250
|
+
`,
|
|
251
|
+
})
|
|
252
|
+
export class InviteCardComponent implements CardComponentProps {
|
|
253
|
+
data = input.required<Record<string, unknown>>();
|
|
254
|
+
onConfirm = input.required<(data?: Record<string, unknown>) => void>();
|
|
255
|
+
onCancel = input.required<() => void>();
|
|
256
|
+
onStateChange = input<(state: 'loading' | 'success' | 'error', message?: string) => void>();
|
|
257
|
+
|
|
258
|
+
email = '';
|
|
259
|
+
|
|
260
|
+
confirm() {
|
|
261
|
+
this.onConfirm()({ email: this.email });
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
cancel() {
|
|
265
|
+
this.onCancel()();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
// app.config.ts
|
|
272
|
+
pillar.init({
|
|
273
|
+
productKey: 'your-product-key',
|
|
274
|
+
cards: {
|
|
275
|
+
invite_member: InviteCardComponent,
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Theme Synchronization
|
|
281
|
+
|
|
282
|
+
Sync Pillar's theme with your app's dark mode:
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
@Component({...})
|
|
286
|
+
export class ThemeToggleComponent {
|
|
287
|
+
private pillar = injectPillar();
|
|
288
|
+
|
|
289
|
+
toggleDarkMode(isDark: boolean) {
|
|
290
|
+
this.pillar.setTheme({ mode: isDark ? 'dark' : 'light' });
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Subscribe to Events
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
@Component({...})
|
|
299
|
+
export class AnalyticsComponent implements OnInit, OnDestroy {
|
|
300
|
+
private pillar = injectPillar();
|
|
301
|
+
private unsubscribes: (() => void)[] = [];
|
|
302
|
+
|
|
303
|
+
ngOnInit() {
|
|
304
|
+
this.unsubscribes.push(
|
|
305
|
+
this.pillar.on('panel:open', () => {
|
|
306
|
+
analytics.track('help_panel_opened');
|
|
307
|
+
}),
|
|
308
|
+
this.pillar.on('task:execute', (task) => {
|
|
309
|
+
analytics.track('task_executed', { name: task.name });
|
|
310
|
+
})
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
ngOnDestroy() {
|
|
315
|
+
this.unsubscribes.forEach(unsub => unsub());
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Configuration Options
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
interface PillarInitConfig {
|
|
324
|
+
/** Your product key from app.trypillar.com */
|
|
325
|
+
productKey: string;
|
|
326
|
+
|
|
327
|
+
/** Additional SDK configuration */
|
|
328
|
+
config?: {
|
|
329
|
+
panel?: {
|
|
330
|
+
/** Panel placement: 'auto' | 'manual' */
|
|
331
|
+
container?: string;
|
|
332
|
+
/** Use Shadow DOM for style isolation */
|
|
333
|
+
useShadowDOM?: boolean;
|
|
334
|
+
};
|
|
335
|
+
theme?: {
|
|
336
|
+
/** Theme mode: 'light' | 'dark' | 'auto' */
|
|
337
|
+
mode?: string;
|
|
338
|
+
/** Custom colors */
|
|
339
|
+
colors?: {
|
|
340
|
+
primary?: string;
|
|
341
|
+
// ... other color options
|
|
342
|
+
};
|
|
343
|
+
};
|
|
344
|
+
// ... other options
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
/** Global task handler */
|
|
348
|
+
onTask?: (task: TaskExecutePayload) => void;
|
|
349
|
+
|
|
350
|
+
/** Custom card components */
|
|
351
|
+
cards?: Record<string, Type<any>>;
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## License
|
|
356
|
+
|
|
357
|
+
MIT
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* injectHelpPanel Function
|
|
3
|
+
* Angular injection helper for panel-specific controls
|
|
4
|
+
*/
|
|
5
|
+
import { inject, computed } from '@angular/core';
|
|
6
|
+
import { PillarService } from './pillar.service';
|
|
7
|
+
/**
|
|
8
|
+
* Angular injection function for panel-specific controls.
|
|
9
|
+
* Provides a simplified API focused on panel operations.
|
|
10
|
+
*
|
|
11
|
+
* Must be called within an injection context (constructor, field initializer, or inject()).
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* @Component({
|
|
16
|
+
* selector: 'app-help-menu',
|
|
17
|
+
* standalone: true,
|
|
18
|
+
* template: `
|
|
19
|
+
* <div>
|
|
20
|
+
* <button (click)="toggle()">
|
|
21
|
+
* {{ isOpen() ? 'Close' : 'Help' }}
|
|
22
|
+
* </button>
|
|
23
|
+
* <button (click)="openChat()">Ask AI</button>
|
|
24
|
+
* </div>
|
|
25
|
+
* `,
|
|
26
|
+
* })
|
|
27
|
+
* export class HelpMenuComponent {
|
|
28
|
+
* private panel = injectHelpPanel();
|
|
29
|
+
* isOpen = this.panel.isOpen;
|
|
30
|
+
* toggle = this.panel.toggle;
|
|
31
|
+
* openChat = this.panel.openChat;
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export function injectHelpPanel() {
|
|
36
|
+
const service = inject(PillarService);
|
|
37
|
+
const openSearch = (query) => {
|
|
38
|
+
if (query) {
|
|
39
|
+
service.search(query);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
service.open({ view: 'search' });
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const openChat = () => {
|
|
46
|
+
service.navigate('chat');
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
isOpen: computed(() => service.isPanelOpen()),
|
|
50
|
+
open: service.open.bind(service),
|
|
51
|
+
close: service.close.bind(service),
|
|
52
|
+
toggle: service.toggle.bind(service),
|
|
53
|
+
openArticle: service.openArticle.bind(service),
|
|
54
|
+
openCategory: service.openCategory.bind(service),
|
|
55
|
+
openSearch,
|
|
56
|
+
openChat,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5qZWN0LWhlbHAtcGFuZWwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL2luamVjdC1oZWxwLXBhbmVsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVILE9BQU8sRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ2pELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUdqRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBMkJHO0FBQ0gsTUFBTSxVQUFVLGVBQWU7SUFDN0IsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBRXRDLE1BQU0sVUFBVSxHQUFHLENBQUMsS0FBYyxFQUFRLEVBQUU7UUFDMUMsSUFBSSxLQUFLLEVBQUUsQ0FBQztZQUNWLE9BQU8sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDeEIsQ0FBQzthQUFNLENBQUM7WUFDTixPQUFPLENBQUMsSUFBSSxDQUFDLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUM7UUFDbkMsQ0FBQztJQUNILENBQUMsQ0FBQztJQUVGLE1BQU0sUUFBUSxHQUFHLEdBQVMsRUFBRTtRQUMxQixPQUFPLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQzNCLENBQUMsQ0FBQztJQUVGLE9BQU87UUFDTCxNQUFNLEVBQUUsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLE9BQU8sQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUM3QyxJQUFJLEVBQUUsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDO1FBQ2hDLEtBQUssRUFBRSxPQUFPLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUM7UUFDbEMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQztRQUNwQyxXQUFXLEVBQUUsT0FBTyxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDO1FBQzlDLFlBQVksRUFBRSxPQUFPLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUM7UUFDaEQsVUFBVTtRQUNWLFFBQVE7S0FDVCxDQUFDO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogaW5qZWN0SGVscFBhbmVsIEZ1bmN0aW9uXG4gKiBBbmd1bGFyIGluamVjdGlvbiBoZWxwZXIgZm9yIHBhbmVsLXNwZWNpZmljIGNvbnRyb2xzXG4gKi9cblxuaW1wb3J0IHsgaW5qZWN0LCBjb21wdXRlZCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgUGlsbGFyU2VydmljZSB9IGZyb20gJy4vcGlsbGFyLnNlcnZpY2UnO1xuaW1wb3J0IHR5cGUgeyBJbmplY3RIZWxwUGFuZWxSZXN1bHQgfSBmcm9tICcuL3R5cGVzJztcblxuLyoqXG4gKiBBbmd1bGFyIGluamVjdGlvbiBmdW5jdGlvbiBmb3IgcGFuZWwtc3BlY2lmaWMgY29udHJvbHMuXG4gKiBQcm92aWRlcyBhIHNpbXBsaWZpZWQgQVBJIGZvY3VzZWQgb24gcGFuZWwgb3BlcmF0aW9ucy5cbiAqXG4gKiBNdXN0IGJlIGNhbGxlZCB3aXRoaW4gYW4gaW5qZWN0aW9uIGNvbnRleHQgKGNvbnN0cnVjdG9yLCBmaWVsZCBpbml0aWFsaXplciwgb3IgaW5qZWN0KCkpLlxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBAQ29tcG9uZW50KHtcbiAqICAgc2VsZWN0b3I6ICdhcHAtaGVscC1tZW51JyxcbiAqICAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAqICAgdGVtcGxhdGU6IGBcbiAqICAgICA8ZGl2PlxuICogICAgICAgPGJ1dHRvbiAoY2xpY2spPVwidG9nZ2xlKClcIj5cbiAqICAgICAgICAge3sgaXNPcGVuKCkgPyAnQ2xvc2UnIDogJ0hlbHAnIH19XG4gKiAgICAgICA8L2J1dHRvbj5cbiAqICAgICAgIDxidXR0b24gKGNsaWNrKT1cIm9wZW5DaGF0KClcIj5Bc2sgQUk8L2J1dHRvbj5cbiAqICAgICA8L2Rpdj5cbiAqICAgYCxcbiAqIH0pXG4gKiBleHBvcnQgY2xhc3MgSGVscE1lbnVDb21wb25lbnQge1xuICogICBwcml2YXRlIHBhbmVsID0gaW5qZWN0SGVscFBhbmVsKCk7XG4gKiAgIGlzT3BlbiA9IHRoaXMucGFuZWwuaXNPcGVuO1xuICogICB0b2dnbGUgPSB0aGlzLnBhbmVsLnRvZ2dsZTtcbiAqICAgb3BlbkNoYXQgPSB0aGlzLnBhbmVsLm9wZW5DaGF0O1xuICogfVxuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpbmplY3RIZWxwUGFuZWwoKTogSW5qZWN0SGVscFBhbmVsUmVzdWx0IHtcbiAgY29uc3Qgc2VydmljZSA9IGluamVjdChQaWxsYXJTZXJ2aWNlKTtcblxuICBjb25zdCBvcGVuU2VhcmNoID0gKHF1ZXJ5Pzogc3RyaW5nKTogdm9pZCA9PiB7XG4gICAgaWYgKHF1ZXJ5KSB7XG4gICAgICBzZXJ2aWNlLnNlYXJjaChxdWVyeSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHNlcnZpY2Uub3Blbih7IHZpZXc6ICdzZWFyY2gnIH0pO1xuICAgIH1cbiAgfTtcblxuICBjb25zdCBvcGVuQ2hhdCA9ICgpOiB2b2lkID0+IHtcbiAgICBzZXJ2aWNlLm5hdmlnYXRlKCdjaGF0Jyk7XG4gIH07XG5cbiAgcmV0dXJuIHtcbiAgICBpc09wZW46IGNvbXB1dGVkKCgpID0+IHNlcnZpY2UuaXNQYW5lbE9wZW4oKSksXG4gICAgb3Blbjogc2VydmljZS5vcGVuLmJpbmQoc2VydmljZSksXG4gICAgY2xvc2U6IHNlcnZpY2UuY2xvc2UuYmluZChzZXJ2aWNlKSxcbiAgICB0b2dnbGU6IHNlcnZpY2UudG9nZ2xlLmJpbmQoc2VydmljZSksXG4gICAgb3BlbkFydGljbGU6IHNlcnZpY2Uub3BlbkFydGljbGUuYmluZChzZXJ2aWNlKSxcbiAgICBvcGVuQ2F0ZWdvcnk6IHNlcnZpY2Uub3BlbkNhdGVnb3J5LmJpbmQoc2VydmljZSksXG4gICAgb3BlblNlYXJjaCxcbiAgICBvcGVuQ2hhdCxcbiAgfTtcbn1cbiJdfQ==
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* injectPillarTool Function
|
|
3
|
+
* Angular-idiomatic injection helper for registering Pillar tools
|
|
4
|
+
*
|
|
5
|
+
* Register one or more tools with co-located metadata and handlers.
|
|
6
|
+
* Tools are registered when called and automatically unregistered
|
|
7
|
+
* when the component is destroyed.
|
|
8
|
+
*
|
|
9
|
+
* @example Single tool
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { Component } from '@angular/core';
|
|
12
|
+
* import { injectPillarTool } from '@pillar-ai/angular';
|
|
13
|
+
*
|
|
14
|
+
* @Component({
|
|
15
|
+
* selector: 'app-cart-button',
|
|
16
|
+
* standalone: true,
|
|
17
|
+
* template: `<button>Cart</button>`,
|
|
18
|
+
* })
|
|
19
|
+
* export class CartButtonComponent {
|
|
20
|
+
* constructor() {
|
|
21
|
+
* injectPillarTool({
|
|
22
|
+
* name: 'add_to_cart',
|
|
23
|
+
* description: 'Add a product to the shopping cart',
|
|
24
|
+
* inputSchema: {
|
|
25
|
+
* type: 'object',
|
|
26
|
+
* properties: {
|
|
27
|
+
* productId: { type: 'string', description: 'Product ID' },
|
|
28
|
+
* quantity: { type: 'number', description: 'Quantity to add' },
|
|
29
|
+
* },
|
|
30
|
+
* required: ['productId', 'quantity'],
|
|
31
|
+
* },
|
|
32
|
+
* execute: async ({ productId, quantity }) => {
|
|
33
|
+
* await cartApi.add(productId, quantity);
|
|
34
|
+
* return { content: [{ type: 'text', text: 'Added to cart' }] };
|
|
35
|
+
* },
|
|
36
|
+
* });
|
|
37
|
+
* }
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @example Multiple tools
|
|
42
|
+
* ```typescript
|
|
43
|
+
* import { Component } from '@angular/core';
|
|
44
|
+
* import { injectPillarTool } from '@pillar-ai/angular';
|
|
45
|
+
*
|
|
46
|
+
* @Component({
|
|
47
|
+
* selector: 'app-billing-page',
|
|
48
|
+
* standalone: true,
|
|
49
|
+
* template: `<div>Billing Content</div>`,
|
|
50
|
+
* })
|
|
51
|
+
* export class BillingPageComponent {
|
|
52
|
+
* constructor() {
|
|
53
|
+
* injectPillarTool([
|
|
54
|
+
* {
|
|
55
|
+
* name: 'get_current_plan',
|
|
56
|
+
* description: 'Get the current billing plan',
|
|
57
|
+
* execute: async () => ({ plan: 'pro', price: 29 }),
|
|
58
|
+
* },
|
|
59
|
+
* {
|
|
60
|
+
* name: 'upgrade_plan',
|
|
61
|
+
* description: 'Upgrade to a higher plan',
|
|
62
|
+
* inputSchema: {
|
|
63
|
+
* type: 'object',
|
|
64
|
+
* properties: { planId: { type: 'string' } },
|
|
65
|
+
* required: ['planId'],
|
|
66
|
+
* },
|
|
67
|
+
* execute: async ({ planId }) => {
|
|
68
|
+
* await billingApi.upgrade(planId);
|
|
69
|
+
* return { content: [{ type: 'text', text: 'Upgraded!' }] };
|
|
70
|
+
* },
|
|
71
|
+
* },
|
|
72
|
+
* ]);
|
|
73
|
+
* }
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
import { inject, DestroyRef } from '@angular/core';
|
|
78
|
+
import { PillarService } from './pillar.service';
|
|
79
|
+
/**
|
|
80
|
+
* Register one or more Pillar tools with co-located metadata and handlers.
|
|
81
|
+
*
|
|
82
|
+
* The tools are registered when this function is called and automatically
|
|
83
|
+
* unregistered when the component is destroyed. Must be called within
|
|
84
|
+
* an injection context (constructor, field initializer, or inject()).
|
|
85
|
+
*
|
|
86
|
+
* @param schemaOrSchemas - Single tool schema or array of tool schemas
|
|
87
|
+
*/
|
|
88
|
+
export function injectPillarTool(
|
|
89
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
90
|
+
schemaOrSchemas) {
|
|
91
|
+
const service = inject(PillarService);
|
|
92
|
+
const destroyRef = inject(DestroyRef);
|
|
93
|
+
// Normalize to array for consistent handling
|
|
94
|
+
const schemas = Array.isArray(schemaOrSchemas)
|
|
95
|
+
? schemaOrSchemas
|
|
96
|
+
: [schemaOrSchemas];
|
|
97
|
+
// Track unsubscribe functions
|
|
98
|
+
const unsubscribes = [];
|
|
99
|
+
// Register all tools
|
|
100
|
+
schemas.forEach((schema) => {
|
|
101
|
+
const unsub = service.defineTool(schema);
|
|
102
|
+
unsubscribes.push(unsub);
|
|
103
|
+
});
|
|
104
|
+
// Cleanup on destroy
|
|
105
|
+
destroyRef.onDestroy(() => {
|
|
106
|
+
unsubscribes.forEach((unsub) => unsub());
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/** @deprecated Use injectPillarTool instead */
|
|
110
|
+
export const injectPillarAction = injectPillarTool;
|
|
111
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5qZWN0LXBpbGxhci10b29sLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2xpYi9pbmplY3QtcGlsbGFyLXRvb2wudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTJFRztBQUVILE9BQU8sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRW5ELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUVqRDs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sVUFBVSxnQkFBZ0I7QUFDOUIsOERBQThEO0FBQzlELGVBQW9EO0lBRXBELE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxhQUFhLENBQUMsQ0FBQztJQUN0QyxNQUFNLFVBQVUsR0FBRyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUM7SUFFdEMsNkNBQTZDO0lBQzdDLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsZUFBZSxDQUFDO1FBQzVDLENBQUMsQ0FBQyxlQUFlO1FBQ2pCLENBQUMsQ0FBQyxDQUFDLGVBQWUsQ0FBQyxDQUFDO0lBRXRCLDhCQUE4QjtJQUM5QixNQUFNLFlBQVksR0FBc0IsRUFBRSxDQUFDO0lBRTNDLHFCQUFxQjtJQUNyQixPQUFPLENBQUMsT0FBTyxDQUFDLENBQUMsTUFBTSxFQUFFLEVBQUU7UUFDekIsTUFBTSxLQUFLLEdBQUcsT0FBTyxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUN6QyxZQUFZLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzNCLENBQUMsQ0FBQyxDQUFDO0lBRUgscUJBQXFCO0lBQ3JCLFVBQVUsQ0FBQyxTQUFTLENBQUMsR0FBRyxFQUFFO1FBQ3hCLFlBQVksQ0FBQyxPQUFPLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUM7SUFDM0MsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQsK0NBQStDO0FBQy9DLE1BQU0sQ0FBQyxNQUFNLGtCQUFrQixHQUFHLGdCQUFnQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBpbmplY3RQaWxsYXJUb29sIEZ1bmN0aW9uXG4gKiBBbmd1bGFyLWlkaW9tYXRpYyBpbmplY3Rpb24gaGVscGVyIGZvciByZWdpc3RlcmluZyBQaWxsYXIgdG9vbHNcbiAqXG4gKiBSZWdpc3RlciBvbmUgb3IgbW9yZSB0b29scyB3aXRoIGNvLWxvY2F0ZWQgbWV0YWRhdGEgYW5kIGhhbmRsZXJzLlxuICogVG9vbHMgYXJlIHJlZ2lzdGVyZWQgd2hlbiBjYWxsZWQgYW5kIGF1dG9tYXRpY2FsbHkgdW5yZWdpc3RlcmVkXG4gKiB3aGVuIHRoZSBjb21wb25lbnQgaXMgZGVzdHJveWVkLlxuICpcbiAqIEBleGFtcGxlIFNpbmdsZSB0b29sXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBpbXBvcnQgeyBDb21wb25lbnQgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbiAqIGltcG9ydCB7IGluamVjdFBpbGxhclRvb2wgfSBmcm9tICdAcGlsbGFyLWFpL2FuZ3VsYXInO1xuICpcbiAqIEBDb21wb25lbnQoe1xuICogICBzZWxlY3RvcjogJ2FwcC1jYXJ0LWJ1dHRvbicsXG4gKiAgIHN0YW5kYWxvbmU6IHRydWUsXG4gKiAgIHRlbXBsYXRlOiBgPGJ1dHRvbj5DYXJ0PC9idXR0b24+YCxcbiAqIH0pXG4gKiBleHBvcnQgY2xhc3MgQ2FydEJ1dHRvbkNvbXBvbmVudCB7XG4gKiAgIGNvbnN0cnVjdG9yKCkge1xuICogICAgIGluamVjdFBpbGxhclRvb2woe1xuICogICAgICAgbmFtZTogJ2FkZF90b19jYXJ0JyxcbiAqICAgICAgIGRlc2NyaXB0aW9uOiAnQWRkIGEgcHJvZHVjdCB0byB0aGUgc2hvcHBpbmcgY2FydCcsXG4gKiAgICAgICBpbnB1dFNjaGVtYToge1xuICogICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAqICAgICAgICAgcHJvcGVydGllczoge1xuICogICAgICAgICAgIHByb2R1Y3RJZDogeyB0eXBlOiAnc3RyaW5nJywgZGVzY3JpcHRpb246ICdQcm9kdWN0IElEJyB9LFxuICogICAgICAgICAgIHF1YW50aXR5OiB7IHR5cGU6ICdudW1iZXInLCBkZXNjcmlwdGlvbjogJ1F1YW50aXR5IHRvIGFkZCcgfSxcbiAqICAgICAgICAgfSxcbiAqICAgICAgICAgcmVxdWlyZWQ6IFsncHJvZHVjdElkJywgJ3F1YW50aXR5J10sXG4gKiAgICAgICB9LFxuICogICAgICAgZXhlY3V0ZTogYXN5bmMgKHsgcHJvZHVjdElkLCBxdWFudGl0eSB9KSA9PiB7XG4gKiAgICAgICAgIGF3YWl0IGNhcnRBcGkuYWRkKHByb2R1Y3RJZCwgcXVhbnRpdHkpO1xuICogICAgICAgICByZXR1cm4geyBjb250ZW50OiBbeyB0eXBlOiAndGV4dCcsIHRleHQ6ICdBZGRlZCB0byBjYXJ0JyB9XSB9O1xuICogICAgICAgfSxcbiAqICAgICB9KTtcbiAqICAgfVxuICogfVxuICogYGBgXG4gKlxuICogQGV4YW1wbGUgTXVsdGlwbGUgdG9vbHNcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGltcG9ydCB7IENvbXBvbmVudCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuICogaW1wb3J0IHsgaW5qZWN0UGlsbGFyVG9vbCB9IGZyb20gJ0BwaWxsYXItYWkvYW5ndWxhcic7XG4gKlxuICogQENvbXBvbmVudCh7XG4gKiAgIHNlbGVjdG9yOiAnYXBwLWJpbGxpbmctcGFnZScsXG4gKiAgIHN0YW5kYWxvbmU6IHRydWUsXG4gKiAgIHRlbXBsYXRlOiBgPGRpdj5CaWxsaW5nIENvbnRlbnQ8L2Rpdj5gLFxuICogfSlcbiAqIGV4cG9ydCBjbGFzcyBCaWxsaW5nUGFnZUNvbXBvbmVudCB7XG4gKiAgIGNvbnN0cnVjdG9yKCkge1xuICogICAgIGluamVjdFBpbGxhclRvb2woW1xuICogICAgICAge1xuICogICAgICAgICBuYW1lOiAnZ2V0X2N1cnJlbnRfcGxhbicsXG4gKiAgICAgICAgIGRlc2NyaXB0aW9uOiAnR2V0IHRoZSBjdXJyZW50IGJpbGxpbmcgcGxhbicsXG4gKiAgICAgICAgIGV4ZWN1dGU6IGFzeW5jICgpID0+ICh7IHBsYW46ICdwcm8nLCBwcmljZTogMjkgfSksXG4gKiAgICAgICB9LFxuICogICAgICAge1xuICogICAgICAgICBuYW1lOiAndXBncmFkZV9wbGFuJyxcbiAqICAgICAgICAgZGVzY3JpcHRpb246ICdVcGdyYWRlIHRvIGEgaGlnaGVyIHBsYW4nLFxuICogICAgICAgICBpbnB1dFNjaGVtYToge1xuICogICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICogICAgICAgICAgIHByb3BlcnRpZXM6IHsgcGxhbklkOiB7IHR5cGU6ICdzdHJpbmcnIH0gfSxcbiAqICAgICAgICAgICByZXF1aXJlZDogWydwbGFuSWQnXSxcbiAqICAgICAgICAgfSxcbiAqICAgICAgICAgZXhlY3V0ZTogYXN5bmMgKHsgcGxhbklkIH0pID0+IHtcbiAqICAgICAgICAgICBhd2FpdCBiaWxsaW5nQXBpLnVwZ3JhZGUocGxhbklkKTtcbiAqICAgICAgICAgICByZXR1cm4geyBjb250ZW50OiBbeyB0eXBlOiAndGV4dCcsIHRleHQ6ICdVcGdyYWRlZCEnIH1dIH07XG4gKiAgICAgICAgIH0sXG4gKiAgICAgICB9LFxuICogICAgIF0pO1xuICogICB9XG4gKiB9XG4gKiBgYGBcbiAqL1xuXG5pbXBvcnQgeyBpbmplY3QsIERlc3Ryb3lSZWYgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB0eXBlIHsgVG9vbFNjaGVtYSB9IGZyb20gJ0BwaWxsYXItYWkvc2RrJztcbmltcG9ydCB7IFBpbGxhclNlcnZpY2UgfSBmcm9tICcuL3BpbGxhci5zZXJ2aWNlJztcblxuLyoqXG4gKiBSZWdpc3RlciBvbmUgb3IgbW9yZSBQaWxsYXIgdG9vbHMgd2l0aCBjby1sb2NhdGVkIG1ldGFkYXRhIGFuZCBoYW5kbGVycy5cbiAqXG4gKiBUaGUgdG9vbHMgYXJlIHJlZ2lzdGVyZWQgd2hlbiB0aGlzIGZ1bmN0aW9uIGlzIGNhbGxlZCBhbmQgYXV0b21hdGljYWxseVxuICogdW5yZWdpc3RlcmVkIHdoZW4gdGhlIGNvbXBvbmVudCBpcyBkZXN0cm95ZWQuIE11c3QgYmUgY2FsbGVkIHdpdGhpblxuICogYW4gaW5qZWN0aW9uIGNvbnRleHQgKGNvbnN0cnVjdG9yLCBmaWVsZCBpbml0aWFsaXplciwgb3IgaW5qZWN0KCkpLlxuICpcbiAqIEBwYXJhbSBzY2hlbWFPclNjaGVtYXMgLSBTaW5nbGUgdG9vbCBzY2hlbWEgb3IgYXJyYXkgb2YgdG9vbCBzY2hlbWFzXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpbmplY3RQaWxsYXJUb29sKFxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueVxuICBzY2hlbWFPclNjaGVtYXM6IFRvb2xTY2hlbWE8YW55PiB8IFRvb2xTY2hlbWE8YW55PltdXG4pOiB2b2lkIHtcbiAgY29uc3Qgc2VydmljZSA9IGluamVjdChQaWxsYXJTZXJ2aWNlKTtcbiAgY29uc3QgZGVzdHJveVJlZiA9IGluamVjdChEZXN0cm95UmVmKTtcblxuICAvLyBOb3JtYWxpemUgdG8gYXJyYXkgZm9yIGNvbnNpc3RlbnQgaGFuZGxpbmdcbiAgY29uc3Qgc2NoZW1hcyA9IEFycmF5LmlzQXJyYXkoc2NoZW1hT3JTY2hlbWFzKVxuICAgID8gc2NoZW1hT3JTY2hlbWFzXG4gICAgOiBbc2NoZW1hT3JTY2hlbWFzXTtcblxuICAvLyBUcmFjayB1bnN1YnNjcmliZSBmdW5jdGlvbnNcbiAgY29uc3QgdW5zdWJzY3JpYmVzOiBBcnJheTwoKSA9PiB2b2lkPiA9IFtdO1xuXG4gIC8vIFJlZ2lzdGVyIGFsbCB0b29sc1xuICBzY2hlbWFzLmZvckVhY2goKHNjaGVtYSkgPT4ge1xuICAgIGNvbnN0IHVuc3ViID0gc2VydmljZS5kZWZpbmVUb29sKHNjaGVtYSk7XG4gICAgdW5zdWJzY3JpYmVzLnB1c2godW5zdWIpO1xuICB9KTtcblxuICAvLyBDbGVhbnVwIG9uIGRlc3Ryb3lcbiAgZGVzdHJveVJlZi5vbkRlc3Ryb3koKCkgPT4ge1xuICAgIHVuc3Vic2NyaWJlcy5mb3JFYWNoKCh1bnN1YikgPT4gdW5zdWIoKSk7XG4gIH0pO1xufVxuXG4vKiogQGRlcHJlY2F0ZWQgVXNlIGluamVjdFBpbGxhclRvb2wgaW5zdGVhZCAqL1xuZXhwb3J0IGNvbnN0IGluamVjdFBpbGxhckFjdGlvbiA9IGluamVjdFBpbGxhclRvb2w7XG4iXX0=
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* injectPillar Function
|
|
3
|
+
* Angular-idiomatic injection helper for accessing Pillar SDK
|
|
4
|
+
*/
|
|
5
|
+
import { inject } from '@angular/core';
|
|
6
|
+
import { PillarService } from './pillar.service';
|
|
7
|
+
/**
|
|
8
|
+
* Angular injection function to access the Pillar SDK.
|
|
9
|
+
* Use this in components, directives, or services to interact with Pillar.
|
|
10
|
+
*
|
|
11
|
+
* Must be called within an injection context (constructor, field initializer, or inject()).
|
|
12
|
+
*
|
|
13
|
+
* @example Basic usage (untyped)
|
|
14
|
+
* ```typescript
|
|
15
|
+
* @Component({
|
|
16
|
+
* selector: 'app-help-button',
|
|
17
|
+
* standalone: true,
|
|
18
|
+
* template: `
|
|
19
|
+
* <button (click)="toggle()">
|
|
20
|
+
* {{ isPanelOpen() ? 'Close Help' : 'Get Help' }}
|
|
21
|
+
* </button>
|
|
22
|
+
* `,
|
|
23
|
+
* })
|
|
24
|
+
* export class HelpButtonComponent {
|
|
25
|
+
* private pillar = injectPillar();
|
|
26
|
+
* isPanelOpen = this.pillar.isPanelOpen;
|
|
27
|
+
* toggle = this.pillar.toggle;
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @example Type-safe onTask with action definitions
|
|
32
|
+
* ```typescript
|
|
33
|
+
* import { actions } from '@/lib/pillar/actions';
|
|
34
|
+
*
|
|
35
|
+
* @Component({...})
|
|
36
|
+
* export class MyComponent implements OnInit, OnDestroy {
|
|
37
|
+
* private pillar = injectPillar<typeof actions>();
|
|
38
|
+
* private unsubscribe?: () => void;
|
|
39
|
+
*
|
|
40
|
+
* ngOnInit() {
|
|
41
|
+
* // TypeScript knows data has the correct shape
|
|
42
|
+
* this.unsubscribe = this.pillar.onTask('add_new_source', (data) => {
|
|
43
|
+
* console.log(data.url); // ✓ Typed!
|
|
44
|
+
* });
|
|
45
|
+
* }
|
|
46
|
+
*
|
|
47
|
+
* ngOnDestroy() {
|
|
48
|
+
* this.unsubscribe?.();
|
|
49
|
+
* }
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function injectPillar() {
|
|
54
|
+
const service = inject(PillarService);
|
|
55
|
+
// Create a type-safe wrapper around pillar.onTask
|
|
56
|
+
const onTask = (taskName, handler) => {
|
|
57
|
+
// Cast handler to match the SDK's expected type
|
|
58
|
+
// The runtime behavior is the same, this is just for type narrowing
|
|
59
|
+
return service.onTask(taskName, handler);
|
|
60
|
+
};
|
|
61
|
+
return {
|
|
62
|
+
pillar: () => service.getInstance(),
|
|
63
|
+
state: service.state,
|
|
64
|
+
isReady: service.isReady,
|
|
65
|
+
isPanelOpen: service.isPanelOpen,
|
|
66
|
+
open: service.open.bind(service),
|
|
67
|
+
close: service.close.bind(service),
|
|
68
|
+
toggle: service.toggle.bind(service),
|
|
69
|
+
openArticle: service.openArticle.bind(service),
|
|
70
|
+
openCategory: service.openCategory.bind(service),
|
|
71
|
+
search: service.search.bind(service),
|
|
72
|
+
navigate: service.navigate.bind(service),
|
|
73
|
+
setTheme: service.setTheme.bind(service),
|
|
74
|
+
setTextSelectionEnabled: service.setTextSelectionEnabled.bind(service),
|
|
75
|
+
on: service.on.bind(service),
|
|
76
|
+
onTask,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5qZWN0LXBpbGxhci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvaW5qZWN0LXBpbGxhci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFFSCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBT3ZDLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQWlFakQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTZDRztBQUNILE1BQU0sVUFBVSxZQUFZO0lBRzFCLE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxhQUFhLENBQUMsQ0FBQztJQUV0QyxrREFBa0Q7SUFDbEQsTUFBTSxNQUFNLEdBQUcsQ0FDYixRQUFlLEVBQ2YsT0FBd0QsRUFDMUMsRUFBRTtRQUNoQixnREFBZ0Q7UUFDaEQsb0VBQW9FO1FBQ3BFLE9BQU8sT0FBTyxDQUFDLE1BQU0sQ0FDbkIsUUFBa0IsRUFDbEIsT0FBa0QsQ0FDbkQsQ0FBQztJQUNKLENBQUMsQ0FBQztJQUVGLE9BQU87UUFDTCxNQUFNLEVBQUUsR0FBRyxFQUFFLENBQUMsT0FBTyxDQUFDLFdBQVcsRUFBRTtRQUNuQyxLQUFLLEVBQUUsT0FBTyxDQUFDLEtBQUs7UUFDcEIsT0FBTyxFQUFFLE9BQU8sQ0FBQyxPQUFPO1FBQ3hCLFdBQVcsRUFBRSxPQUFPLENBQUMsV0FBVztRQUNoQyxJQUFJLEVBQUUsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDO1FBQ2hDLEtBQUssRUFBRSxPQUFPLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUM7UUFDbEMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQztRQUNwQyxXQUFXLEVBQUUsT0FBTyxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDO1FBQzlDLFlBQVksRUFBRSxPQUFPLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUM7UUFDaEQsTUFBTSxFQUFFLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQztRQUNwQyxRQUFRLEVBQUUsT0FBTyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDO1FBQ3hDLFFBQVEsRUFBRSxPQUFPLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUM7UUFDeEMsdUJBQXVCLEVBQUUsT0FBTyxDQUFDLHVCQUF1QixDQUFDLElBQUksQ0FBQyxPQUFPLENBQUM7UUFDdEUsRUFBRSxFQUFFLE9BQU8sQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQztRQUM1QixNQUFNO0tBQ1AsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIGluamVjdFBpbGxhciBGdW5jdGlvblxuICogQW5ndWxhci1pZGlvbWF0aWMgaW5qZWN0aW9uIGhlbHBlciBmb3IgYWNjZXNzaW5nIFBpbGxhciBTREtcbiAqL1xuXG5pbXBvcnQgeyBpbmplY3QgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB0eXBlIHtcbiAgU3luY0FjdGlvbkRlZmluaXRpb25zLFxuICBBY3Rpb25EZWZpbml0aW9ucyxcbiAgQWN0aW9uRGF0YVR5cGUsXG4gIEFjdGlvbk5hbWVzLFxufSBmcm9tICdAcGlsbGFyLWFpL3Nkayc7XG5pbXBvcnQgeyBQaWxsYXJTZXJ2aWNlIH0gZnJvbSAnLi9waWxsYXIuc2VydmljZSc7XG5cbi8qKlxuICogUmVzdWx0IHR5cGUgZm9yIGluamVjdFBpbGxhciB3aXRoIHR5cGUtc2FmZSBvblRhc2suXG4gKlxuICogQHRlbXBsYXRlIFRBY3Rpb25zIC0gVGhlIGFjdGlvbiBkZWZpbml0aW9ucyBmb3IgdHlwZSBpbmZlcmVuY2VcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBJbmplY3RQaWxsYXJSZXN1bHQ8XG4gIFRBY3Rpb25zIGV4dGVuZHMgU3luY0FjdGlvbkRlZmluaXRpb25zIHwgQWN0aW9uRGVmaW5pdGlvbnMgPSBTeW5jQWN0aW9uRGVmaW5pdGlvbnMsXG4+IHtcbiAgLyoqIEdldCB0aGUgUGlsbGFyIFNESyBpbnN0YW5jZSAqL1xuICBwaWxsYXI6ICgpID0+IFJldHVyblR5cGU8UGlsbGFyU2VydmljZVsnZ2V0SW5zdGFuY2UnXT47XG5cbiAgLyoqIEN1cnJlbnQgU0RLIHN0YXRlICovXG4gIHN0YXRlOiBQaWxsYXJTZXJ2aWNlWydzdGF0ZSddO1xuXG4gIC8qKiBXaGV0aGVyIHRoZSBTREsgaXMgcmVhZHkgKi9cbiAgaXNSZWFkeTogUGlsbGFyU2VydmljZVsnaXNSZWFkeSddO1xuXG4gIC8qKiBXaGV0aGVyIHRoZSBwYW5lbCBpcyBjdXJyZW50bHkgb3BlbiAqL1xuICBpc1BhbmVsT3BlbjogUGlsbGFyU2VydmljZVsnaXNQYW5lbE9wZW4nXTtcblxuICAvKiogT3BlbiB0aGUgaGVscCBwYW5lbCAqL1xuICBvcGVuOiBQaWxsYXJTZXJ2aWNlWydvcGVuJ107XG5cbiAgLyoqIENsb3NlIHRoZSBoZWxwIHBhbmVsICovXG4gIGNsb3NlOiBQaWxsYXJTZXJ2aWNlWydjbG9zZSddO1xuXG4gIC8qKiBUb2dnbGUgdGhlIGhlbHAgcGFuZWwgKi9cbiAgdG9nZ2xlOiBQaWxsYXJTZXJ2aWNlWyd0b2dnbGUnXTtcblxuICAvKiogT3BlbiBhIHNwZWNpZmljIGFydGljbGUgKi9cbiAgb3BlbkFydGljbGU6IFBpbGxhclNlcnZpY2VbJ29wZW5BcnRpY2xlJ107XG5cbiAgLyoqIE9wZW4gYSBzcGVjaWZpYyBjYXRlZ29yeSAqL1xuICBvcGVuQ2F0ZWdvcnk6IFBpbGxhclNlcnZpY2VbJ29wZW5DYXRlZ29yeSddO1xuXG4gIC8qKiBQZXJmb3JtIGEgc2VhcmNoICovXG4gIHNlYXJjaDogUGlsbGFyU2VydmljZVsnc2VhcmNoJ107XG5cbiAgLyoqIE5hdmlnYXRlIHRvIGEgc3BlY2lmaWMgdmlldyAqL1xuICBuYXZpZ2F0ZTogUGlsbGFyU2VydmljZVsnbmF2aWdhdGUnXTtcblxuICAvKiogVXBkYXRlIHRoZSBwYW5lbCB0aGVtZSBhdCBydW50aW1lICovXG4gIHNldFRoZW1lOiBQaWxsYXJTZXJ2aWNlWydzZXRUaGVtZSddO1xuXG4gIC8qKiBFbmFibGUgb3IgZGlzYWJsZSB0aGUgdGV4dCBzZWxlY3Rpb24gXCJBc2sgQUlcIiBwb3BvdmVyICovXG4gIHNldFRleHRTZWxlY3Rpb25FbmFibGVkOiBQaWxsYXJTZXJ2aWNlWydzZXRUZXh0U2VsZWN0aW9uRW5hYmxlZCddO1xuXG4gIC8qKiBTdWJzY3JpYmUgdG8gU0RLIGV2ZW50cyAqL1xuICBvbjogUGlsbGFyU2VydmljZVsnb24nXTtcblxuICAvKipcbiAgICogVHlwZS1zYWZlIHRhc2sgaGFuZGxlciByZWdpc3RyYXRpb24uXG4gICAqXG4gICAqIEBwYXJhbSB0YXNrTmFtZSAtIFRoZSBhY3Rpb24gbmFtZSAoYXV0b2NvbXBsZXRlZCBmcm9tIHlvdXIgYWN0aW9ucylcbiAgICogQHBhcmFtIGhhbmRsZXIgLSBIYW5kbGVyIGZ1bmN0aW9uIHdpdGggdHlwZWQgZGF0YSBwYXJhbWV0ZXJcbiAgICogQHJldHVybnMgVW5zdWJzY3JpYmUgZnVuY3Rpb25cbiAgICovXG4gIG9uVGFzazogPFROYW1lIGV4dGVuZHMgQWN0aW9uTmFtZXM8VEFjdGlvbnM+PihcbiAgICB0YXNrTmFtZTogVE5hbWUsXG4gICAgaGFuZGxlcjogKGRhdGE6IEFjdGlvbkRhdGFUeXBlPFRBY3Rpb25zLCBUTmFtZT4pID0+IHZvaWRcbiAgKSA9PiAoKSA9PiB2b2lkO1xufVxuXG4vKipcbiAqIEFuZ3VsYXIgaW5qZWN0aW9uIGZ1bmN0aW9uIHRvIGFjY2VzcyB0aGUgUGlsbGFyIFNESy5cbiAqIFVzZSB0aGlzIGluIGNvbXBvbmVudHMsIGRpcmVjdGl2ZXMsIG9yIHNlcnZpY2VzIHRvIGludGVyYWN0IHdpdGggUGlsbGFyLlxuICpcbiAqIE11c3QgYmUgY2FsbGVkIHdpdGhpbiBhbiBpbmplY3Rpb24gY29udGV4dCAoY29uc3RydWN0b3IsIGZpZWxkIGluaXRpYWxpemVyLCBvciBpbmplY3QoKSkuXG4gKlxuICogQGV4YW1wbGUgQmFzaWMgdXNhZ2UgKHVudHlwZWQpXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBAQ29tcG9uZW50KHtcbiAqICAgc2VsZWN0b3I6ICdhcHAtaGVscC1idXR0b24nLFxuICogICBzdGFuZGFsb25lOiB0cnVlLFxuICogICB0ZW1wbGF0ZTogYFxuICogICAgIDxidXR0b24gKGNsaWNrKT1cInRvZ2dsZSgpXCI+XG4gKiAgICAgICB7eyBpc1BhbmVsT3BlbigpID8gJ0Nsb3NlIEhlbHAnIDogJ0dldCBIZWxwJyB9fVxuICogICAgIDwvYnV0dG9uPlxuICogICBgLFxuICogfSlcbiAqIGV4cG9ydCBjbGFzcyBIZWxwQnV0dG9uQ29tcG9uZW50IHtcbiAqICAgcHJpdmF0ZSBwaWxsYXIgPSBpbmplY3RQaWxsYXIoKTtcbiAqICAgaXNQYW5lbE9wZW4gPSB0aGlzLnBpbGxhci5pc1BhbmVsT3BlbjtcbiAqICAgdG9nZ2xlID0gdGhpcy5waWxsYXIudG9nZ2xlO1xuICogfVxuICogYGBgXG4gKlxuICogQGV4YW1wbGUgVHlwZS1zYWZlIG9uVGFzayB3aXRoIGFjdGlvbiBkZWZpbml0aW9uc1xuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0IHsgYWN0aW9ucyB9IGZyb20gJ0AvbGliL3BpbGxhci9hY3Rpb25zJztcbiAqXG4gKiBAQ29tcG9uZW50KHsuLi59KVxuICogZXhwb3J0IGNsYXNzIE15Q29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBPbkRlc3Ryb3kge1xuICogICBwcml2YXRlIHBpbGxhciA9IGluamVjdFBpbGxhcjx0eXBlb2YgYWN0aW9ucz4oKTtcbiAqICAgcHJpdmF0ZSB1bnN1YnNjcmliZT86ICgpID0+IHZvaWQ7XG4gKlxuICogICBuZ09uSW5pdCgpIHtcbiAqICAgICAvLyBUeXBlU2NyaXB0IGtub3dzIGRhdGEgaGFzIHRoZSBjb3JyZWN0IHNoYXBlXG4gKiAgICAgdGhpcy51bnN1YnNjcmliZSA9IHRoaXMucGlsbGFyLm9uVGFzaygnYWRkX25ld19zb3VyY2UnLCAoZGF0YSkgPT4ge1xuICogICAgICAgY29uc29sZS5sb2coZGF0YS51cmwpOyAvLyDinJMgVHlwZWQhXG4gKiAgICAgfSk7XG4gKiAgIH1cbiAqXG4gKiAgIG5nT25EZXN0cm95KCkge1xuICogICAgIHRoaXMudW5zdWJzY3JpYmU/LigpO1xuICogICB9XG4gKiB9XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGluamVjdFBpbGxhcjxcbiAgVEFjdGlvbnMgZXh0ZW5kcyBTeW5jQWN0aW9uRGVmaW5pdGlvbnMgfCBBY3Rpb25EZWZpbml0aW9ucyA9IFN5bmNBY3Rpb25EZWZpbml0aW9ucyxcbj4oKTogSW5qZWN0UGlsbGFyUmVzdWx0PFRBY3Rpb25zPiB7XG4gIGNvbnN0IHNlcnZpY2UgPSBpbmplY3QoUGlsbGFyU2VydmljZSk7XG5cbiAgLy8gQ3JlYXRlIGEgdHlwZS1zYWZlIHdyYXBwZXIgYXJvdW5kIHBpbGxhci5vblRhc2tcbiAgY29uc3Qgb25UYXNrID0gPFROYW1lIGV4dGVuZHMgQWN0aW9uTmFtZXM8VEFjdGlvbnM+PihcbiAgICB0YXNrTmFtZTogVE5hbWUsXG4gICAgaGFuZGxlcjogKGRhdGE6IEFjdGlvbkRhdGFUeXBlPFRBY3Rpb25zLCBUTmFtZT4pID0+IHZvaWRcbiAgKTogKCgpID0+IHZvaWQpID0+IHtcbiAgICAvLyBDYXN0IGhhbmRsZXIgdG8gbWF0Y2ggdGhlIFNESydzIGV4cGVjdGVkIHR5cGVcbiAgICAvLyBUaGUgcnVudGltZSBiZWhhdmlvciBpcyB0aGUgc2FtZSwgdGhpcyBpcyBqdXN0IGZvciB0eXBlIG5hcnJvd2luZ1xuICAgIHJldHVybiBzZXJ2aWNlLm9uVGFzayhcbiAgICAgIHRhc2tOYW1lIGFzIHN0cmluZyxcbiAgICAgIGhhbmRsZXIgYXMgKGRhdGE6IFJlY29yZDxzdHJpbmcsIHVua25vd24+KSA9PiB2b2lkXG4gICAgKTtcbiAgfTtcblxuICByZXR1cm4ge1xuICAgIHBpbGxhcjogKCkgPT4gc2VydmljZS5nZXRJbnN0YW5jZSgpLFxuICAgIHN0YXRlOiBzZXJ2aWNlLnN0YXRlLFxuICAgIGlzUmVhZHk6IHNlcnZpY2UuaXNSZWFkeSxcbiAgICBpc1BhbmVsT3Blbjogc2VydmljZS5pc1BhbmVsT3BlbixcbiAgICBvcGVuOiBzZXJ2aWNlLm9wZW4uYmluZChzZXJ2aWNlKSxcbiAgICBjbG9zZTogc2VydmljZS5jbG9zZS5iaW5kKHNlcnZpY2UpLFxuICAgIHRvZ2dsZTogc2VydmljZS50b2dnbGUuYmluZChzZXJ2aWNlKSxcbiAgICBvcGVuQXJ0aWNsZTogc2VydmljZS5vcGVuQXJ0aWNsZS5iaW5kKHNlcnZpY2UpLFxuICAgIG9wZW5DYXRlZ29yeTogc2VydmljZS5vcGVuQ2F0ZWdvcnkuYmluZChzZXJ2aWNlKSxcbiAgICBzZWFyY2g6IHNlcnZpY2Uuc2VhcmNoLmJpbmQoc2VydmljZSksXG4gICAgbmF2aWdhdGU6IHNlcnZpY2UubmF2aWdhdGUuYmluZChzZXJ2aWNlKSxcbiAgICBzZXRUaGVtZTogc2VydmljZS5zZXRUaGVtZS5iaW5kKHNlcnZpY2UpLFxuICAgIHNldFRleHRTZWxlY3Rpb25FbmFibGVkOiBzZXJ2aWNlLnNldFRleHRTZWxlY3Rpb25FbmFibGVkLmJpbmQoc2VydmljZSksXG4gICAgb246IHNlcnZpY2Uub24uYmluZChzZXJ2aWNlKSxcbiAgICBvblRhc2ssXG4gIH07XG59XG4iXX0=
|