@courseecho/ai-widget-react 1.0.24 → 1.0.25
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/dist/ShadowWrapper.d.ts +3 -7
- package/dist/ShadowWrapper.js +6 -177
- package/package.json +1 -1
- package/dist/core/src/auto-suggestion-generator.d.ts +0 -118
- package/dist/core/src/auto-suggestion-generator.js +0 -538
- package/dist/core/src/communication-service.d.ts +0 -47
- package/dist/core/src/communication-service.js +0 -124
- package/dist/core/src/index.d.ts +0 -11
- package/dist/core/src/index.js +0 -15
- package/dist/core/src/models.d.ts +0 -137
- package/dist/core/src/models.js +0 -5
- package/dist/core/src/sdk.d.ts +0 -166
- package/dist/core/src/sdk.js +0 -387
- package/dist/core/src/state-manager.d.ts +0 -200
- package/dist/core/src/state-manager.js +0 -368
- package/dist/core/src/widget-css.d.ts +0 -14
- package/dist/core/src/widget-css.js +0 -389
- package/dist/react/src/ShadowWrapper.d.ts +0 -12
- package/dist/react/src/ShadowWrapper.js +0 -44
- package/dist/react/src/components.d.ts +0 -25
- package/dist/react/src/components.js +0 -226
- package/dist/react/src/hooks.d.ts +0 -64
- package/dist/react/src/hooks.js +0 -181
- package/dist/react/src/index.d.ts +0 -7
- package/dist/react/src/index.js +0 -5
|
@@ -1,538 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AI Widget SDK - Core Communication Service
|
|
3
|
-
* Handle HTTP requests to backend API
|
|
4
|
-
*/
|
|
5
|
-
/** Safely parse a Response body as JSON — returns null for empty/non-JSON bodies instead of throwing. */
|
|
6
|
-
async function safeJson(res) {
|
|
7
|
-
const text = await res.text();
|
|
8
|
-
if (!text || !text.trim())
|
|
9
|
-
return null;
|
|
10
|
-
try {
|
|
11
|
-
return JSON.parse(text);
|
|
12
|
-
}
|
|
13
|
-
catch {
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
export class AiCommunicationService {
|
|
18
|
-
constructor(apiEndpoint, apiKey) {
|
|
19
|
-
if (!apiEndpoint) {
|
|
20
|
-
throw new Error('apiEndpoint is required');
|
|
21
|
-
}
|
|
22
|
-
this.apiEndpoint = apiEndpoint.replace(/\/$/, ''); // Remove trailing slash
|
|
23
|
-
this.apiKey = apiKey;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Set JWT token for authentication
|
|
27
|
-
*/
|
|
28
|
-
setJwtToken(token) {
|
|
29
|
-
this.jwtToken = token;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Get current JWT token
|
|
33
|
-
*/
|
|
34
|
-
getJwtToken() {
|
|
35
|
-
return this.jwtToken;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Check if JWT token is set
|
|
39
|
-
*/
|
|
40
|
-
isAuthenticated() {
|
|
41
|
-
return !!this.jwtToken;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Send query to backend API
|
|
45
|
-
*/
|
|
46
|
-
async sendQuery(request) {
|
|
47
|
-
if (!this.jwtToken && !this.apiKey) {
|
|
48
|
-
throw new Error('Authentication required: Set JWT token or API key. Call setJwtToken() first.');
|
|
49
|
-
}
|
|
50
|
-
const headers = {
|
|
51
|
-
'Content-Type': 'application/json',
|
|
52
|
-
};
|
|
53
|
-
if (this.jwtToken) {
|
|
54
|
-
headers['Authorization'] = `Bearer ${this.jwtToken}`;
|
|
55
|
-
}
|
|
56
|
-
else if (this.apiKey) {
|
|
57
|
-
headers['X-API-Key'] = this.apiKey;
|
|
58
|
-
}
|
|
59
|
-
try {
|
|
60
|
-
const response = await fetch(`${this.apiEndpoint}/api/aiorchestrator/query`, {
|
|
61
|
-
method: 'POST',
|
|
62
|
-
headers,
|
|
63
|
-
body: JSON.stringify(request),
|
|
64
|
-
});
|
|
65
|
-
if (!response.ok) {
|
|
66
|
-
const errorData = await safeJson(response);
|
|
67
|
-
throw new ApiError(errorData?.message || `HTTP ${response.status}`, response.status, errorData);
|
|
68
|
-
}
|
|
69
|
-
const data = (await safeJson(response));
|
|
70
|
-
if (!data)
|
|
71
|
-
throw new ApiError('Empty response from server', 500);
|
|
72
|
-
// Ensure timestamp is a Date object
|
|
73
|
-
data.timestamp = new Date(data.timestamp);
|
|
74
|
-
return data;
|
|
75
|
-
}
|
|
76
|
-
catch (error) {
|
|
77
|
-
if (error instanceof ApiError) {
|
|
78
|
-
throw error;
|
|
79
|
-
}
|
|
80
|
-
throw new ApiError(`Failed to send query: ${String(error)}`, 500);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Check backend health
|
|
85
|
-
*/
|
|
86
|
-
async checkHealth() {
|
|
87
|
-
try {
|
|
88
|
-
const response = await fetch(`${this.apiEndpoint}/health`);
|
|
89
|
-
return response.ok;
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Get API endpoint
|
|
97
|
-
*/
|
|
98
|
-
getApiEndpoint() {
|
|
99
|
-
return this.apiEndpoint;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Custom API Error class
|
|
104
|
-
*/
|
|
105
|
-
export class ApiError extends Error {
|
|
106
|
-
constructor(message, statusCode, responseData) {
|
|
107
|
-
super(message);
|
|
108
|
-
this.statusCode = statusCode;
|
|
109
|
-
this.responseData = responseData;
|
|
110
|
-
this.name = 'ApiError';
|
|
111
|
-
}
|
|
112
|
-
isRateLimited() {
|
|
113
|
-
return this.statusCode === 429;
|
|
114
|
-
}
|
|
115
|
-
isUnauthorized() {
|
|
116
|
-
return this.statusCode === 401;
|
|
117
|
-
}
|
|
118
|
-
isBadRequest() {
|
|
119
|
-
return this.statusCode === 400;
|
|
120
|
-
}
|
|
121
|
-
isServerError() {
|
|
122
|
-
return this.statusCode >= 500;
|
|
123
|
-
}
|
|
124
|
-
}
|