@courseecho/ai-widget-react 1.0.23 → 1.0.24

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,118 @@
1
+ /**
2
+ * Page Content Scanner and AI Suggestion Generator
3
+ * Automatically extracts page content and generates AI-powered suggestions
4
+ */
5
+ import { AiSuggestion, PageScanConfig, AiSuggestionGeneratorConfig } from './models';
6
+ export interface PageContent {
7
+ url: string;
8
+ title: string;
9
+ description: string;
10
+ headings: string[];
11
+ keywords: string[];
12
+ pageType: string;
13
+ rawText: string;
14
+ }
15
+ /**
16
+ * Scans page content and extracts relevant information
17
+ */
18
+ export declare class PageContentScanner {
19
+ private config;
20
+ private cachedContent;
21
+ constructor(config?: PageScanConfig);
22
+ /**
23
+ * Scan page content
24
+ */
25
+ scanPage(): PageContent;
26
+ /**
27
+ * Get cached content if still valid
28
+ */
29
+ private getCachedContent;
30
+ /**
31
+ * Cache content
32
+ */
33
+ private cacheContent;
34
+ /**
35
+ * Extract page title
36
+ */
37
+ private extractTitle;
38
+ /**
39
+ * Extract meta description
40
+ */
41
+ private extractDescription;
42
+ /**
43
+ * Extract all headings
44
+ */
45
+ private extractHeadings;
46
+ /**
47
+ * Extract keywords from meta tags
48
+ */
49
+ private extractKeywords;
50
+ /**
51
+ * Detect page type (course, product, blog, etc.)
52
+ */
53
+ private detectPageType;
54
+ /**
55
+ * Extract main text content
56
+ */
57
+ private extractMainContent;
58
+ /**
59
+ * Clear cache
60
+ */
61
+ clearCache(): void;
62
+ /**
63
+ * Update config
64
+ */
65
+ updateConfig(config: Partial<PageScanConfig>): void;
66
+ }
67
+ /**
68
+ * Generates AI-powered suggestions based on page content
69
+ */
70
+ export declare class AiSuggestionGenerator {
71
+ private config;
72
+ private pageScanner;
73
+ constructor(config?: AiSuggestionGeneratorConfig, pageScanner?: PageContentScanner);
74
+ /**
75
+ * Generate suggestions based on page content
76
+ */
77
+ generateSuggestions(): Promise<AiSuggestion[]>;
78
+ /**
79
+ * Free tier: Generate suggestions locally based on heuristics
80
+ */
81
+ private generateFreeSuggestions;
82
+ /**
83
+ * Premium tier: Use backend AI for smart suggestions
84
+ */
85
+ private generatePremiumSuggestions;
86
+ /**
87
+ * Course page suggestions
88
+ */
89
+ private generateCourseSuggestions;
90
+ /**
91
+ * Product page suggestions
92
+ */
93
+ private generateProductSuggestions;
94
+ /**
95
+ * Blog page suggestions
96
+ */
97
+ private generateBlogSuggestions;
98
+ /**
99
+ * Support page suggestions
100
+ */
101
+ private generateSupportSuggestions;
102
+ /**
103
+ * Documentation page suggestions
104
+ */
105
+ private generateDocsSuggestions;
106
+ /**
107
+ * Generic page suggestions
108
+ */
109
+ private generateGenericSuggestions;
110
+ /**
111
+ * Update config
112
+ */
113
+ updateConfig(config: Partial<AiSuggestionGeneratorConfig>): void;
114
+ /**
115
+ * Get current tier
116
+ */
117
+ getTier(): string;
118
+ }
@@ -0,0 +1,538 @@
1
+ /**
2
+ * Page Content Scanner and AI Suggestion Generator
3
+ * Automatically extracts page content and generates AI-powered suggestions
4
+ */
5
+ /**
6
+ * Scans page content and extracts relevant information
7
+ */
8
+ export class PageContentScanner {
9
+ constructor(config) {
10
+ this.cachedContent = new Map();
11
+ this.config = {
12
+ enabled: true,
13
+ autoScan: true,
14
+ maxTextLength: 5000,
15
+ cacheDuration: 5 * 60 * 1000, // 5 minutes
16
+ mergeWithUserSuggestions: true,
17
+ ...config,
18
+ };
19
+ }
20
+ /**
21
+ * Scan page content
22
+ */
23
+ scanPage() {
24
+ const url = window.location.href;
25
+ // Check cache first
26
+ const cached = this.getCachedContent(url);
27
+ if (cached) {
28
+ return cached;
29
+ }
30
+ const content = {
31
+ url,
32
+ title: this.extractTitle(),
33
+ description: this.extractDescription(),
34
+ headings: this.extractHeadings(),
35
+ keywords: this.extractKeywords(),
36
+ pageType: this.detectPageType(),
37
+ rawText: this.extractMainContent(),
38
+ };
39
+ // Cache the content
40
+ this.cacheContent(url, content);
41
+ return content;
42
+ }
43
+ /**
44
+ * Get cached content if still valid
45
+ */
46
+ getCachedContent(url) {
47
+ const cached = this.cachedContent.get(url);
48
+ if (!cached) {
49
+ return null;
50
+ }
51
+ if (this.config.cacheDuration && Date.now() - cached.timestamp > this.config.cacheDuration) {
52
+ this.cachedContent.delete(url);
53
+ return null;
54
+ }
55
+ return cached.content;
56
+ }
57
+ /**
58
+ * Cache content
59
+ */
60
+ cacheContent(url, content) {
61
+ this.cachedContent.set(url, {
62
+ content,
63
+ timestamp: Date.now(),
64
+ });
65
+ }
66
+ /**
67
+ * Extract page title
68
+ */
69
+ extractTitle() {
70
+ const titleTag = document.querySelector('title');
71
+ if (titleTag) {
72
+ return titleTag.textContent || '';
73
+ }
74
+ const h1 = document.querySelector('h1');
75
+ if (h1) {
76
+ return h1.textContent || '';
77
+ }
78
+ return document.title || 'Untitled Page';
79
+ }
80
+ /**
81
+ * Extract meta description
82
+ */
83
+ extractDescription() {
84
+ const metaDesc = document.querySelector('meta[name="description"]');
85
+ if (metaDesc) {
86
+ return metaDesc.getAttribute('content') || '';
87
+ }
88
+ const metaOgDesc = document.querySelector('meta[property="og:description"]');
89
+ if (metaOgDesc) {
90
+ return metaOgDesc.getAttribute('content') || '';
91
+ }
92
+ // Fallback: use first paragraph
93
+ const firstP = document.querySelector('p');
94
+ if (firstP) {
95
+ return firstP.textContent?.slice(0, 160) || '';
96
+ }
97
+ return '';
98
+ }
99
+ /**
100
+ * Extract all headings
101
+ */
102
+ extractHeadings() {
103
+ const headings = Array.from(document.querySelectorAll('h1, h2, h3, h4, h5, h6'));
104
+ return headings
105
+ .map((h) => h.textContent || '')
106
+ .filter((text) => text.length > 0)
107
+ .slice(0, 20); // Limit to 20 headings
108
+ }
109
+ /**
110
+ * Extract keywords from meta tags
111
+ */
112
+ extractKeywords() {
113
+ const keywordsMeta = document.querySelector('meta[name="keywords"]');
114
+ if (keywordsMeta) {
115
+ const content = keywordsMeta.getAttribute('content') || '';
116
+ return content.split(',').map((k) => k.trim());
117
+ }
118
+ // Generate keywords from headings and title
119
+ const headings = this.extractHeadings();
120
+ return headings.slice(0, 10);
121
+ }
122
+ /**
123
+ * Detect page type (course, product, blog, etc.)
124
+ */
125
+ detectPageType() {
126
+ const url = window.location.pathname.toLowerCase();
127
+ const content = document.body.innerText.toLowerCase();
128
+ if (url.includes('course') || content.includes('learning objectives')) {
129
+ return 'course';
130
+ }
131
+ if (url.includes('product') || url.includes('shop')) {
132
+ return 'product';
133
+ }
134
+ if (url.includes('blog') || url.includes('article')) {
135
+ return 'blog';
136
+ }
137
+ if (url.includes('support') || url.includes('help') || url.includes('faq')) {
138
+ return 'support';
139
+ }
140
+ if (url.includes('docs') || url.includes('documentation')) {
141
+ return 'documentation';
142
+ }
143
+ return 'generic';
144
+ }
145
+ /**
146
+ * Extract main text content
147
+ */
148
+ extractMainContent() {
149
+ // Remove script, style, and excluded elements
150
+ const clone = document.body.cloneNode(true);
151
+ // Remove unwanted elements
152
+ const toRemove = clone.querySelectorAll('script, style, nav, footer, .ads, .sidebar');
153
+ toRemove.forEach((el) => el.remove());
154
+ // Apply custom selectors if provided
155
+ if (this.config.excludeSelectors) {
156
+ this.config.excludeSelectors.forEach((selector) => {
157
+ clone.querySelectorAll(selector).forEach((el) => el.remove());
158
+ });
159
+ }
160
+ let text = clone.innerText || clone.textContent || '';
161
+ // Clean up whitespace
162
+ text = text
163
+ .split('\n')
164
+ .map((line) => line.trim())
165
+ .filter((line) => line.length > 0)
166
+ .join(' ');
167
+ // Truncate to max length
168
+ if (this.config.maxTextLength && text.length > this.config.maxTextLength) {
169
+ text = text.slice(0, this.config.maxTextLength) + '...';
170
+ }
171
+ return text;
172
+ }
173
+ /**
174
+ * Clear cache
175
+ */
176
+ clearCache() {
177
+ this.cachedContent.clear();
178
+ }
179
+ /**
180
+ * Update config
181
+ */
182
+ updateConfig(config) {
183
+ this.config = { ...this.config, ...config };
184
+ }
185
+ }
186
+ /**
187
+ * Generates AI-powered suggestions based on page content
188
+ */
189
+ export class AiSuggestionGenerator {
190
+ constructor(config, pageScanner) {
191
+ this.config = {
192
+ enabled: true,
193
+ tier: 'free',
194
+ timeoutMs: 5000,
195
+ ...config,
196
+ };
197
+ this.pageScanner = pageScanner || new PageContentScanner();
198
+ }
199
+ /**
200
+ * Generate suggestions based on page content
201
+ */
202
+ async generateSuggestions() {
203
+ if (!this.config.enabled) {
204
+ return [];
205
+ }
206
+ // Check tier (free gets limited suggestions)
207
+ if (this.config.tier === 'free') {
208
+ return this.generateFreeSuggestions();
209
+ }
210
+ // Premium tier: use backend AI
211
+ return this.generatePremiumSuggestions();
212
+ }
213
+ /**
214
+ * Free tier: Generate suggestions locally based on heuristics
215
+ */
216
+ generateFreeSuggestions() {
217
+ const pageContent = this.pageScanner.scanPage();
218
+ const suggestions = [];
219
+ // Generate suggestions based on page type
220
+ switch (pageContent.pageType) {
221
+ case 'course':
222
+ suggestions.push(...this.generateCourseSuggestions(pageContent));
223
+ break;
224
+ case 'product':
225
+ suggestions.push(...this.generateProductSuggestions(pageContent));
226
+ break;
227
+ case 'blog':
228
+ suggestions.push(...this.generateBlogSuggestions(pageContent));
229
+ break;
230
+ case 'support':
231
+ suggestions.push(...this.generateSupportSuggestions(pageContent));
232
+ break;
233
+ case 'documentation':
234
+ suggestions.push(...this.generateDocsSuggestions(pageContent));
235
+ break;
236
+ default:
237
+ suggestions.push(...this.generateGenericSuggestions(pageContent));
238
+ }
239
+ return suggestions;
240
+ }
241
+ /**
242
+ * Premium tier: Use backend AI for smart suggestions
243
+ */
244
+ async generatePremiumSuggestions() {
245
+ if (!this.config.backendUrl) {
246
+ console.warn('Backend URL not configured. Falling back to free tier.');
247
+ return this.generateFreeSuggestions();
248
+ }
249
+ try {
250
+ const pageContent = this.pageScanner.scanPage();
251
+ const response = await fetch(`${this.config.backendUrl}/api/suggestions/generate`, {
252
+ method: 'POST',
253
+ headers: {
254
+ 'Content-Type': 'application/json',
255
+ ...(this.config.apiKey && { 'X-API-Key': this.config.apiKey }),
256
+ },
257
+ body: JSON.stringify({
258
+ pageContent,
259
+ tier: this.config.tier,
260
+ }),
261
+ signal: AbortSignal.timeout(this.config.timeoutMs || 5000),
262
+ });
263
+ if (!response.ok) {
264
+ throw new Error(`API error: ${response.status}`);
265
+ }
266
+ return await response.json();
267
+ }
268
+ catch (error) {
269
+ console.error('Error generating premium suggestions:', error);
270
+ // Fallback to free tier
271
+ return this.generateFreeSuggestions();
272
+ }
273
+ }
274
+ /**
275
+ * Course page suggestions
276
+ */
277
+ generateCourseSuggestions(pageContent) {
278
+ return [
279
+ {
280
+ id: `auto-course-1-${Date.now()}`,
281
+ text: 'What are the learning objectives?',
282
+ icon: '📚',
283
+ category: 'Course',
284
+ description: 'Understand course goals and objectives',
285
+ },
286
+ {
287
+ id: `auto-course-2-${Date.now()}`,
288
+ text: 'How do I submit assignments?',
289
+ icon: '📝',
290
+ category: 'Assignments',
291
+ description: 'Learn how to complete and submit work',
292
+ },
293
+ {
294
+ id: `auto-course-3-${Date.now()}`,
295
+ text: 'What resources are available?',
296
+ icon: '🔍',
297
+ category: 'Resources',
298
+ description: 'Find course materials and references',
299
+ },
300
+ {
301
+ id: `auto-course-4-${Date.now()}`,
302
+ text: 'How is this course graded?',
303
+ icon: '📊',
304
+ category: 'Grading',
305
+ description: 'Review grading criteria and rubrics',
306
+ },
307
+ {
308
+ id: `auto-course-5-${Date.now()}`,
309
+ text: 'Who is the instructor?',
310
+ icon: '👨‍🏫',
311
+ category: 'Instructor',
312
+ description: 'Learn about the course instructor',
313
+ },
314
+ ];
315
+ }
316
+ /**
317
+ * Product page suggestions
318
+ */
319
+ generateProductSuggestions(pageContent) {
320
+ return [
321
+ {
322
+ id: `auto-product-1-${Date.now()}`,
323
+ text: 'What are the product specifications?',
324
+ icon: '⚙️',
325
+ category: 'Details',
326
+ description: 'View detailed product specs',
327
+ },
328
+ {
329
+ id: `auto-product-2-${Date.now()}`,
330
+ text: 'How much does this cost?',
331
+ icon: '💰',
332
+ category: 'Pricing',
333
+ description: 'See pricing and available plans',
334
+ },
335
+ {
336
+ id: `auto-product-3-${Date.now()}`,
337
+ text: 'What shipping options are available?',
338
+ icon: '🚚',
339
+ category: 'Shipping',
340
+ description: 'Check delivery options',
341
+ },
342
+ {
343
+ id: `auto-product-4-${Date.now()}`,
344
+ text: 'What is your return policy?',
345
+ icon: '↩️',
346
+ category: 'Returns',
347
+ description: 'Learn about returns and refunds',
348
+ },
349
+ {
350
+ id: `auto-product-5-${Date.now()}`,
351
+ text: 'Do you have customer reviews?',
352
+ icon: '⭐',
353
+ category: 'Reviews',
354
+ description: 'See what customers think',
355
+ },
356
+ ];
357
+ }
358
+ /**
359
+ * Blog page suggestions
360
+ */
361
+ generateBlogSuggestions(pageContent) {
362
+ return [
363
+ {
364
+ id: `auto-blog-1-${Date.now()}`,
365
+ text: 'What is this article about?',
366
+ icon: '📖',
367
+ category: 'Content',
368
+ description: 'Get summary of the article',
369
+ },
370
+ {
371
+ id: `auto-blog-2-${Date.now()}`,
372
+ text: 'Can you expand on this topic?',
373
+ icon: '💭',
374
+ category: 'Discussion',
375
+ description: 'Dive deeper into the subject',
376
+ },
377
+ {
378
+ id: `auto-blog-3-${Date.now()}`,
379
+ text: 'Related articles and resources?',
380
+ icon: '🔗',
381
+ category: 'Links',
382
+ description: 'Find related content',
383
+ },
384
+ {
385
+ id: `auto-blog-4-${Date.now()}`,
386
+ text: 'Who is the author?',
387
+ icon: '✍️',
388
+ category: 'Author',
389
+ description: 'Learn about the writer',
390
+ },
391
+ {
392
+ id: `auto-blog-5-${Date.now()}`,
393
+ text: 'When was this published?',
394
+ icon: '📅',
395
+ category: 'Date',
396
+ description: 'Check publication date',
397
+ },
398
+ ];
399
+ }
400
+ /**
401
+ * Support page suggestions
402
+ */
403
+ generateSupportSuggestions(pageContent) {
404
+ return [
405
+ {
406
+ id: `auto-support-1-${Date.now()}`,
407
+ text: 'How do I reset my password?',
408
+ icon: '🔐',
409
+ category: 'Account',
410
+ description: 'Password recovery help',
411
+ },
412
+ {
413
+ id: `auto-support-2-${Date.now()}`,
414
+ text: 'How do I contact support?',
415
+ icon: '📞',
416
+ category: 'Support',
417
+ description: 'Get in touch with support',
418
+ },
419
+ {
420
+ id: `auto-support-3-${Date.now()}`,
421
+ text: 'What are my billing options?',
422
+ icon: '💳',
423
+ category: 'Billing',
424
+ description: 'Payment information',
425
+ },
426
+ {
427
+ id: `auto-support-4-${Date.now()}`,
428
+ text: 'How do I cancel my subscription?',
429
+ icon: '❌',
430
+ category: 'Subscription',
431
+ description: 'Manage subscription',
432
+ },
433
+ {
434
+ id: `auto-support-5-${Date.now()}`,
435
+ text: 'What are my rights?',
436
+ icon: '⚖️',
437
+ category: 'Legal',
438
+ description: 'View terms and conditions',
439
+ },
440
+ ];
441
+ }
442
+ /**
443
+ * Documentation page suggestions
444
+ */
445
+ generateDocsSuggestions(pageContent) {
446
+ return [
447
+ {
448
+ id: `auto-docs-1-${Date.now()}`,
449
+ text: 'How do I get started?',
450
+ icon: '🚀',
451
+ category: 'Getting Started',
452
+ description: 'Quick start guide',
453
+ },
454
+ {
455
+ id: `auto-docs-2-${Date.now()}`,
456
+ text: 'What are the system requirements?',
457
+ icon: '⚙️',
458
+ category: 'Requirements',
459
+ description: 'Technical specifications',
460
+ },
461
+ {
462
+ id: `auto-docs-3-${Date.now()}`,
463
+ text: 'Do you have code examples?',
464
+ icon: '💻',
465
+ category: 'Examples',
466
+ description: 'View code samples',
467
+ },
468
+ {
469
+ id: `auto-docs-4-${Date.now()}`,
470
+ text: 'How is this documented?',
471
+ icon: '📚',
472
+ category: 'Docs',
473
+ description: 'Browse all documentation',
474
+ },
475
+ {
476
+ id: `auto-docs-5-${Date.now()}`,
477
+ text: 'How do I report issues?',
478
+ icon: '🐛',
479
+ category: 'Issues',
480
+ description: 'Report bugs and issues',
481
+ },
482
+ ];
483
+ }
484
+ /**
485
+ * Generic page suggestions
486
+ */
487
+ generateGenericSuggestions(pageContent) {
488
+ return [
489
+ {
490
+ id: `auto-generic-1-${Date.now()}`,
491
+ text: 'Tell me more about this topic',
492
+ icon: '❓',
493
+ category: 'General',
494
+ description: 'Get more information',
495
+ },
496
+ {
497
+ id: `auto-generic-2-${Date.now()}`,
498
+ text: 'What are the key points?',
499
+ icon: '📍',
500
+ category: 'Summary',
501
+ description: 'See main highlights',
502
+ },
503
+ {
504
+ id: `auto-generic-3-${Date.now()}`,
505
+ text: 'How can I use this?',
506
+ icon: '💡',
507
+ category: 'Usage',
508
+ description: 'Learn how to use this',
509
+ },
510
+ {
511
+ id: `auto-generic-4-${Date.now()}`,
512
+ text: 'Where can I learn more?',
513
+ icon: '🔗',
514
+ category: 'Resources',
515
+ description: 'Find additional resources',
516
+ },
517
+ {
518
+ id: `auto-generic-5-${Date.now()}`,
519
+ text: 'How do I get help?',
520
+ icon: '🤝',
521
+ category: 'Help',
522
+ description: 'Get support',
523
+ },
524
+ ];
525
+ }
526
+ /**
527
+ * Update config
528
+ */
529
+ updateConfig(config) {
530
+ this.config = { ...this.config, ...config };
531
+ }
532
+ /**
533
+ * Get current tier
534
+ */
535
+ getTier() {
536
+ return this.config.tier || 'free';
537
+ }
538
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * AI Widget SDK - Core Communication Service
3
+ * Handle HTTP requests to backend API
4
+ */
5
+ import { AiQueryRequest, AiQueryResponse } from './models';
6
+ export declare class AiCommunicationService {
7
+ private apiEndpoint;
8
+ private apiKey?;
9
+ private jwtToken?;
10
+ constructor(apiEndpoint: string, apiKey?: string);
11
+ /**
12
+ * Set JWT token for authentication
13
+ */
14
+ setJwtToken(token: string): void;
15
+ /**
16
+ * Get current JWT token
17
+ */
18
+ getJwtToken(): string | undefined;
19
+ /**
20
+ * Check if JWT token is set
21
+ */
22
+ isAuthenticated(): boolean;
23
+ /**
24
+ * Send query to backend API
25
+ */
26
+ sendQuery(request: AiQueryRequest): Promise<AiQueryResponse>;
27
+ /**
28
+ * Check backend health
29
+ */
30
+ checkHealth(): Promise<boolean>;
31
+ /**
32
+ * Get API endpoint
33
+ */
34
+ getApiEndpoint(): string;
35
+ }
36
+ /**
37
+ * Custom API Error class
38
+ */
39
+ export declare class ApiError extends Error {
40
+ statusCode: number;
41
+ responseData?: any | undefined;
42
+ constructor(message: string, statusCode: number, responseData?: any | undefined);
43
+ isRateLimited(): boolean;
44
+ isUnauthorized(): boolean;
45
+ isBadRequest(): boolean;
46
+ isServerError(): boolean;
47
+ }