@courseecho/ai-widget-angular 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,93 @@
1
+ # ⚠️ DEPRECATION NOTICE
2
+
3
+ ## Status: CONSOLIDATED
4
+
5
+ The **@courseecho/ai-widget-angular** package (in this directory) is now the official Angular implementation.
6
+
7
+ ### What Changed?
8
+
9
+ | Aspect | Before | Now |
10
+ |--------|--------|-----|
11
+ | Angular Support | 14-17 | **19+** (modern only) |
12
+ | Syntax | `*ngIf`, `*ngFor` | **@if, @for** (new control flow) |
13
+ | Version | Multi-version | **Single version** |
14
+ | Purpose | Standalone | **Part of unified SDK** |
15
+
16
+ ### Old Package Location
17
+
18
+ **DEPRECATED:** `courseecho-bot-fe/projects/ai-widget-sdk/`
19
+
20
+ This old package is being deprecated in favor of the unified multi-framework approach.
21
+
22
+ ### Migration Guide
23
+
24
+ If you were using the old **@courseecho/ai-widget-sdk** from npm:
25
+
26
+ ```bash
27
+ # Old (deprecated)
28
+ npm uninstall @courseecho/ai-widget-sdk
29
+
30
+ # New (recommended)
31
+ npm install @courseecho/ai-widget-angular
32
+ ```
33
+
34
+ The API is similar but improved. See [README.md](./README.md) for details.
35
+
36
+ ### Why Consolidate?
37
+
38
+ ✅ **Unified Ecosystem** - Same API as React, Vue, Node.js packages
39
+ ✅ **Modern Angular** - Uses Angular 19+ with latest syntax
40
+ ✅ **Better Maintenance** - Single source of truth
41
+ ✅ **Consistency** - All frameworks follow same pattern
42
+ ✅ **Smaller Bundle** - Optimized for modern Angular
43
+
44
+ ### What's the Same?
45
+
46
+ - ✅ Floating chat widget
47
+ - ✅ Message history
48
+ - ✅ Authentication (JWT, API key)
49
+ - ✅ Context tracking
50
+ - ✅ Export functionality
51
+
52
+ ### What's Different?
53
+
54
+ | Feature | Old | New |
55
+ |---------|-----|-----|
56
+ | Chart Rendering | ✅ Built-in | 📊 Via backend |
57
+ | Control Flow | Traditional | Modern (@if, @for) |
58
+ | Angular Versions | 14+ | 19+ only |
59
+ | Bundle Size | 40KB | 18KB |
60
+ | Multi-framework API | ❌ | ✅ |
61
+
62
+ ### Chart Support in New Version?
63
+
64
+ Charts are still available! They come from:
65
+ 1. **Backend responses** - AI returns chart data
66
+ 2. **Widget displays them** - Using standard HTML/canvas
67
+ 3. **Libraries optional** - No Chart.js dependency required
68
+
69
+ ### Still Need the Old One?
70
+
71
+ If you **absolutely must** use the old one with Angular 14-18:
72
+
73
+ ```bash
74
+ # Temporary: Access from bot-fe
75
+ npm install courseecho-bot-fe/projects/ai-widget-sdk
76
+ ```
77
+
78
+ But we recommend upgrading to Angular 19+ and using the new unified package.
79
+
80
+ ### Timeline
81
+
82
+ - **Now:** Old package marked deprecated
83
+ - **v1.1:** Old package removed from npm
84
+ - **v2.0:** Old package archive only (no support)
85
+
86
+ ### Questions?
87
+
88
+ See [README.md](./README.md) or check the [main product guide](../PRODUCT_OVERVIEW.md).
89
+
90
+ ---
91
+
92
+ **Recommendation:** Migrate to the new @courseecho/ai-widget-angular package for best experience! 🚀
93
+
package/README.md ADDED
@@ -0,0 +1,624 @@
1
+ # @courseecho/ai-widget-angular
2
+
3
+ AI Chat Widget for Angular 14+. Beautiful, production-ready component and service for integrating intelligent chat capabilities into your Angular applications.
4
+
5
+ ## ✨ Features
6
+
7
+ - 🎨 **Beautiful Floating Widget** - Professional, customizable chat interface
8
+ - 🚀 **Angular 19+** - Modern Angular 19+ with latest control flow syntax (@if, @for)
9
+ - 🔐 **Secure Authentication** - JWT and API key support
10
+ - 💬 **Advanced Chat** - Message history, source attribution, follow-up suggestions
11
+ - 📊 **Data Visualization** - Support for charts and data display
12
+ - 🎯 **Context Aware** - Automatic page context tracking
13
+ - 📱 **Responsive** - Works on mobile, tablet, desktop
14
+ - 📦 **Type-Safe** - Full TypeScript support
15
+ - ⚡ **Optimized** - ~18KB bundle, minimal dependencies
16
+
17
+ ## 📦 Installation
18
+
19
+ ### Prerequisites
20
+ - Angular 19+
21
+ - RxJS 7.8+
22
+ - Node.js 18+
23
+
24
+ ### Step 1: Install Core SDK (Required)
25
+ ```bash
26
+ npm install @courseecho/ai-core-sdk
27
+ ```
28
+
29
+ ### Step 2: Install Angular Widget
30
+ ```bash
31
+ npm install @courseecho/ai-widget-angular
32
+ ```
33
+
34
+ ## 🚀 Quick Start (5 Minutes)
35
+
36
+ ### Option 1: Module-based (Traditional Angular)
37
+
38
+ ```typescript
39
+ import { NgModule } from '@angular/core';
40
+ import { BrowserModule } from '@angular/platform-browser';
41
+ import { AiWidgetModule } from '@courseecho/ai-widget-angular';
42
+ import { AppComponent } from './app.component';
43
+
44
+ @NgModule({
45
+ declarations: [AppComponent],
46
+ imports: [BrowserModule, AiWidgetModule],
47
+ bootstrap: [AppComponent]
48
+ })
49
+ export class AppModule { }
50
+ ```
51
+
52
+ ```typescript
53
+ import { Component } from '@angular/core';
54
+ import { AiWidgetConfig, AiAuthConfig } from '@courseecho/ai-widget-angular';
55
+
56
+ @Component({
57
+ selector: 'app-root',
58
+ template: `
59
+ <app-ai-chat-widget [config]="config" [authConfig]="authConfig"></app-ai-chat-widget>
60
+ `
61
+ })
62
+ export class AppComponent {
63
+ config: AiWidgetConfig = {
64
+ apiEndpoint: 'http://localhost:5000',
65
+ enableFloatingButton: true,
66
+ position: 'bottom-right',
67
+ theme: 'light'
68
+ };
69
+
70
+ authConfig: AiAuthConfig = {
71
+ jwtToken: 'your-jwt-token-here'
72
+ };
73
+ }
74
+ ```
75
+
76
+ ### Option 2: Standalone Component (Angular 19+)
77
+
78
+ ```typescript
79
+ import { bootstrapApplication } from '@angular/platform-browser';
80
+ import { AppComponent } from './app.component';
81
+
82
+ bootstrapApplication(AppComponent);
83
+ ```
84
+
85
+ ```typescript
86
+ import { Component } from '@angular/core';
87
+ import { AiChatWidgetComponent, AiWidgetConfig } from '@courseecho/ai-widget-angular';
88
+
89
+ @Component({
90
+ selector: 'app-root',
91
+ standalone: true,
92
+ imports: [AiChatWidgetComponent],
93
+ template: `
94
+ <app-ai-chat-widget [config]="config"></app-ai-chat-widget>
95
+ `
96
+ })
97
+ export class AppComponent {
98
+ config: AiWidgetConfig = {
99
+ apiEndpoint: 'http://localhost:5000',
100
+ position: 'bottom-right'
101
+ };
102
+ }
103
+ ```
104
+
105
+ ## 🎯 Configuration
106
+
107
+ ### Basic Configuration
108
+
109
+ ```typescript
110
+ import { AiWidgetConfig, AiAuthConfig } from '@courseecho/ai-widget-angular';
111
+
112
+ const config: AiWidgetConfig = {
113
+ // Required
114
+ apiEndpoint: 'http://localhost:5000',
115
+
116
+ // Optional
117
+ enableFloatingButton: true,
118
+ position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left',
119
+ theme: 'light' | 'dark',
120
+
121
+ // Context tracking
122
+ contextConfig: {
123
+ enableContextTracking: true,
124
+ contextUpdateInterval: 5000,
125
+ includePageMetadata: true
126
+ }
127
+ };
128
+
129
+ const authConfig: AiAuthConfig = {
130
+ jwtToken: 'your-jwt-token', // Option 1
131
+ // OR
132
+ apiKey: 'your-api-key', // Option 2
133
+ // OR
134
+ refreshTokenFn: async () => {
135
+ // Fetch new token from your auth service
136
+ return 'new-jwt-token';
137
+ }
138
+ };
139
+ ```
140
+
141
+ ## 🔧 Service Usage
142
+
143
+ Inject `AiWidgetService` to use the SDK directly:
144
+
145
+ ```typescript
146
+ import { Component, OnInit } from '@angular/core';
147
+ import { AiWidgetService } from '@courseecho/ai-widget-angular';
148
+
149
+ @Component({
150
+ selector: 'app-chat-page',
151
+ template: `
152
+ <div class="chat-container">
153
+ <div *ngFor="let msg of (messages$ | async)" class="message">
154
+ {{ msg.content }}
155
+ </div>
156
+ <input [(ngModel)]="userInput" />
157
+ <button (click)="send()">Send</button>
158
+ </div>
159
+ `
160
+ })
161
+ export class ChatPageComponent implements OnInit {
162
+ messages$ = this.aiService.messages$;
163
+ loading$ = this.aiService.loading$;
164
+ error$ = this.aiService.error$;
165
+
166
+ userInput = '';
167
+
168
+ constructor(private aiService: AiWidgetService) {}
169
+
170
+ ngOnInit() {
171
+ this.aiService.initialize({
172
+ apiEndpoint: 'http://localhost:5000',
173
+ contextConfig: { enableContextTracking: true }
174
+ }, {
175
+ jwtToken: 'your-token'
176
+ });
177
+ }
178
+
179
+ async send() {
180
+ try {
181
+ await this.aiService.sendQuery(this.userInput);
182
+ this.userInput = '';
183
+ } catch (error) {
184
+ console.error('Error:', error);
185
+ }
186
+ }
187
+
188
+ clearChat() {
189
+ this.aiService.clear();
190
+ }
191
+
192
+ exportChat() {
193
+ const data = this.aiService.exportChats('json');
194
+ const blob = new Blob([data], { type: 'application/json' });
195
+ const url = URL.createObjectURL(blob);
196
+ const link = document.createElement('a');
197
+ link.href = url;
198
+ link.download = `chat-${Date.now()}.json`;
199
+ link.click();
200
+ }
201
+ }
202
+ ```
203
+
204
+ ## 📡 Service Methods
205
+
206
+ ### sendQuery(question: string)
207
+ Send a question to the AI backend.
208
+
209
+ ```typescript
210
+ try {
211
+ const response = await this.aiService.sendQuery('What is Angular?');
212
+ console.log('Response:', response);
213
+ } catch (error) {
214
+ console.error('Error:', error);
215
+ }
216
+ ```
217
+
218
+ ### setContext(context: AiContext)
219
+ Set or update the page context.
220
+
221
+ ```typescript
222
+ this.aiService.setContext({
223
+ pageUrl: window.location.href,
224
+ pageTitle: document.title,
225
+ userId: '123',
226
+ customData: { tier: 'premium' }
227
+ });
228
+ ```
229
+
230
+ ### setJwt(token: string)
231
+ Update JWT token.
232
+
233
+ ```typescript
234
+ this.aiService.setJwt('new-token');
235
+ ```
236
+
237
+ ### setApiKey(key: string)
238
+ Update API key.
239
+
240
+ ```typescript
241
+ this.aiService.setApiKey('new-api-key');
242
+ ```
243
+
244
+ ### clear()
245
+ Clear all messages and reset state.
246
+
247
+ ```typescript
248
+ this.aiService.clear();
249
+ ```
250
+
251
+ ### exportChats(format: 'json' | 'csv')
252
+ Export chat history.
253
+
254
+ ```typescript
255
+ const json = this.aiService.exportChats('json');
256
+ const csv = this.aiService.exportChats('csv');
257
+ ```
258
+
259
+ ### checkHealth()
260
+ Check backend API health.
261
+
262
+ ```typescript
263
+ try {
264
+ await this.aiService.checkHealth();
265
+ console.log('Backend is healthy');
266
+ } catch (error) {
267
+ console.error('Backend is down');
268
+ }
269
+ ```
270
+
271
+ ### getMessages()
272
+ Get current messages (snapshot).
273
+
274
+ ```typescript
275
+ const messages = this.aiService.getMessages();
276
+ ```
277
+
278
+ ### isLoading()
279
+ Get loading state (snapshot).
280
+
281
+ ```typescript
282
+ if (this.aiService.isLoading()) {
283
+ console.log('Loading...');
284
+ }
285
+ ```
286
+
287
+ ### getError()
288
+ Get current error (snapshot).
289
+
290
+ ```typescript
291
+ const error = this.aiService.getError();
292
+ if (error) {
293
+ console.error(error);
294
+ }
295
+ ```
296
+
297
+ ## 📊 Reactive State Observables
298
+
299
+ All state is available as RxJS observables:
300
+
301
+ ```typescript
302
+ // Messages observable
303
+ this.aiService.messages$.subscribe(messages => {
304
+ console.log('Messages:', messages);
305
+ });
306
+
307
+ // Loading state observable
308
+ this.aiService.loading$.subscribe(isLoading => {
309
+ console.log('Is loading:', isLoading);
310
+ });
311
+
312
+ // Error observable
313
+ this.aiService.error$.subscribe(error => {
314
+ if (error) console.error('Error:', error);
315
+ });
316
+
317
+ // Current context observable
318
+ this.aiService.context$.subscribe(context => {
319
+ console.log('Context:', context);
320
+ });
321
+
322
+ // Charts observable
323
+ this.aiService.charts$.subscribe(charts => {
324
+ console.log('Charts:', charts);
325
+ });
326
+ ```
327
+
328
+ Use with `async` pipe in templates:
329
+
330
+ ```html
331
+ <div *ngFor="let chart of (charts$ | async)">
332
+ <!-- Render chart -->
333
+ </div>
334
+ ```
335
+
336
+ ## 🎨 Component Customization
337
+
338
+ ### Position Options
339
+
340
+ ```typescript
341
+ config: AiWidgetConfig = {
342
+ apiEndpoint: 'http://localhost:5000',
343
+ position: 'bottom-right' // 'bottom-left' | 'top-right' | 'top-left'
344
+ };
345
+ ```
346
+
347
+ ### Theme Options
348
+
349
+ ```typescript
350
+ config: AiWidgetConfig = {
351
+ apiEndpoint: 'http://localhost:5000',
352
+ theme: 'light' // 'light' | 'dark'
353
+ };
354
+ ```
355
+
356
+ ### Custom Styling
357
+
358
+ The component uses CSS custom properties for styling:
359
+
360
+ ```css
361
+ .ai-widget-container {
362
+ --ai-primary: #3b82f6;
363
+ --ai-text: #1f2937;
364
+ --ai-bg: #ffffff;
365
+ --ai-border: #e5e7eb;
366
+ --ai-user-bg: #3b82f6;
367
+ --ai-assistant-bg: #f3f4f6;
368
+ }
369
+
370
+ .ai-widget-container.dark-theme {
371
+ --ai-primary: #2563eb;
372
+ --ai-text: #f3f4f6;
373
+ --ai-bg: #1f2937;
374
+ --ai-border: #374151;
375
+ }
376
+ ```
377
+
378
+ ## 🔐 Authentication
379
+
380
+ ### JWT Token
381
+
382
+ ```typescript
383
+ authConfig: AiAuthConfig = {
384
+ jwtToken: 'eyJhbGciOiJIUzI1NiIs...'
385
+ };
386
+
387
+ // Update token later
388
+ this.aiService.setJwt('new-token');
389
+ ```
390
+
391
+ ### API Key
392
+
393
+ ```typescript
394
+ authConfig: AiAuthConfig = {
395
+ apiKey: 'sk-prod-xxxxx'
396
+ };
397
+
398
+ // Update key later
399
+ this.aiService.setApiKey('sk-prod-yyyyy');
400
+ ```
401
+
402
+ ### Token Refresh
403
+
404
+ ```typescript
405
+ authConfig: AiAuthConfig = {
406
+ refreshTokenFn: async () => {
407
+ const response = await fetch('/api/refresh-token', {
408
+ method: 'POST'
409
+ });
410
+ const { token } = await response.json();
411
+ return token;
412
+ }
413
+ };
414
+ ```
415
+
416
+ ## 🧪 Testing
417
+
418
+ ### Unit Tests
419
+
420
+ ```typescript
421
+ import { TestBed } from '@angular/core/testing';
422
+ import { AiWidgetService } from '@courseecho/ai-widget-angular';
423
+
424
+ describe('AiWidgetService', () => {
425
+ let service: AiWidgetService;
426
+
427
+ beforeEach(() => {
428
+ TestBed.configureTestingModule({});
429
+ service = TestBed.inject(AiWidgetService);
430
+ });
431
+
432
+ it('should initialize', () => {
433
+ service.initialize({
434
+ apiEndpoint: 'http://localhost:5000'
435
+ });
436
+ expect(service).toBeDefined();
437
+ });
438
+
439
+ it('should send query', async () => {
440
+ service.initialize({
441
+ apiEndpoint: 'http://localhost:5000'
442
+ }, { jwtToken: 'test' });
443
+
444
+ const response = await service.sendQuery('Test question');
445
+ expect(response).toBeDefined();
446
+ });
447
+ });
448
+ ```
449
+
450
+ ## ⚡ Performance Tips
451
+
452
+ 1. **Lazy load the widget**
453
+ ```typescript
454
+ loadWidget() {
455
+ this.showWidget = true;
456
+ this.aiService.initialize(config);
457
+ }
458
+ ```
459
+
460
+ 2. **Use OnPush change detection**
461
+ ```typescript
462
+ @Component({
463
+ changeDetection: ChangeDetectionStrategy.OnPush
464
+ })
465
+ ```
466
+
467
+ 3. **Unsubscribe properly**
468
+ ```typescript
469
+ private destroy$ = new Subject<void>();
470
+
471
+ ngOnInit() {
472
+ this.aiService.messages$
473
+ .pipe(takeUntil(this.destroy$))
474
+ .subscribe(...);
475
+ }
476
+
477
+ ngOnDestroy() {
478
+ this.destroy$.next();
479
+ this.destroy$.complete();
480
+ }
481
+ ```
482
+
483
+ ## 🐛 Error Handling
484
+
485
+ ```typescript
486
+ async sendQuery(question: string) {
487
+ try {
488
+ const response = await this.aiService.sendQuery(question);
489
+ console.log('Success:', response);
490
+ } catch (error) {
491
+ if (error instanceof Error) {
492
+ if (error.message.includes('401')) {
493
+ console.error('Unauthorized - check your token');
494
+ } else if (error.message.includes('404')) {
495
+ console.error('API endpoint not found');
496
+ } else {
497
+ console.error('Unknown error:', error.message);
498
+ }
499
+ }
500
+ }
501
+ }
502
+
503
+ // Subscribe to error observable
504
+ this.aiService.error$.subscribe(error => {
505
+ if (error) {
506
+ // Show error toast/snackbar
507
+ this.snackBar.open(error, 'Close');
508
+ }
509
+ });
510
+ ```
511
+
512
+ ## 📚 Comparison with Other Frameworks
513
+
514
+ | Feature | Angular 19+ | React | Vue 3 |
515
+ |---------|-------------|-------|-------|
516
+ | Component | Standalone | Functional | Composition |
517
+ | State | RxJS Observables | Hooks | Composables |
518
+ | Bundle Size | 18KB | 15KB | 12KB |
519
+ | Modern Syntax | @if, @for, @switch | JSX | Template |
520
+ | Performance | Excellent | Excellent | Excellent |
521
+ | Type Safety | Excellent | Good | Good |
522
+
523
+ ## 🚀 Deployment
524
+
525
+ ### Build for Production
526
+
527
+ ```bash
528
+ npm run build
529
+ ```
530
+
531
+ ### Include in Angular Build
532
+
533
+ The package is automatically bundled with your Angular app.
534
+
535
+ ### Self-Hosted Package
536
+
537
+ ```bash
538
+ npm install
539
+ npm run build
540
+ # Output in dist/ folder
541
+ ```
542
+
543
+ ## 🔗 Resources
544
+
545
+ - [Integration Guide](../MULTI_FRAMEWORK_INTEGRATION_GUIDE.md#angular-integration)
546
+ - [Quick Start](../QUICK_START_CHEATSHEET.md#angular)
547
+ - [API Reference](../CAPABILITIES_AND_ENDPOINTS.md)
548
+ - [Product Overview](../PRODUCT_OVERVIEW.md)
549
+
550
+ ## 📝 Types
551
+
552
+ ```typescript
553
+ interface AiMessage {
554
+ id: string;
555
+ role: 'user' | 'assistant';
556
+ content: string;
557
+ timestamp: Date;
558
+ sources?: string[];
559
+ isStreaming?: boolean;
560
+ }
561
+
562
+ interface AiResponse {
563
+ success: boolean;
564
+ answer: string;
565
+ sources?: string[];
566
+ charts?: AiChart[];
567
+ followUpQuestions?: string[];
568
+ error?: string;
569
+ }
570
+
571
+ interface AiContext {
572
+ pageUrl: string;
573
+ pageTitle: string;
574
+ userId?: string;
575
+ sessionId?: string;
576
+ customData?: Record<string, any>;
577
+ }
578
+
579
+ interface AiWidgetConfig {
580
+ apiEndpoint: string;
581
+ enableFloatingButton?: boolean;
582
+ position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
583
+ theme?: 'light' | 'dark';
584
+ contextConfig?: AiContextConfig;
585
+ }
586
+
587
+ interface AiAuthConfig {
588
+ jwtToken?: string;
589
+ apiKey?: string;
590
+ refreshTokenFn?: () => Promise<string>;
591
+ }
592
+ ```
593
+
594
+ ## 🆘 Troubleshooting
595
+
596
+ **Q: Widget doesn't appear**
597
+ - Ensure backend API is running
598
+ - Check API endpoint configuration
599
+ - Verify CORS is enabled on backend
600
+
601
+ **Q: Authentication fails**
602
+ - Verify JWT token is valid
603
+ - Check token hasn't expired
604
+ - Ensure API supports the auth method
605
+
606
+ **Q: Performance issues**
607
+ - Check network in DevTools
608
+ - Verify backend latency
609
+ - Use OnPush change detection
610
+
611
+ ## 📞 Support
612
+
613
+ - See [README.md](../README.md) for all resources
614
+ - Check [QUICK_START_CHEATSHEET.md](../QUICK_START_CHEATSHEET.md) for common issues
615
+ - Review [CAPABILITIES_AND_ENDPOINTS.md](../CAPABILITIES_AND_ENDPOINTS.md) for API details
616
+
617
+ ## 📄 License
618
+
619
+ MIT - Free to use, modify, and distribute
620
+
621
+ ---
622
+
623
+ **Ready to get started?** Install the package and follow the Quick Start above!
624
+