@foisit/angular-wrapper 1.2.0 β 2.1.1
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/README.md +558 -121
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,216 +1,653 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @foisit/angular-wrapper
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@foisit/angular-wrapper)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
> **The AI-Powered Conversational Assistant for Angular Applications**
|
|
6
7
|
|
|
7
|
-
-
|
|
8
|
-
- π¨ **Visual Feedback**: Show visual cues when the assistant is active.
|
|
9
|
-
- π **Effortless Integration**: Set up voice commands in minutes with minimal code.
|
|
10
|
-
- π£οΈ **Voice Feedback**: Interactive voice responses make the assistant engaging.
|
|
11
|
-
- π **Double Activation**: Activate or put the assistant to sleep with a double-tap.
|
|
8
|
+
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.
|
|
12
9
|
|
|
13
|
-
|
|
10
|
+
> [!NOTE]
|
|
11
|
+
> ποΈ **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.
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## π Table of Contents
|
|
16
|
+
|
|
17
|
+
- [Features](#-features)
|
|
18
|
+
- [Installation](#-installation)
|
|
19
|
+
- [Quick Start](#-quick-start)
|
|
20
|
+
- [Core Concepts](#-core-concepts)
|
|
21
|
+
- [API Reference](#-api-reference)
|
|
22
|
+
- [Advanced Usage](#-advanced-usage)
|
|
23
|
+
- [Examples](#-examples)
|
|
24
|
+
- [TypeScript Support](#-typescript-support)
|
|
25
|
+
- [Best Practices](#-best-practices)
|
|
16
26
|
|
|
17
27
|
---
|
|
18
28
|
|
|
19
|
-
##
|
|
29
|
+
## β¨ Features
|
|
30
|
+
|
|
31
|
+
- **π§ Natural Language Understanding** - AI-powered intent matching using GPT-4o mini (proxied securely)
|
|
32
|
+
- **π Smart Slot Filling** - Auto-generates forms for missing parameters
|
|
33
|
+
- **β οΈ Critical Action Protection** - Built-in confirmation dialogs for dangerous operations
|
|
34
|
+
- **π¨ Premium UI** - Glassmorphic overlay with dark/light mode support
|
|
35
|
+
- **π Zero Backend Required** - Secure proxy architecture keeps API keys server-side
|
|
36
|
+
- **β‘ Angular Native** - Uses Dependency Injection, Signals, and RxJS
|
|
37
|
+
- **π― Type-Safe** - Full TypeScript support with comprehensive types
|
|
38
|
+
- **π± Responsive** - Works flawlessly on desktop and mobile
|
|
39
|
+
|
|
40
|
+
---
|
|
20
41
|
|
|
21
|
-
|
|
42
|
+
## π Installation
|
|
22
43
|
|
|
23
44
|
```bash
|
|
24
45
|
npm install @foisit/angular-wrapper
|
|
25
46
|
```
|
|
26
47
|
|
|
27
|
-
|
|
48
|
+
### Peer Dependencies
|
|
28
49
|
|
|
29
|
-
```
|
|
30
|
-
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"@angular/core": "^17.0.0 || ^18.0.0",
|
|
53
|
+
"@angular/common": "^17.0.0 || ^18.0.0"
|
|
54
|
+
}
|
|
31
55
|
```
|
|
32
56
|
|
|
33
57
|
---
|
|
34
58
|
|
|
35
|
-
##
|
|
59
|
+
## π Quick Start
|
|
60
|
+
|
|
61
|
+
### Step 1: Import the Module
|
|
36
62
|
|
|
37
|
-
|
|
63
|
+
#### For Standalone Apps (Recommended)
|
|
38
64
|
|
|
39
|
-
|
|
65
|
+
```typescript
|
|
66
|
+
// app.config.ts
|
|
67
|
+
import { ApplicationConfig, importProvidersFrom } from '@angular/core';
|
|
68
|
+
import { AssistantModule } from '@foisit/angular-wrapper';
|
|
40
69
|
|
|
41
|
-
|
|
70
|
+
export const appConfig: ApplicationConfig = {
|
|
71
|
+
providers: [
|
|
72
|
+
importProvidersFrom(
|
|
73
|
+
AssistantModule.forRoot({
|
|
74
|
+
introMessage: 'Welcome! How can I assist you today?',
|
|
75
|
+
enableSmartIntent: true,
|
|
76
|
+
commands: [
|
|
77
|
+
{
|
|
78
|
+
command: 'navigate to profile',
|
|
79
|
+
action: () => console.log('Navigating to profile...'),
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
})
|
|
83
|
+
),
|
|
84
|
+
],
|
|
85
|
+
};
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### For Module-Based Apps
|
|
42
89
|
|
|
43
90
|
```typescript
|
|
91
|
+
// app.module.ts
|
|
44
92
|
import { NgModule } from '@angular/core';
|
|
45
|
-
import { BrowserModule } from '@angular/platform-browser';
|
|
46
|
-
import { AppComponent } from './app.component';
|
|
47
93
|
import { AssistantModule } from '@foisit/angular-wrapper';
|
|
48
94
|
|
|
49
95
|
@NgModule({
|
|
50
|
-
declarations: [AppComponent],
|
|
51
96
|
imports: [
|
|
52
|
-
BrowserModule,
|
|
53
97
|
AssistantModule.forRoot({
|
|
54
|
-
|
|
55
|
-
fallbackResponse: 'Sorry, I didnβt understand that.',
|
|
98
|
+
introMessage: 'Welcome! How can I assist you today?',
|
|
56
99
|
commands: [
|
|
57
|
-
|
|
58
|
-
{ command: 'log out', action: () => console.log('Logging out...') },
|
|
100
|
+
/* your commands */
|
|
59
101
|
],
|
|
60
102
|
}),
|
|
61
103
|
],
|
|
62
|
-
bootstrap: [AppComponent],
|
|
63
104
|
})
|
|
64
105
|
export class AppModule {}
|
|
65
106
|
```
|
|
66
107
|
|
|
67
|
-
|
|
108
|
+
### Step 2: Use the Service
|
|
68
109
|
|
|
69
110
|
```typescript
|
|
70
|
-
|
|
71
|
-
import {
|
|
72
|
-
import {
|
|
73
|
-
import { AssistantModule } from '@foisit/angular-wrapper';
|
|
111
|
+
// my-component.ts
|
|
112
|
+
import { Component } from '@angular/core';
|
|
113
|
+
import { AssistantService } from '@foisit/angular-wrapper';
|
|
74
114
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
115
|
+
@Component({
|
|
116
|
+
selector: 'app-my-component',
|
|
117
|
+
template: ` <button (click)="openAssistant()">Open Assistant</button> `,
|
|
118
|
+
})
|
|
119
|
+
export class MyComponent {
|
|
120
|
+
constructor(private assistant: AssistantService) {}
|
|
121
|
+
|
|
122
|
+
openAssistant() {
|
|
123
|
+
this.assistant.toggle();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## π― Core Concepts
|
|
131
|
+
|
|
132
|
+
### 1. Commands
|
|
133
|
+
|
|
134
|
+
Commands are the building blocks of your assistant. Each command represents an action users can trigger through natural language.
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
{
|
|
138
|
+
command: 'delete account',
|
|
139
|
+
description: 'Permanently delete user account',
|
|
140
|
+
action: () => this.accountService.delete()
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### 2. Parameters (Slot Filling)
|
|
145
|
+
|
|
146
|
+
Define parameters and Foisit will automatically generate forms to collect them:
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
{
|
|
150
|
+
command: 'create user',
|
|
151
|
+
description: 'Create a new user account',
|
|
152
|
+
parameters: [
|
|
153
|
+
{ name: 'username', type: 'string', required: true },
|
|
154
|
+
{ name: 'email', type: 'string', required: true },
|
|
155
|
+
{ name: 'age', type: 'number', required: false }
|
|
89
156
|
],
|
|
90
|
-
|
|
157
|
+
action: (params) => this.userService.create(params)
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Supported Parameter Types:**
|
|
162
|
+
|
|
163
|
+
- `string` - Text input
|
|
164
|
+
- `number` - Numeric input
|
|
165
|
+
- `date` - Date picker
|
|
166
|
+
- `select` - Dropdown (static or async options)
|
|
167
|
+
|
|
168
|
+
### 3. Critical Actions
|
|
169
|
+
|
|
170
|
+
Protect dangerous operations with automatic confirmation dialogs:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
{
|
|
174
|
+
command: 'delete all data',
|
|
175
|
+
critical: true, // π Requires confirmation
|
|
176
|
+
description: 'Permanently delete all application data',
|
|
177
|
+
action: async () => {
|
|
178
|
+
await this.dataService.deleteAll();
|
|
179
|
+
return 'β
All data deleted successfully.';
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### 4. Select Parameters (Static)
|
|
185
|
+
|
|
186
|
+
Provide predefined options:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
{
|
|
190
|
+
command: 'set theme',
|
|
191
|
+
parameters: [{
|
|
192
|
+
name: 'theme',
|
|
193
|
+
type: 'select',
|
|
194
|
+
options: [
|
|
195
|
+
{ label: 'Light Mode', value: 'light' },
|
|
196
|
+
{ label: 'Dark Mode', value: 'dark' },
|
|
197
|
+
{ label: 'Auto', value: 'auto' }
|
|
198
|
+
]
|
|
199
|
+
}],
|
|
200
|
+
action: (params) => this.themeService.set(params.theme)
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### 5. Dynamic Select Parameters
|
|
205
|
+
|
|
206
|
+
Load options from APIs:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
{
|
|
210
|
+
command: 'assign to user',
|
|
211
|
+
parameters: [{
|
|
212
|
+
name: 'userId',
|
|
213
|
+
type: 'select',
|
|
214
|
+
getOptions: async () => {
|
|
215
|
+
const users = await this.userService.getAll();
|
|
216
|
+
return users.map(u => ({
|
|
217
|
+
label: `${u.name} (${u.email})`,
|
|
218
|
+
value: u.id
|
|
219
|
+
}));
|
|
220
|
+
}
|
|
221
|
+
}],
|
|
222
|
+
action: (params) => this.taskService.assign(params.userId)
|
|
223
|
+
}
|
|
91
224
|
```
|
|
92
225
|
|
|
93
226
|
---
|
|
94
227
|
|
|
95
|
-
|
|
228
|
+
## π API Reference
|
|
96
229
|
|
|
97
|
-
|
|
230
|
+
### `AssistantService`
|
|
98
231
|
|
|
99
|
-
|
|
232
|
+
Injectable service for programmatic control:
|
|
233
|
+
|
|
234
|
+
#### Methods
|
|
235
|
+
|
|
236
|
+
##### `toggle(onSubmit?, onClose?)`
|
|
237
|
+
|
|
238
|
+
Opens or closes the assistant overlay.
|
|
100
239
|
|
|
101
240
|
```typescript
|
|
102
|
-
|
|
241
|
+
// Basic usage
|
|
242
|
+
this.assistant.toggle();
|
|
243
|
+
|
|
244
|
+
// With callbacks
|
|
245
|
+
this.assistant.toggle(
|
|
246
|
+
(userInput) => console.log('User said:', userInput),
|
|
247
|
+
() => console.log('Assistant closed')
|
|
248
|
+
);
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
##### `addCommand(command, action?)`
|
|
252
|
+
|
|
253
|
+
Dynamically add a command at runtime.
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
// Add a simple command
|
|
257
|
+
this.assistant.addCommand('refresh data', () => {
|
|
258
|
+
this.dataService.refresh();
|
|
259
|
+
return 'Data refreshed!';
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Add a command with full config
|
|
263
|
+
this.assistant.addCommand({
|
|
264
|
+
command: 'export report',
|
|
265
|
+
description: 'Export data as PDF',
|
|
266
|
+
parameters: [
|
|
267
|
+
{
|
|
268
|
+
name: 'format',
|
|
269
|
+
type: 'select',
|
|
270
|
+
options: [
|
|
271
|
+
{ label: 'PDF', value: 'pdf' },
|
|
272
|
+
{ label: 'Excel', value: 'xlsx' },
|
|
273
|
+
],
|
|
274
|
+
},
|
|
275
|
+
],
|
|
276
|
+
action: async (params) => {
|
|
277
|
+
await this.reportService.export(params.format);
|
|
278
|
+
return `Report exported as ${params.format}`;
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
##### `removeCommand(commandPhrase)`
|
|
284
|
+
|
|
285
|
+
Remove a registered command.
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
this.assistant.removeCommand('delete account');
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
##### `getCommands()`
|
|
292
|
+
|
|
293
|
+
Get list of all registered command names.
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
const commands = this.assistant.getCommands();
|
|
297
|
+
console.log('Available commands:', commands);
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## π§ Configuration Options
|
|
303
|
+
|
|
304
|
+
### `AssistantConfig`
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
interface AssistantConfig {
|
|
308
|
+
// Activation keyword (optional)
|
|
309
|
+
activationCommand?: string;
|
|
310
|
+
|
|
311
|
+
// Welcome message shown when assistant opens
|
|
312
|
+
introMessage?: string;
|
|
313
|
+
|
|
314
|
+
// Response for unrecognized inputs
|
|
315
|
+
fallbackResponse?: string;
|
|
316
|
+
|
|
317
|
+
// Enable AI-powered natural language understanding
|
|
318
|
+
enableSmartIntent?: boolean;
|
|
319
|
+
|
|
320
|
+
// Input field placeholder text
|
|
321
|
+
inputPlaceholder?: string;
|
|
322
|
+
|
|
323
|
+
// List of commands
|
|
324
|
+
commands: AssistantCommand[];
|
|
325
|
+
|
|
326
|
+
// Floating button configuration
|
|
327
|
+
floatingButton?: {
|
|
328
|
+
visible?: boolean;
|
|
329
|
+
tooltip?: string;
|
|
330
|
+
customHtml?: string;
|
|
331
|
+
position?: { bottom: string; right: string };
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## π¨ Advanced Usage
|
|
339
|
+
|
|
340
|
+
### Example 1: Multi-Step Booking System
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
import { Component } from '@angular/core';
|
|
103
344
|
import { AssistantService } from '@foisit/angular-wrapper';
|
|
104
345
|
|
|
105
346
|
@Component({
|
|
106
|
-
selector: 'app-
|
|
107
|
-
|
|
108
|
-
styleUrls: ['./app.component.scss'],
|
|
347
|
+
selector: 'app-booking',
|
|
348
|
+
template: `<button (click)="setupBooking()">Enable Booking</button>`,
|
|
109
349
|
})
|
|
110
|
-
export class
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
350
|
+
export class BookingComponent {
|
|
351
|
+
constructor(private assistant: AssistantService, private bookingService: BookingService) {}
|
|
352
|
+
|
|
353
|
+
setupBooking() {
|
|
354
|
+
this.assistant.addCommand({
|
|
355
|
+
command: 'book appointment',
|
|
356
|
+
description: 'Schedule a new appointment',
|
|
357
|
+
parameters: [
|
|
358
|
+
{
|
|
359
|
+
name: 'service',
|
|
360
|
+
description: 'Type of service',
|
|
361
|
+
type: 'select',
|
|
362
|
+
required: true,
|
|
363
|
+
getOptions: async () => {
|
|
364
|
+
const services = await this.bookingService.getServices();
|
|
365
|
+
return services.map((s) => ({
|
|
366
|
+
label: s.name,
|
|
367
|
+
value: s.id,
|
|
368
|
+
}));
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
name: 'date',
|
|
373
|
+
description: 'Preferred date',
|
|
374
|
+
type: 'date',
|
|
375
|
+
required: true,
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
name: 'notes',
|
|
379
|
+
description: 'Additional notes',
|
|
380
|
+
type: 'string',
|
|
381
|
+
required: false,
|
|
382
|
+
},
|
|
383
|
+
],
|
|
384
|
+
action: async (params) => {
|
|
385
|
+
const booking = await this.bookingService.create(params);
|
|
386
|
+
return {
|
|
387
|
+
type: 'success',
|
|
388
|
+
message: `β
Appointment booked for ${params.date}!`,
|
|
389
|
+
};
|
|
390
|
+
},
|
|
118
391
|
});
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
```
|
|
119
395
|
|
|
120
|
-
|
|
121
|
-
this.color.set('transparent');
|
|
122
|
-
});
|
|
396
|
+
### Example 2: E-Commerce Product Search
|
|
123
397
|
|
|
124
|
-
|
|
125
|
-
|
|
398
|
+
```typescript
|
|
399
|
+
this.assistant.addCommand({
|
|
400
|
+
command: 'search products',
|
|
401
|
+
parameters: [
|
|
402
|
+
{ name: 'query', type: 'string', required: true },
|
|
403
|
+
{
|
|
404
|
+
name: 'category',
|
|
405
|
+
type: 'select',
|
|
406
|
+
required: false,
|
|
407
|
+
options: [
|
|
408
|
+
{ label: 'Electronics', value: 'electronics' },
|
|
409
|
+
{ label: 'Clothing', value: 'clothing' },
|
|
410
|
+
{ label: 'Books', value: 'books' },
|
|
411
|
+
],
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
name: 'minPrice',
|
|
415
|
+
type: 'number',
|
|
416
|
+
required: false,
|
|
417
|
+
},
|
|
418
|
+
],
|
|
419
|
+
action: async (params) => {
|
|
420
|
+
const results = await this.productService.search(params);
|
|
421
|
+
this.router.navigate(['/products'], {
|
|
422
|
+
queryParams: { q: params.query },
|
|
126
423
|
});
|
|
424
|
+
return `Found ${results.length} products matching "${params.query}"`;
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
```
|
|
127
428
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
429
|
+
### Example 3: Form Validation with Error Handling
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
this.assistant.addCommand({
|
|
433
|
+
command: 'create account',
|
|
434
|
+
parameters: [
|
|
435
|
+
{ name: 'email', type: 'string', required: true },
|
|
436
|
+
{ name: 'password', type: 'string', required: true },
|
|
437
|
+
{ name: 'age', type: 'number', required: true },
|
|
438
|
+
],
|
|
439
|
+
action: async (params) => {
|
|
440
|
+
// Validation
|
|
441
|
+
if (params.age < 18) {
|
|
442
|
+
return {
|
|
443
|
+
type: 'error',
|
|
444
|
+
message: 'β You must be 18 or older to create an account.',
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (!params.email.includes('@')) {
|
|
449
|
+
return {
|
|
450
|
+
type: 'error',
|
|
451
|
+
message: 'β Please provide a valid email address.',
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Create account
|
|
456
|
+
try {
|
|
457
|
+
await this.authService.register(params);
|
|
458
|
+
return {
|
|
459
|
+
type: 'success',
|
|
460
|
+
message: 'β
Account created successfully! You can now log in.',
|
|
461
|
+
};
|
|
462
|
+
} catch (error) {
|
|
463
|
+
return {
|
|
464
|
+
type: 'error',
|
|
465
|
+
message: `β Registration failed: ${error.message}`,
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
});
|
|
132
470
|
```
|
|
133
471
|
|
|
134
472
|
---
|
|
135
473
|
|
|
136
|
-
|
|
474
|
+
## π TypeScript Support
|
|
137
475
|
|
|
138
|
-
|
|
476
|
+
### Full Type Definitions
|
|
139
477
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
478
|
+
```typescript
|
|
479
|
+
import { AssistantCommand, InteractiveResponse } from '@foisit/core';
|
|
480
|
+
|
|
481
|
+
// Type-safe command definition
|
|
482
|
+
const myCommand: AssistantCommand = {
|
|
483
|
+
command: 'update settings',
|
|
484
|
+
description: 'Update user preferences',
|
|
485
|
+
parameters: [
|
|
486
|
+
{
|
|
487
|
+
name: 'theme',
|
|
488
|
+
type: 'select',
|
|
489
|
+
required: true,
|
|
490
|
+
options: [
|
|
491
|
+
{ label: 'Light', value: 'light' },
|
|
492
|
+
{ label: 'Dark', value: 'dark' },
|
|
493
|
+
],
|
|
494
|
+
},
|
|
495
|
+
],
|
|
496
|
+
action: async (params: { theme: string }): Promise<InteractiveResponse> => {
|
|
497
|
+
await this.settingsService.update(params.theme);
|
|
498
|
+
return {
|
|
499
|
+
type: 'success',
|
|
500
|
+
message: `Theme updated to ${params.theme}`,
|
|
501
|
+
};
|
|
502
|
+
},
|
|
503
|
+
};
|
|
159
504
|
```
|
|
160
505
|
|
|
161
506
|
---
|
|
162
507
|
|
|
163
|
-
|
|
508
|
+
## π― Best Practices
|
|
164
509
|
|
|
165
|
-
|
|
510
|
+
### 1. Command Naming
|
|
166
511
|
|
|
167
|
-
|
|
168
|
-
|
|
512
|
+
β
**Good:**
|
|
513
|
+
|
|
514
|
+
- "create user"
|
|
515
|
+
- "delete account"
|
|
516
|
+
- "export report"
|
|
517
|
+
|
|
518
|
+
β **Avoid:**
|
|
519
|
+
|
|
520
|
+
- "CreateUser" (not natural)
|
|
521
|
+
- "usr_del" (not descriptive)
|
|
522
|
+
- "do the thing" (too vague)
|
|
523
|
+
|
|
524
|
+
### 2. Descriptions
|
|
525
|
+
|
|
526
|
+
Always provide clear descriptions for AI matching:
|
|
527
|
+
|
|
528
|
+
```typescript
|
|
529
|
+
{
|
|
530
|
+
command: 'reset password',
|
|
531
|
+
description: 'Reset the user password and send recovery email',
|
|
532
|
+
// AI can match: "forgot my password", "can't log in", etc.
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### 3. Error Handling
|
|
537
|
+
|
|
538
|
+
Return user-friendly error messages:
|
|
539
|
+
|
|
540
|
+
```typescript
|
|
541
|
+
action: async (params) => {
|
|
542
|
+
try {
|
|
543
|
+
await this.api.call(params);
|
|
544
|
+
return { type: 'success', message: 'β
Done!' };
|
|
545
|
+
} catch (error) {
|
|
546
|
+
return {
|
|
547
|
+
type: 'error',
|
|
548
|
+
message: `β Something went wrong: ${error.message}`,
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### 4. Use Signals for Reactive State
|
|
555
|
+
|
|
556
|
+
```typescript
|
|
557
|
+
import { signal } from '@angular/core';
|
|
558
|
+
|
|
559
|
+
export class MyComponent {
|
|
560
|
+
theme = signal<'light' | 'dark'>('light');
|
|
561
|
+
|
|
562
|
+
constructor(private assistant: AssistantService) {
|
|
563
|
+
this.assistant.addCommand('toggle theme', () => {
|
|
564
|
+
const newTheme = this.theme() === 'light' ? 'dark' : 'light';
|
|
565
|
+
this.theme.set(newTheme);
|
|
566
|
+
return `Theme switched to ${newTheme}`;
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
}
|
|
169
570
|
```
|
|
170
571
|
|
|
171
572
|
---
|
|
172
573
|
|
|
173
|
-
##
|
|
574
|
+
## π§ͺ Testing
|
|
174
575
|
|
|
175
|
-
###
|
|
576
|
+
### Unit Testing Commands
|
|
176
577
|
|
|
177
|
-
|
|
578
|
+
```typescript
|
|
579
|
+
import { TestBed } from '@angular/core/testing';
|
|
580
|
+
import { AssistantService } from '@foisit/angular-wrapper';
|
|
581
|
+
|
|
582
|
+
describe('AssistantService', () => {
|
|
583
|
+
let service: AssistantService;
|
|
584
|
+
|
|
585
|
+
beforeEach(() => {
|
|
586
|
+
TestBed.configureTestingModule({
|
|
587
|
+
imports: [AssistantModule.forRoot({ commands: [] })],
|
|
588
|
+
});
|
|
589
|
+
service = TestBed.inject(AssistantService);
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
it('should add and execute command', () => {
|
|
593
|
+
let executed = false;
|
|
594
|
+
service.addCommand('test', () => {
|
|
595
|
+
executed = true;
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
// Trigger command execution
|
|
599
|
+
service.toggle();
|
|
600
|
+
// Test execution...
|
|
178
601
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
| `commands` | `Array` | A list of `{ command: string, action: Function }` |
|
|
602
|
+
expect(executed).toBe(true);
|
|
603
|
+
});
|
|
604
|
+
});
|
|
605
|
+
```
|
|
184
606
|
|
|
185
607
|
---
|
|
186
608
|
|
|
187
|
-
|
|
609
|
+
## οΏ½ Related Packages
|
|
188
610
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
| `removeCommand` | Remove an existing command dynamically. |
|
|
193
|
-
| `startListening` | Start listening for voice commands. |
|
|
194
|
-
| `stopListening` | Stop listening for voice commands. |
|
|
611
|
+
- **[@foisit/core](../core)** - Core engine (auto-installed)
|
|
612
|
+
- **[@foisit/react-wrapper](../react-wrapper)** - React integration
|
|
613
|
+
- **[@foisit/vue-wrapper](../vue-wrapper)** - Vue integration
|
|
195
614
|
|
|
196
615
|
---
|
|
197
616
|
|
|
198
|
-
##
|
|
617
|
+
## π Troubleshooting
|
|
618
|
+
|
|
619
|
+
### Assistant not appearing
|
|
620
|
+
|
|
621
|
+
Ensure `AssistantModule.forRoot()` is imported in your app configuration and double-tap the floating button.
|
|
622
|
+
|
|
623
|
+
### Commands not executing
|
|
199
624
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
625
|
+
Check browser console for errors. Ensure `action` functions are returning values or promises.
|
|
626
|
+
|
|
627
|
+
### TypeScript errors
|
|
628
|
+
|
|
629
|
+
Make sure you're using Angular 17+ and have `@angular/core` installed.
|
|
630
|
+
|
|
631
|
+
---
|
|
632
|
+
|
|
633
|
+
## π License
|
|
634
|
+
|
|
635
|
+
MIT Β© [Foisit](https://github.com/boluwatifee4/foisit)
|
|
205
636
|
|
|
206
637
|
---
|
|
207
638
|
|
|
208
639
|
## π€ Contributing
|
|
209
640
|
|
|
210
|
-
|
|
641
|
+
Contributions are welcome! Please read our [Contributing Guide](../../CONTRIBUTING.md) first.
|
|
211
642
|
|
|
212
643
|
---
|
|
213
644
|
|
|
214
|
-
##
|
|
645
|
+
## π¬ Support
|
|
646
|
+
|
|
647
|
+
- π§ Email: support@foisit.com
|
|
648
|
+
- π¬ Discord: [Join our community](https://discord.gg/foisit)
|
|
649
|
+
- π Issues: [GitHub Issues](https://github.com/boluwatifee4/foisit/issues)
|
|
650
|
+
|
|
651
|
+
---
|
|
215
652
|
|
|
216
|
-
|
|
653
|
+
**Made with β€οΈ by the Foisit Team**
|