@foisit/angular-wrapper 1.2.0 → 2.0.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 +620 -122
- package/fesm2022/foisit-angular-wrapper.mjs +288 -0
- package/fesm2022/foisit-angular-wrapper.mjs.map +1 -0
- package/index.d.ts +3 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,216 +1,714 @@
|
|
|
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] > **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
11
|
|
|
15
|
-
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Features](#features)
|
|
17
|
+
- [Installation](#installation)
|
|
18
|
+
- [Quick Start](#quick-start)
|
|
19
|
+
- [Core Concepts](#core-concepts)
|
|
20
|
+
- [API Reference](#api-reference)
|
|
21
|
+
- [Advanced Usage](#advanced-usage)
|
|
22
|
+
- [Examples](#examples)
|
|
23
|
+
- [TypeScript Support](#typescript-support)
|
|
24
|
+
- [Best Practices](#best-practices)
|
|
16
25
|
|
|
17
26
|
---
|
|
18
27
|
|
|
19
|
-
##
|
|
28
|
+
## Features
|
|
20
29
|
|
|
21
|
-
|
|
30
|
+
- **Natural Language Understanding** - AI-powered intent matching using GPT-4o mini (proxied securely)
|
|
31
|
+
- **Smart Slot Filling** - Auto-generates forms for missing parameters
|
|
32
|
+
- **Critical Action Protection** - Built-in confirmation dialogs for dangerous operations
|
|
33
|
+
- **Premium UI** - Glassmorphic overlay with dark/light mode support
|
|
34
|
+
- **Zero Backend Required** - Secure proxy architecture keeps API keys server-side
|
|
35
|
+
- **Angular Native** - Uses Dependency Injection, Signals, and RxJS
|
|
36
|
+
- **Type-Safe** - Full TypeScript support with comprehensive types
|
|
37
|
+
- **Responsive** - Works flawlessly on desktop and mobile
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
22
42
|
|
|
23
43
|
```bash
|
|
24
44
|
npm install @foisit/angular-wrapper
|
|
25
45
|
```
|
|
26
46
|
|
|
27
|
-
|
|
47
|
+
### Peer Dependencies
|
|
28
48
|
|
|
29
|
-
```
|
|
30
|
-
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"@angular/core": "^17.0.0 || ^18.0.0",
|
|
52
|
+
"@angular/common": "^17.0.0 || ^18.0.0"
|
|
53
|
+
}
|
|
31
54
|
```
|
|
32
55
|
|
|
33
56
|
---
|
|
34
57
|
|
|
35
|
-
##
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
### Step 1: Import the Module
|
|
61
|
+
|
|
62
|
+
#### For Standalone Apps (Recommended)
|
|
36
63
|
|
|
37
|
-
|
|
64
|
+
```typescript
|
|
65
|
+
// app.config.ts
|
|
66
|
+
import { ApplicationConfig, importProvidersFrom } from '@angular/core';
|
|
67
|
+
import { AssistantModule } from '@foisit/angular-wrapper';
|
|
38
68
|
|
|
39
|
-
|
|
69
|
+
export const appConfig: ApplicationConfig = {
|
|
70
|
+
providers: [
|
|
71
|
+
importProvidersFrom(
|
|
72
|
+
AssistantModule.forRoot({
|
|
73
|
+
introMessage: 'Welcome! How can I assist you today?',
|
|
74
|
+
enableSmartIntent: true,
|
|
75
|
+
commands: [
|
|
76
|
+
{
|
|
77
|
+
command: 'navigate to profile',
|
|
78
|
+
action: () => console.log('Navigating to profile...'),
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
})
|
|
82
|
+
),
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
```
|
|
40
86
|
|
|
41
|
-
####
|
|
87
|
+
#### For Module-Based Apps
|
|
42
88
|
|
|
43
89
|
```typescript
|
|
90
|
+
// app.module.ts
|
|
44
91
|
import { NgModule } from '@angular/core';
|
|
45
|
-
import { BrowserModule } from '@angular/platform-browser';
|
|
46
|
-
import { AppComponent } from './app.component';
|
|
47
92
|
import { AssistantModule } from '@foisit/angular-wrapper';
|
|
48
93
|
|
|
49
94
|
@NgModule({
|
|
50
|
-
declarations: [AppComponent],
|
|
51
95
|
imports: [
|
|
52
|
-
BrowserModule,
|
|
53
96
|
AssistantModule.forRoot({
|
|
54
|
-
|
|
55
|
-
fallbackResponse: 'Sorry, I didn’t understand that.',
|
|
97
|
+
introMessage: 'Welcome! How can I assist you today?',
|
|
56
98
|
commands: [
|
|
57
|
-
|
|
58
|
-
{ command: 'log out', action: () => console.log('Logging out...') },
|
|
99
|
+
/* your commands */
|
|
59
100
|
],
|
|
60
101
|
}),
|
|
61
102
|
],
|
|
62
|
-
bootstrap: [AppComponent],
|
|
63
103
|
})
|
|
64
104
|
export class AppModule {}
|
|
65
105
|
```
|
|
66
106
|
|
|
67
|
-
|
|
107
|
+
### Step 2: Use the Service
|
|
68
108
|
|
|
69
109
|
```typescript
|
|
70
|
-
|
|
71
|
-
import {
|
|
72
|
-
import {
|
|
73
|
-
import { AssistantModule } from '@foisit/angular-wrapper';
|
|
110
|
+
// my-component.ts
|
|
111
|
+
import { Component } from '@angular/core';
|
|
112
|
+
import { AssistantService } from '@foisit/angular-wrapper';
|
|
74
113
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
114
|
+
@Component({
|
|
115
|
+
selector: 'app-my-component',
|
|
116
|
+
template: ` <button (click)="openAssistant()">Open Assistant</button> `,
|
|
117
|
+
})
|
|
118
|
+
export class MyComponent {
|
|
119
|
+
constructor(private assistant: AssistantService) {}
|
|
120
|
+
|
|
121
|
+
openAssistant() {
|
|
122
|
+
this.assistant.toggle();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Core Concepts
|
|
130
|
+
|
|
131
|
+
### 1. Commands
|
|
132
|
+
|
|
133
|
+
Commands are the building blocks of your assistant. Each command represents an action users can trigger through natural language.
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
{
|
|
137
|
+
command: 'delete account',
|
|
138
|
+
description: 'Permanently delete user account',
|
|
139
|
+
action: () => this.accountService.delete()
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 2. Parameters (Slot Filling)
|
|
144
|
+
|
|
145
|
+
Define parameters and Foisit will automatically generate forms to collect them:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
{
|
|
149
|
+
command: 'create user',
|
|
150
|
+
description: 'Create a new user account',
|
|
151
|
+
parameters: [
|
|
152
|
+
{ name: 'username', type: 'string', required: true },
|
|
153
|
+
{ name: 'email', type: 'string', required: true },
|
|
154
|
+
{ name: 'age', type: 'number', required: false }
|
|
89
155
|
],
|
|
90
|
-
|
|
156
|
+
action: (params) => this.userService.create(params)
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Supported Parameter Types:**
|
|
161
|
+
|
|
162
|
+
- `string` - Text input
|
|
163
|
+
- `number` - Numeric input
|
|
164
|
+
- `date` - Date picker
|
|
165
|
+
- `select` - Dropdown (static or async options)
|
|
166
|
+
- `file` - File upload input
|
|
167
|
+
|
|
168
|
+
### 3. File Parameters
|
|
169
|
+
|
|
170
|
+
Collect files via the built-in form UI and receive them in your command `action`.
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
{
|
|
174
|
+
command: 'upload file',
|
|
175
|
+
description: 'Pick a file and return it to the action',
|
|
176
|
+
parameters: [
|
|
177
|
+
{
|
|
178
|
+
name: 'attachment',
|
|
179
|
+
type: 'file',
|
|
180
|
+
required: true,
|
|
181
|
+
accept: ['image/*', 'audio/*', 'video/*'],
|
|
182
|
+
multiple: false,
|
|
183
|
+
// delivery: 'file' | 'base64' (default: 'file')
|
|
184
|
+
delivery: 'file',
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
action: async (params) => {
|
|
188
|
+
const file = params?.attachment as File | undefined;
|
|
189
|
+
if (!file) return { type: 'error', message: 'No file provided.' };
|
|
190
|
+
return {
|
|
191
|
+
type: 'success',
|
|
192
|
+
message: `File received. Name: ${file.name}, Type: ${file.type || 'unknown'}, Size: ${file.size} bytes`,
|
|
193
|
+
};
|
|
194
|
+
},
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
`FileParameter` supports validations like `maxFiles`, `maxSizeBytes`, `maxTotalBytes`, and media/image constraints like `maxDurationSec`, `maxWidth`, and `maxHeight`.
|
|
199
|
+
|
|
200
|
+
### 4. Critical Actions
|
|
201
|
+
|
|
202
|
+
Protect dangerous operations with automatic confirmation dialogs:
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
{
|
|
206
|
+
command: 'delete all data',
|
|
207
|
+
critical: true, // Requires confirmation
|
|
208
|
+
description: 'Permanently delete all application data',
|
|
209
|
+
action: async () => {
|
|
210
|
+
await this.dataService.deleteAll();
|
|
211
|
+
return 'All data deleted successfully.';
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### 5. Select Parameters (Static)
|
|
217
|
+
|
|
218
|
+
Provide predefined options:
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
{
|
|
222
|
+
command: 'set theme',
|
|
223
|
+
parameters: [{
|
|
224
|
+
name: 'theme',
|
|
225
|
+
type: 'select',
|
|
226
|
+
options: [
|
|
227
|
+
{ label: 'Light Mode', value: 'light' },
|
|
228
|
+
{ label: 'Dark Mode', value: 'dark' },
|
|
229
|
+
{ label: 'Auto', value: 'auto' }
|
|
230
|
+
]
|
|
231
|
+
}],
|
|
232
|
+
action: (params) => this.themeService.set(params.theme)
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### 6. Dynamic Select Parameters
|
|
237
|
+
|
|
238
|
+
Load options from APIs:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
{
|
|
242
|
+
command: 'assign to user',
|
|
243
|
+
parameters: [{
|
|
244
|
+
name: 'userId',
|
|
245
|
+
type: 'select',
|
|
246
|
+
getOptions: async () => {
|
|
247
|
+
const users = await this.userService.getAll();
|
|
248
|
+
return users.map(u => ({
|
|
249
|
+
label: `${u.name} (${u.email})`,
|
|
250
|
+
value: u.id
|
|
251
|
+
}));
|
|
252
|
+
}
|
|
253
|
+
}],
|
|
254
|
+
action: (params) => this.taskService.assign(params.userId)
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## API Reference
|
|
261
|
+
|
|
262
|
+
### `AssistantService`
|
|
263
|
+
|
|
264
|
+
Injectable service for programmatic control:
|
|
265
|
+
|
|
266
|
+
#### Methods
|
|
267
|
+
|
|
268
|
+
##### `toggle(onSubmit?, onClose?)`
|
|
269
|
+
|
|
270
|
+
Opens or closes the assistant overlay.
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
// Basic usage
|
|
274
|
+
this.assistant.toggle();
|
|
275
|
+
|
|
276
|
+
// With callbacks
|
|
277
|
+
this.assistant.toggle(
|
|
278
|
+
(userInput) => console.log('User said:', userInput),
|
|
279
|
+
() => console.log('Assistant closed')
|
|
280
|
+
);
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
##### `addCommand(command, action?)`
|
|
284
|
+
|
|
285
|
+
Dynamically add or update a command at runtime. Commands added via `addCommand` take effect immediately in the running application; they are stored in memory for the current session (they are not persisted after a page reload unless you re-register them during app startup).
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
// Add a simple command
|
|
289
|
+
this.assistant.addCommand('refresh data', () => {
|
|
290
|
+
this.dataService.refresh();
|
|
291
|
+
return 'Data refreshed!';
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// Add a command with full config
|
|
295
|
+
this.assistant.addCommand({
|
|
296
|
+
command: 'export report',
|
|
297
|
+
description: 'Export data as PDF',
|
|
298
|
+
parameters: [
|
|
299
|
+
{
|
|
300
|
+
name: 'format',
|
|
301
|
+
type: 'select',
|
|
302
|
+
options: [
|
|
303
|
+
{ label: 'PDF', value: 'pdf' },
|
|
304
|
+
{ label: 'Excel', value: 'xlsx' },
|
|
305
|
+
],
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
action: async (params) => {
|
|
309
|
+
await this.reportService.export(params.format);
|
|
310
|
+
return `Report exported as ${params.format}`;
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Dynamic Updates (Add / Remove / Update commands at runtime) ✅
|
|
316
|
+
|
|
317
|
+
- Use `addCommand` to register a new command or to replace an existing command's behavior (remove first if you want a clean replacement).
|
|
318
|
+
- Use `removeCommand(commandPhrase)` to unregister a command. This is useful for temporary or context-specific commands.
|
|
319
|
+
- Commands are in-memory only; to persist them across reloads, re-register on app startup (e.g., in an initialization hook).
|
|
320
|
+
|
|
321
|
+
Example — register a temporary command and clean it up in `ngOnDestroy`:
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
// component.ts
|
|
325
|
+
import { OnDestroy } from '@angular/core';
|
|
326
|
+
|
|
327
|
+
export class MyComponent implements OnDestroy {
|
|
328
|
+
constructor(private assistant: AssistantService) {
|
|
329
|
+
this.assistant.addCommand('temp action', () => 'Temporary action executed');
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
ngOnDestroy() {
|
|
333
|
+
// Remove the temporary command when component unmounts
|
|
334
|
+
this.assistant.removeCommand('temp action');
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Notes:
|
|
340
|
+
|
|
341
|
+
- If a command has only optional params, consider returning a `form` InteractiveResponse to prompt the user when no params are provided.
|
|
342
|
+
- Always remove transient commands in your cleanup lifecycle to avoid leaks and confusing UX.
|
|
343
|
+
|
|
344
|
+
##### `removeCommand(commandPhrase)`
|
|
345
|
+
|
|
346
|
+
Remove a registered command.
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
this.assistant.removeCommand('delete account');
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
##### `getCommands()`
|
|
353
|
+
|
|
354
|
+
Get list of all registered command names.
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
const commands = this.assistant.getCommands();
|
|
358
|
+
console.log('Available commands:', commands);
|
|
91
359
|
```
|
|
92
360
|
|
|
93
361
|
---
|
|
94
362
|
|
|
95
|
-
|
|
363
|
+
## Configuration Options
|
|
364
|
+
|
|
365
|
+
### `AssistantConfig`
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
interface AssistantConfig {
|
|
369
|
+
// Activation keyword (optional)
|
|
370
|
+
activationCommand?: string;
|
|
96
371
|
|
|
97
|
-
|
|
372
|
+
// Welcome message shown when assistant opens
|
|
373
|
+
introMessage?: string;
|
|
98
374
|
|
|
99
|
-
|
|
375
|
+
// Response for unrecognized inputs
|
|
376
|
+
fallbackResponse?: string;
|
|
377
|
+
|
|
378
|
+
// Enable AI-powered natural language understanding
|
|
379
|
+
enableSmartIntent?: boolean;
|
|
380
|
+
|
|
381
|
+
// Input field placeholder text
|
|
382
|
+
inputPlaceholder?: string;
|
|
383
|
+
|
|
384
|
+
// List of commands
|
|
385
|
+
commands: AssistantCommand[];
|
|
386
|
+
|
|
387
|
+
// Floating button configuration
|
|
388
|
+
floatingButton?: {
|
|
389
|
+
visible?: boolean;
|
|
390
|
+
tooltip?: string;
|
|
391
|
+
customHtml?: string;
|
|
392
|
+
position?: { bottom: string; right: string };
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## Advanced Usage
|
|
400
|
+
|
|
401
|
+
### Example 1: Multi-Step Booking System
|
|
100
402
|
|
|
101
403
|
```typescript
|
|
102
|
-
import { Component
|
|
404
|
+
import { Component } from '@angular/core';
|
|
103
405
|
import { AssistantService } from '@foisit/angular-wrapper';
|
|
104
406
|
|
|
105
407
|
@Component({
|
|
106
|
-
selector: 'app-
|
|
107
|
-
|
|
108
|
-
styleUrls: ['./app.component.scss'],
|
|
408
|
+
selector: 'app-booking',
|
|
409
|
+
template: `<button (click)="setupBooking()">Enable Booking</button>`,
|
|
109
410
|
})
|
|
110
|
-
export class
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
411
|
+
export class BookingComponent {
|
|
412
|
+
constructor(private assistant: AssistantService, private bookingService: BookingService) {}
|
|
413
|
+
|
|
414
|
+
setupBooking() {
|
|
415
|
+
this.assistant.addCommand({
|
|
416
|
+
command: 'book appointment',
|
|
417
|
+
description: 'Schedule a new appointment',
|
|
418
|
+
parameters: [
|
|
419
|
+
{
|
|
420
|
+
name: 'service',
|
|
421
|
+
description: 'Type of service',
|
|
422
|
+
type: 'select',
|
|
423
|
+
required: true,
|
|
424
|
+
getOptions: async () => {
|
|
425
|
+
const services = await this.bookingService.getServices();
|
|
426
|
+
return services.map((s) => ({
|
|
427
|
+
label: s.name,
|
|
428
|
+
value: s.id,
|
|
429
|
+
}));
|
|
430
|
+
},
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
name: 'date',
|
|
434
|
+
description: 'Preferred date',
|
|
435
|
+
type: 'date',
|
|
436
|
+
required: true,
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
name: 'notes',
|
|
440
|
+
description: 'Additional notes',
|
|
441
|
+
type: 'string',
|
|
442
|
+
required: false,
|
|
443
|
+
},
|
|
444
|
+
],
|
|
445
|
+
action: async (params) => {
|
|
446
|
+
const booking = await this.bookingService.create(params);
|
|
447
|
+
return {
|
|
448
|
+
type: 'success',
|
|
449
|
+
message: `✅ Appointment booked for ${params.date}!`,
|
|
450
|
+
};
|
|
451
|
+
},
|
|
118
452
|
});
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
```
|
|
119
456
|
|
|
120
|
-
|
|
121
|
-
this.color.set('transparent');
|
|
122
|
-
});
|
|
457
|
+
### Example 2: E-Commerce Product Search
|
|
123
458
|
|
|
124
|
-
|
|
125
|
-
|
|
459
|
+
```typescript
|
|
460
|
+
this.assistant.addCommand({
|
|
461
|
+
command: 'search products',
|
|
462
|
+
parameters: [
|
|
463
|
+
{ name: 'query', type: 'string', required: true },
|
|
464
|
+
{
|
|
465
|
+
name: 'category',
|
|
466
|
+
type: 'select',
|
|
467
|
+
required: false,
|
|
468
|
+
options: [
|
|
469
|
+
{ label: 'Electronics', value: 'electronics' },
|
|
470
|
+
{ label: 'Clothing', value: 'clothing' },
|
|
471
|
+
{ label: 'Books', value: 'books' },
|
|
472
|
+
],
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
name: 'minPrice',
|
|
476
|
+
type: 'number',
|
|
477
|
+
required: false,
|
|
478
|
+
},
|
|
479
|
+
],
|
|
480
|
+
action: async (params) => {
|
|
481
|
+
const results = await this.productService.search(params);
|
|
482
|
+
this.router.navigate(['/products'], {
|
|
483
|
+
queryParams: { q: params.query },
|
|
126
484
|
});
|
|
485
|
+
return `Found ${results.length} products matching "${params.query}"`;
|
|
486
|
+
},
|
|
487
|
+
});
|
|
488
|
+
```
|
|
127
489
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
490
|
+
### Example 3: Form Validation with Error Handling
|
|
491
|
+
|
|
492
|
+
```typescript
|
|
493
|
+
this.assistant.addCommand({
|
|
494
|
+
command: 'create account',
|
|
495
|
+
parameters: [
|
|
496
|
+
{ name: 'email', type: 'string', required: true },
|
|
497
|
+
{ name: 'password', type: 'string', required: true },
|
|
498
|
+
{ name: 'age', type: 'number', required: true },
|
|
499
|
+
],
|
|
500
|
+
action: async (params) => {
|
|
501
|
+
// Validation
|
|
502
|
+
if (params.age < 18) {
|
|
503
|
+
return {
|
|
504
|
+
type: 'error',
|
|
505
|
+
message: '❌ You must be 18 or older to create an account.',
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (!params.email.includes('@')) {
|
|
510
|
+
return {
|
|
511
|
+
type: 'error',
|
|
512
|
+
message: '❌ Please provide a valid email address.',
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Create account
|
|
517
|
+
try {
|
|
518
|
+
await this.authService.register(params);
|
|
519
|
+
return {
|
|
520
|
+
type: 'success',
|
|
521
|
+
message: '✅ Account created successfully! You can now log in.',
|
|
522
|
+
};
|
|
523
|
+
} catch (error) {
|
|
524
|
+
return {
|
|
525
|
+
type: 'error',
|
|
526
|
+
message: `❌ Registration failed: ${error.message}`,
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
},
|
|
530
|
+
});
|
|
132
531
|
```
|
|
133
532
|
|
|
134
533
|
---
|
|
135
534
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
Create a clean UI to showcase your assistant's magic.
|
|
535
|
+
## TypeScript Support
|
|
139
536
|
|
|
140
|
-
|
|
537
|
+
### Full Type Definitions
|
|
141
538
|
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
539
|
+
```typescript
|
|
540
|
+
import { AssistantCommand, InteractiveResponse } from '@foisit/core';
|
|
541
|
+
|
|
542
|
+
// Type-safe command definition
|
|
543
|
+
const myCommand: AssistantCommand = {
|
|
544
|
+
command: 'update settings',
|
|
545
|
+
description: 'Update user preferences',
|
|
546
|
+
parameters: [
|
|
547
|
+
{
|
|
548
|
+
name: 'theme',
|
|
549
|
+
type: 'select',
|
|
550
|
+
required: true,
|
|
551
|
+
options: [
|
|
552
|
+
{ label: 'Light', value: 'light' },
|
|
553
|
+
{ label: 'Dark', value: 'dark' },
|
|
554
|
+
],
|
|
555
|
+
},
|
|
556
|
+
],
|
|
557
|
+
action: async (params: { theme: string }): Promise<InteractiveResponse> => {
|
|
558
|
+
await this.settingsService.update(params.theme);
|
|
559
|
+
return {
|
|
560
|
+
type: 'success',
|
|
561
|
+
message: `Theme updated to ${params.theme}`,
|
|
562
|
+
};
|
|
563
|
+
},
|
|
564
|
+
};
|
|
159
565
|
```
|
|
160
566
|
|
|
161
567
|
---
|
|
162
568
|
|
|
163
|
-
|
|
569
|
+
## Best Practices
|
|
164
570
|
|
|
165
|
-
|
|
571
|
+
### 1. Command Naming
|
|
166
572
|
|
|
167
|
-
|
|
168
|
-
|
|
573
|
+
✅ **Good:**
|
|
574
|
+
|
|
575
|
+
- "create user"
|
|
576
|
+
- "delete account"
|
|
577
|
+
- "export report"
|
|
578
|
+
|
|
579
|
+
❌ **Avoid:**
|
|
580
|
+
|
|
581
|
+
- "CreateUser" (not natural)
|
|
582
|
+
- "usr_del" (not descriptive)
|
|
583
|
+
- "do the thing" (too vague)
|
|
584
|
+
|
|
585
|
+
### 2. Descriptions
|
|
586
|
+
|
|
587
|
+
Always provide clear descriptions for AI matching:
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
{
|
|
591
|
+
command: 'reset password',
|
|
592
|
+
description: 'Reset the user password and send recovery email',
|
|
593
|
+
// AI can match: "forgot my password", "can't log in", etc.
|
|
594
|
+
}
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
### 3. Error Handling
|
|
598
|
+
|
|
599
|
+
Return user-friendly error messages:
|
|
600
|
+
|
|
601
|
+
```typescript
|
|
602
|
+
action: async (params) => {
|
|
603
|
+
try {
|
|
604
|
+
await this.api.call(params);
|
|
605
|
+
return { type: 'success', message: '✅ Done!' };
|
|
606
|
+
} catch (error) {
|
|
607
|
+
return {
|
|
608
|
+
type: 'error',
|
|
609
|
+
message: `❌ Something went wrong: ${error.message}`,
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
### 4. Use Signals for Reactive State
|
|
616
|
+
|
|
617
|
+
```typescript
|
|
618
|
+
import { signal } from '@angular/core';
|
|
619
|
+
|
|
620
|
+
export class MyComponent {
|
|
621
|
+
theme = signal<'light' | 'dark'>('light');
|
|
622
|
+
|
|
623
|
+
constructor(private assistant: AssistantService) {
|
|
624
|
+
this.assistant.addCommand('toggle theme', () => {
|
|
625
|
+
const newTheme = this.theme() === 'light' ? 'dark' : 'light';
|
|
626
|
+
this.theme.set(newTheme);
|
|
627
|
+
return `Theme switched to ${newTheme}`;
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
}
|
|
169
631
|
```
|
|
170
632
|
|
|
171
633
|
---
|
|
172
634
|
|
|
173
|
-
##
|
|
635
|
+
## Testing
|
|
174
636
|
|
|
175
|
-
###
|
|
637
|
+
### Unit Testing Commands
|
|
638
|
+
|
|
639
|
+
```typescript
|
|
640
|
+
import { TestBed } from '@angular/core/testing';
|
|
641
|
+
import { AssistantService } from '@foisit/angular-wrapper';
|
|
176
642
|
|
|
177
|
-
|
|
643
|
+
describe('AssistantService', () => {
|
|
644
|
+
let service: AssistantService;
|
|
178
645
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
646
|
+
beforeEach(() => {
|
|
647
|
+
TestBed.configureTestingModule({
|
|
648
|
+
imports: [AssistantModule.forRoot({ commands: [] })],
|
|
649
|
+
});
|
|
650
|
+
service = TestBed.inject(AssistantService);
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
it('should add and execute command', () => {
|
|
654
|
+
let executed = false;
|
|
655
|
+
service.addCommand('test', () => {
|
|
656
|
+
executed = true;
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
// Trigger command execution
|
|
660
|
+
service.toggle();
|
|
661
|
+
// Test execution...
|
|
662
|
+
|
|
663
|
+
expect(executed).toBe(true);
|
|
664
|
+
});
|
|
665
|
+
});
|
|
666
|
+
```
|
|
184
667
|
|
|
185
668
|
---
|
|
186
669
|
|
|
187
|
-
|
|
670
|
+
## Related Packages
|
|
188
671
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
| `removeCommand` | Remove an existing command dynamically. |
|
|
193
|
-
| `startListening` | Start listening for voice commands. |
|
|
194
|
-
| `stopListening` | Stop listening for voice commands. |
|
|
672
|
+
- **[@foisit/core](../core)** - Core engine (auto-installed)
|
|
673
|
+
- **[@foisit/react-wrapper](../react-wrapper)** - React integration
|
|
674
|
+
- **[@foisit/vue-wrapper](../vue-wrapper)** - Vue integration
|
|
195
675
|
|
|
196
676
|
---
|
|
197
677
|
|
|
198
|
-
##
|
|
678
|
+
## Troubleshooting
|
|
199
679
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
680
|
+
### Assistant not appearing
|
|
681
|
+
|
|
682
|
+
Ensure `AssistantModule.forRoot()` is imported in your app configuration and double-tap the floating button.
|
|
683
|
+
|
|
684
|
+
### Commands not executing
|
|
685
|
+
|
|
686
|
+
Check browser console for errors. Ensure `action` functions are returning values or promises.
|
|
687
|
+
|
|
688
|
+
### TypeScript errors
|
|
689
|
+
|
|
690
|
+
Make sure you're using Angular 17+ and have `@angular/core` installed.
|
|
691
|
+
|
|
692
|
+
---
|
|
693
|
+
|
|
694
|
+
## License
|
|
695
|
+
|
|
696
|
+
MIT © [Foisit](https://github.com/boluwatifee4/foisit)
|
|
205
697
|
|
|
206
698
|
---
|
|
207
699
|
|
|
208
|
-
##
|
|
700
|
+
## Contributing
|
|
209
701
|
|
|
210
|
-
|
|
702
|
+
Contributions are welcome! Please read our [Contributing Guide](../../CONTRIBUTING.md) first.
|
|
211
703
|
|
|
212
704
|
---
|
|
213
705
|
|
|
214
|
-
##
|
|
706
|
+
## Support
|
|
707
|
+
|
|
708
|
+
- Email: support@foisit.com
|
|
709
|
+
- Discord: [Join our community](https://discord.gg/foisit)
|
|
710
|
+
- Issues: [GitHub Issues](https://github.com/boluwatifee4/foisit/issues)
|
|
711
|
+
|
|
712
|
+
---
|
|
215
713
|
|
|
216
|
-
|
|
714
|
+
**Made by the Foisit Team**
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Component, Inject, Injectable, NgModule } from '@angular/core';
|
|
3
|
+
import { CommonModule } from '@angular/common';
|
|
4
|
+
import { CommandHandler, FallbackHandler, VoiceProcessor, TextToSpeech, StateManager, GestureHandler, OverlayManager } from '@foisit/core';
|
|
5
|
+
|
|
6
|
+
class AngularWrapperComponent {
|
|
7
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: AngularWrapperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.0.7", type: AngularWrapperComponent, isStandalone: true, selector: "lib-angular-wrapper", ngImport: i0, template: "<p>AngularWrapper works!</p>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
9
|
+
}
|
|
10
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: AngularWrapperComponent, decorators: [{
|
|
11
|
+
type: Component,
|
|
12
|
+
args: [{ selector: 'lib-angular-wrapper', imports: [CommonModule], template: "<p>AngularWrapper works!</p>\n" }]
|
|
13
|
+
}] });
|
|
14
|
+
|
|
15
|
+
class AssistantService {
|
|
16
|
+
config;
|
|
17
|
+
commandHandler;
|
|
18
|
+
fallbackHandler;
|
|
19
|
+
voiceProcessor;
|
|
20
|
+
textToSpeech;
|
|
21
|
+
stateManager;
|
|
22
|
+
lastProcessedInput = '';
|
|
23
|
+
processingLock = false;
|
|
24
|
+
isActivated = false; // Tracks activation status
|
|
25
|
+
gestureHandler;
|
|
26
|
+
overlayManager;
|
|
27
|
+
defaultIntroMessage = 'How can I help you?';
|
|
28
|
+
constructor(config) {
|
|
29
|
+
this.config = config;
|
|
30
|
+
// Pass enableSmartIntent (default true) and potential apiKey (if we add it to config later)
|
|
31
|
+
this.commandHandler = new CommandHandler({
|
|
32
|
+
enableSmartIntent: this.config.enableSmartIntent !== false,
|
|
33
|
+
intentEndpoint: this.config.intentEndpoint,
|
|
34
|
+
});
|
|
35
|
+
this.fallbackHandler = new FallbackHandler();
|
|
36
|
+
this.voiceProcessor = new VoiceProcessor();
|
|
37
|
+
this.textToSpeech = new TextToSpeech();
|
|
38
|
+
this.stateManager = new StateManager();
|
|
39
|
+
this.gestureHandler = new GestureHandler();
|
|
40
|
+
this.overlayManager = new OverlayManager({
|
|
41
|
+
floatingButton: this.config.floatingButton,
|
|
42
|
+
inputPlaceholder: this.config.inputPlaceholder,
|
|
43
|
+
});
|
|
44
|
+
// Setup commands from config
|
|
45
|
+
this.config.commands.forEach((cmd) => this.commandHandler.addCommand(cmd));
|
|
46
|
+
// Setup fallback response
|
|
47
|
+
if (this.config.fallbackResponse) {
|
|
48
|
+
this.fallbackHandler.setFallbackMessage(this.config.fallbackResponse);
|
|
49
|
+
}
|
|
50
|
+
// Voice disabled for text-first pivot
|
|
51
|
+
// this.startListening();
|
|
52
|
+
// Setup double-tap/double-click listener
|
|
53
|
+
this.gestureHandler.setupDoubleTapListener(() => this.toggle());
|
|
54
|
+
// Register global callbacks for floating button
|
|
55
|
+
this.overlayManager.registerCallbacks(async (input) => {
|
|
56
|
+
if (typeof input === 'string') {
|
|
57
|
+
this.overlayManager.addMessage(input, 'user');
|
|
58
|
+
}
|
|
59
|
+
else if (input && typeof input === 'object') {
|
|
60
|
+
const label = this.extractUserLabel(input);
|
|
61
|
+
if (label) {
|
|
62
|
+
this.overlayManager.addMessage(label, 'user');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
await this.handleCommand(input);
|
|
66
|
+
}, () => console.log('AssistantService: Overlay closed.'));
|
|
67
|
+
}
|
|
68
|
+
/** Start listening for activation and commands */
|
|
69
|
+
startListening() {
|
|
70
|
+
// Voice is currently disabled (text-only mode)
|
|
71
|
+
console.log('AssistantService: Voice is disabled; startListening() is a no-op.');
|
|
72
|
+
return;
|
|
73
|
+
/*
|
|
74
|
+
this.voiceProcessor.startListening(async (transcript: string) => {
|
|
75
|
+
if (this.processingLock) return;
|
|
76
|
+
|
|
77
|
+
const normalizedTranscript = transcript.toLowerCase().trim();
|
|
78
|
+
|
|
79
|
+
// Skip repeated or irrelevant inputs
|
|
80
|
+
if (
|
|
81
|
+
!normalizedTranscript ||
|
|
82
|
+
normalizedTranscript.length < 3 ||
|
|
83
|
+
normalizedTranscript === this.lastProcessedInput
|
|
84
|
+
) {
|
|
85
|
+
console.log('AssistantService: Ignoring irrelevant input.');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this.lastProcessedInput = normalizedTranscript;
|
|
90
|
+
|
|
91
|
+
if (!this.isActivated) {
|
|
92
|
+
await this.processActivation(normalizedTranscript);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const isFallbackOrIntroMessage =
|
|
97
|
+
normalizedTranscript === this.config.fallbackResponse?.toLowerCase() ||
|
|
98
|
+
normalizedTranscript === this.config.introMessage?.toLowerCase() ||
|
|
99
|
+
normalizedTranscript === this.defaultIntroMessage.toLowerCase();
|
|
100
|
+
|
|
101
|
+
if (!isFallbackOrIntroMessage) {
|
|
102
|
+
await this.handleCommand(normalizedTranscript);
|
|
103
|
+
} else {
|
|
104
|
+
console.log('AssistantService: Ignoring fallback or intro message.');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Throttle input processing to prevent rapid feedback
|
|
108
|
+
this.processingLock = true;
|
|
109
|
+
setTimeout(() => (this.processingLock = false), 1000);
|
|
110
|
+
});
|
|
111
|
+
*/
|
|
112
|
+
}
|
|
113
|
+
/** Stop listening for voice input */
|
|
114
|
+
stopListening() {
|
|
115
|
+
// Voice is currently disabled (text-only mode)
|
|
116
|
+
console.log('AssistantService: Voice is disabled; stopListening() is a no-op.');
|
|
117
|
+
this.isActivated = false;
|
|
118
|
+
}
|
|
119
|
+
/** Reset activation state so the next activation flow can occur. */
|
|
120
|
+
reactivate() {
|
|
121
|
+
console.log('AssistantService: Reactivating assistant...');
|
|
122
|
+
this.isActivated = false;
|
|
123
|
+
try {
|
|
124
|
+
this.startListening();
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// no-op
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/** Process activation command */
|
|
131
|
+
async processActivation(transcript) {
|
|
132
|
+
const activationCmd = this.config.activationCommand?.toLowerCase();
|
|
133
|
+
// If no activation command is set, we can't activate via voice
|
|
134
|
+
if (!activationCmd)
|
|
135
|
+
return;
|
|
136
|
+
if (transcript === activationCmd) {
|
|
137
|
+
console.log('AssistantService: Activation matched.');
|
|
138
|
+
this.isActivated = true;
|
|
139
|
+
this.textToSpeech.speak(this.config.introMessage || this.defaultIntroMessage);
|
|
140
|
+
// this.stateManager.setState('listening'); // DISABLED - no gradient animation
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
console.log('AssistantService: Activation command not recognized.');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** Handle recognized commands */
|
|
147
|
+
async handleCommand(input) {
|
|
148
|
+
this.overlayManager.showLoading();
|
|
149
|
+
let response;
|
|
150
|
+
try {
|
|
151
|
+
response = await this.commandHandler.executeCommand(input);
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
this.overlayManager.hideLoading();
|
|
155
|
+
}
|
|
156
|
+
const originalText = typeof input === 'string' ? input : undefined;
|
|
157
|
+
this.processResponse(response, originalText);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Cleanup resources
|
|
161
|
+
*/
|
|
162
|
+
destroy() {
|
|
163
|
+
this.voiceProcessor.stopListening();
|
|
164
|
+
this.gestureHandler.destroy();
|
|
165
|
+
this.overlayManager.destroy();
|
|
166
|
+
}
|
|
167
|
+
/** Unified response processing */
|
|
168
|
+
processResponse(response, originalText) {
|
|
169
|
+
if (response.type === 'error' && originalText) {
|
|
170
|
+
this.fallbackHandler.handleFallback(originalText);
|
|
171
|
+
this.overlayManager.addMessage(this.fallbackHandler.getFallbackMessage(), 'system');
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (response.type === 'form' && response.fields) {
|
|
175
|
+
this.overlayManager.addForm(response.message, response.fields, async (data) => {
|
|
176
|
+
this.overlayManager.showLoading();
|
|
177
|
+
let nextReponse;
|
|
178
|
+
try {
|
|
179
|
+
nextReponse = await this.commandHandler.executeCommand(data);
|
|
180
|
+
}
|
|
181
|
+
finally {
|
|
182
|
+
this.overlayManager.hideLoading();
|
|
183
|
+
}
|
|
184
|
+
this.processResponse(nextReponse);
|
|
185
|
+
});
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if ((response.type === 'ambiguous' || response.type === 'confirm') && response.options) {
|
|
189
|
+
if (response.message) {
|
|
190
|
+
this.overlayManager.addMessage(response.message, 'system');
|
|
191
|
+
}
|
|
192
|
+
this.overlayManager.addOptions(response.options);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (response.message) {
|
|
196
|
+
this.overlayManager.addMessage(response.message, 'system');
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/** Add a command dynamically (supports string or rich object) */
|
|
200
|
+
addCommand(commandOrObj, action) {
|
|
201
|
+
if (typeof commandOrObj === 'string') {
|
|
202
|
+
console.log(`AssistantService: Adding command "${commandOrObj}".`);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
console.log(`AssistantService: Adding rich command "${commandOrObj.command}".`);
|
|
206
|
+
}
|
|
207
|
+
this.commandHandler.addCommand(commandOrObj, action);
|
|
208
|
+
}
|
|
209
|
+
/** Remove a command dynamically */
|
|
210
|
+
removeCommand(command) {
|
|
211
|
+
console.log(`AssistantService: Removing command "${command}".`);
|
|
212
|
+
this.commandHandler.removeCommand(command);
|
|
213
|
+
}
|
|
214
|
+
/** Get all registered commands */
|
|
215
|
+
getCommands() {
|
|
216
|
+
return this.commandHandler.getCommands();
|
|
217
|
+
}
|
|
218
|
+
/** Toggle the assistant overlay */
|
|
219
|
+
toggle(onSubmit, onClose) {
|
|
220
|
+
console.log('AssistantService: Toggling overlay...');
|
|
221
|
+
this.overlayManager.toggle(async (input) => {
|
|
222
|
+
if (typeof input === 'string') {
|
|
223
|
+
this.overlayManager.addMessage(input, 'user');
|
|
224
|
+
}
|
|
225
|
+
else if (input && typeof input === 'object') {
|
|
226
|
+
const label = this.extractUserLabel(input);
|
|
227
|
+
if (label) {
|
|
228
|
+
this.overlayManager.addMessage(label, 'user');
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (onSubmit)
|
|
232
|
+
onSubmit(input);
|
|
233
|
+
await this.handleCommand(input);
|
|
234
|
+
}, () => {
|
|
235
|
+
console.log('AssistantService: Overlay closed.');
|
|
236
|
+
if (onClose)
|
|
237
|
+
onClose();
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
extractUserLabel(payload) {
|
|
241
|
+
const label = payload['label'];
|
|
242
|
+
if (typeof label === 'string' && label.trim()) {
|
|
243
|
+
return label.trim();
|
|
244
|
+
}
|
|
245
|
+
const commandId = payload['commandId'];
|
|
246
|
+
if (typeof commandId === 'string' && commandId.trim()) {
|
|
247
|
+
return commandId.trim();
|
|
248
|
+
}
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: AssistantService, deps: [{ token: 'ASSISTANT_CONFIG' }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
252
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: AssistantService, providedIn: 'root' });
|
|
253
|
+
}
|
|
254
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: AssistantService, decorators: [{
|
|
255
|
+
type: Injectable,
|
|
256
|
+
args: [{
|
|
257
|
+
providedIn: 'root',
|
|
258
|
+
}]
|
|
259
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
260
|
+
type: Inject,
|
|
261
|
+
args: ['ASSISTANT_CONFIG']
|
|
262
|
+
}] }] });
|
|
263
|
+
|
|
264
|
+
class AssistantModule {
|
|
265
|
+
static forRoot(config) {
|
|
266
|
+
return {
|
|
267
|
+
ngModule: AssistantModule,
|
|
268
|
+
providers: [
|
|
269
|
+
{ provide: 'ASSISTANT_CONFIG', useValue: config },
|
|
270
|
+
AssistantService,
|
|
271
|
+
],
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: AssistantModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
275
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.0.7", ngImport: i0, type: AssistantModule });
|
|
276
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: AssistantModule });
|
|
277
|
+
}
|
|
278
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: AssistantModule, decorators: [{
|
|
279
|
+
type: NgModule,
|
|
280
|
+
args: [{}]
|
|
281
|
+
}] });
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Generated bundle index. Do not edit.
|
|
285
|
+
*/
|
|
286
|
+
|
|
287
|
+
export { AngularWrapperComponent, AssistantModule, AssistantService };
|
|
288
|
+
//# sourceMappingURL=foisit-angular-wrapper.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foisit-angular-wrapper.mjs","sources":["../../../../libs/angular-wrapper/src/lib/angular-wrapper/angular-wrapper.component.ts","../../../../libs/angular-wrapper/src/lib/angular-wrapper/angular-wrapper.component.html","../../../../libs/angular-wrapper/src/lib/service/assistant.service.ts","../../../../libs/angular-wrapper/src/lib/assistant.module.ts","../../../../libs/angular-wrapper/src/foisit-angular-wrapper.ts"],"sourcesContent":["import { Component } from '@angular/core';\nimport { CommonModule } from '@angular/common';\n\n@Component({\n selector: 'lib-angular-wrapper',\n imports: [CommonModule],\n templateUrl: './angular-wrapper.component.html',\n styleUrl: './angular-wrapper.component.css',\n})\nexport class AngularWrapperComponent {}\n","<p>AngularWrapper works!</p>\n","import { Injectable, Inject } from '@angular/core';\nimport {\n CommandHandler,\n FallbackHandler,\n VoiceProcessor,\n StateManager,\n AssistantConfig,\n TextToSpeech,\n GestureHandler,\n OverlayManager,\n AssistantCommand,\n AssistantCommandParams,\n InteractiveResponse,\n} from '@foisit/core';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class AssistantService {\n private commandHandler: CommandHandler;\n private fallbackHandler: FallbackHandler;\n private voiceProcessor: VoiceProcessor;\n private textToSpeech: TextToSpeech;\n private stateManager: StateManager;\n private lastProcessedInput = '';\n private processingLock = false;\n private isActivated = false; // Tracks activation status\n private gestureHandler: GestureHandler;\n private overlayManager: OverlayManager;\n private defaultIntroMessage = 'How can I help you?';\n\n constructor(@Inject('ASSISTANT_CONFIG') private config: AssistantConfig) {\n // Pass enableSmartIntent (default true) and potential apiKey (if we add it to config later)\n this.commandHandler = new CommandHandler({\n enableSmartIntent: this.config.enableSmartIntent !== false,\n intentEndpoint: this.config.intentEndpoint,\n });\n this.fallbackHandler = new FallbackHandler();\n this.voiceProcessor = new VoiceProcessor();\n this.textToSpeech = new TextToSpeech();\n this.stateManager = new StateManager();\n this.gestureHandler = new GestureHandler();\n this.overlayManager = new OverlayManager({\n floatingButton: this.config.floatingButton,\n inputPlaceholder: this.config.inputPlaceholder,\n });\n\n // Setup commands from config\n this.config.commands.forEach((cmd) => this.commandHandler.addCommand(cmd));\n\n // Setup fallback response\n if (this.config.fallbackResponse) {\n this.fallbackHandler.setFallbackMessage(this.config.fallbackResponse);\n }\n\n // Voice disabled for text-first pivot\n // this.startListening();\n\n // Setup double-tap/double-click listener\n this.gestureHandler.setupDoubleTapListener(() => this.toggle());\n\n // Register global callbacks for floating button\n this.overlayManager.registerCallbacks(\n async (input) => {\n if (typeof input === 'string') {\n this.overlayManager.addMessage(input, 'user');\n } else if (input && typeof input === 'object') {\n const label = this.extractUserLabel(input as Record<string, unknown>);\n if (label) {\n this.overlayManager.addMessage(label, 'user');\n }\n }\n\n await this.handleCommand(input);\n },\n () => console.log('AssistantService: Overlay closed.')\n );\n }\n\n /** Start listening for activation and commands */\n startListening(): void {\n // Voice is currently disabled (text-only mode)\n console.log('AssistantService: Voice is disabled; startListening() is a no-op.');\n return;\n\n /*\n this.voiceProcessor.startListening(async (transcript: string) => {\n if (this.processingLock) return;\n\n const normalizedTranscript = transcript.toLowerCase().trim();\n\n // Skip repeated or irrelevant inputs\n if (\n !normalizedTranscript ||\n normalizedTranscript.length < 3 ||\n normalizedTranscript === this.lastProcessedInput\n ) {\n console.log('AssistantService: Ignoring irrelevant input.');\n return;\n }\n\n this.lastProcessedInput = normalizedTranscript;\n\n if (!this.isActivated) {\n await this.processActivation(normalizedTranscript);\n return;\n }\n\n const isFallbackOrIntroMessage =\n normalizedTranscript === this.config.fallbackResponse?.toLowerCase() ||\n normalizedTranscript === this.config.introMessage?.toLowerCase() ||\n normalizedTranscript === this.defaultIntroMessage.toLowerCase();\n\n if (!isFallbackOrIntroMessage) {\n await this.handleCommand(normalizedTranscript);\n } else {\n console.log('AssistantService: Ignoring fallback or intro message.');\n }\n\n // Throttle input processing to prevent rapid feedback\n this.processingLock = true;\n setTimeout(() => (this.processingLock = false), 1000);\n });\n */\n }\n\n /** Stop listening for voice input */\n stopListening(): void {\n // Voice is currently disabled (text-only mode)\n console.log('AssistantService: Voice is disabled; stopListening() is a no-op.');\n this.isActivated = false;\n }\n\n /** Reset activation state so the next activation flow can occur. */\n reactivate(): void {\n console.log('AssistantService: Reactivating assistant...');\n this.isActivated = false;\n try {\n this.startListening();\n } catch {\n // no-op\n }\n }\n\n /** Process activation command */\n private async processActivation(transcript: string): Promise<void> {\n const activationCmd = this.config.activationCommand?.toLowerCase();\n\n // If no activation command is set, we can't activate via voice\n if (!activationCmd) return;\n\n if (transcript === activationCmd) {\n console.log('AssistantService: Activation matched.');\n this.isActivated = true;\n this.textToSpeech.speak(\n this.config.introMessage || this.defaultIntroMessage\n );\n // this.stateManager.setState('listening'); // DISABLED - no gradient animation\n } else {\n console.log('AssistantService: Activation command not recognized.');\n }\n }\n\n /** Handle recognized commands */\n private async handleCommand(\n input: string | Record<string, unknown>\n ): Promise<void> {\n this.overlayManager.showLoading();\n let response: InteractiveResponse;\n try {\n response = await this.commandHandler.executeCommand(input);\n } finally {\n this.overlayManager.hideLoading();\n }\n\n const originalText = typeof input === 'string' ? input : undefined;\n this.processResponse(response, originalText);\n }\n\n /**\n * Cleanup resources\n */\n destroy(): void {\n this.voiceProcessor.stopListening();\n this.gestureHandler.destroy();\n this.overlayManager.destroy();\n }\n\n /** Unified response processing */\n private processResponse(\n response: InteractiveResponse,\n originalText?: string\n ): void {\n if (response.type === 'error' && originalText) {\n this.fallbackHandler.handleFallback(originalText);\n this.overlayManager.addMessage(this.fallbackHandler.getFallbackMessage(), 'system');\n return;\n }\n\n if (response.type === 'form' && response.fields) {\n this.overlayManager.addForm(\n response.message,\n response.fields,\n async (data: Record<string, unknown>) => {\n this.overlayManager.showLoading();\n let nextReponse: InteractiveResponse;\n try {\n nextReponse = await this.commandHandler.executeCommand(data);\n } finally {\n this.overlayManager.hideLoading();\n }\n this.processResponse(nextReponse);\n }\n );\n return;\n }\n\n if ((response.type === 'ambiguous' || response.type === 'confirm') && response.options) {\n if (response.message) {\n this.overlayManager.addMessage(response.message, 'system');\n }\n this.overlayManager.addOptions(response.options);\n return;\n }\n\n if (response.message) {\n this.overlayManager.addMessage(response.message, 'system');\n }\n }\n\n /** Add a command dynamically (supports string or rich object) */\n addCommand(\n commandOrObj: string | AssistantCommand,\n action?: (\n params?: AssistantCommandParams\n ) => Promise<string | InteractiveResponse | void> | void\n ): void {\n if (typeof commandOrObj === 'string') {\n console.log(`AssistantService: Adding command \"${commandOrObj}\".`);\n } else {\n console.log(\n `AssistantService: Adding rich command \"${commandOrObj.command}\".`\n );\n }\n this.commandHandler.addCommand(commandOrObj, action);\n }\n\n /** Remove a command dynamically */\n removeCommand(command: string): void {\n console.log(`AssistantService: Removing command \"${command}\".`);\n this.commandHandler.removeCommand(command);\n }\n\n /** Get all registered commands */\n getCommands(): string[] {\n return this.commandHandler.getCommands();\n }\n\n /** Toggle the assistant overlay */\n toggle(\n onSubmit?: (input: string | Record<string, unknown>) => void,\n onClose?: () => void\n ): void {\n console.log('AssistantService: Toggling overlay...');\n this.overlayManager.toggle(\n async (input) => {\n if (typeof input === 'string') {\n this.overlayManager.addMessage(input, 'user');\n } else if (input && typeof input === 'object') {\n const label = this.extractUserLabel(input);\n if (label) {\n this.overlayManager.addMessage(label, 'user');\n }\n }\n\n if (onSubmit) onSubmit(input);\n await this.handleCommand(input);\n },\n () => {\n console.log('AssistantService: Overlay closed.');\n if (onClose) onClose();\n }\n );\n }\n\n private extractUserLabel(payload: Record<string, unknown>): string | null {\n const label = payload['label'];\n if (typeof label === 'string' && label.trim()) {\n return label.trim();\n }\n\n const commandId = payload['commandId'];\n if (typeof commandId === 'string' && commandId.trim()) {\n return commandId.trim();\n }\n\n return null;\n }\n}\n","import { ModuleWithProviders, NgModule } from '@angular/core';\nimport { AssistantService } from './service/assistant.service';\nimport { AssistantConfig } from '@foisit/core';\n\n@NgModule({})\nexport class AssistantModule {\n static forRoot(config: AssistantConfig): ModuleWithProviders<AssistantModule> {\n return {\n ngModule: AssistantModule,\n providers: [\n { provide: 'ASSISTANT_CONFIG', useValue: config },\n AssistantService,\n ],\n };\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
package/index.d.ts
ADDED
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foisit/angular-wrapper",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"@angular/common": "^19.0.0",
|
|
6
6
|
"@angular/core": "^19.0.0"
|
|
7
7
|
},
|
|
8
8
|
"sideEffects": false,
|
|
9
9
|
"files": [
|
|
10
|
-
"
|
|
10
|
+
"fesm2022",
|
|
11
|
+
"esm2022",
|
|
12
|
+
"index.d.ts",
|
|
11
13
|
"README.md"
|
|
12
14
|
],
|
|
13
15
|
"description": "Foisit: Speak, and its done. A voice assistant library for angular apps",
|