@a.izzuddin/ai-chat 0.2.10 → 0.2.13

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/index.mjs CHANGED
@@ -14,7 +14,7 @@ var __decorateClass = (decorators, target, key, kind) => {
14
14
  if (kind && result) __defProp(target, key, result);
15
15
  return result;
16
16
  };
17
- var VERSION = "0.2.10";
17
+ var VERSION = "0.2.13";
18
18
  var AIChat = class extends LitElement {
19
19
  constructor() {
20
20
  super();
@@ -35,6 +35,9 @@ var AIChat = class extends LitElement {
35
35
  this.botMessageBg = "#F5F5F5";
36
36
  this.welcomeMessage = "How can I help you today?";
37
37
  this.welcomeSubtitle = "";
38
+ this.initialQuestionsUrl = "";
39
+ this.language = "en";
40
+ this.showRelatedFaqs = true;
38
41
  this.messages = [];
39
42
  this.input = "";
40
43
  this.isLoading = false;
@@ -78,29 +81,38 @@ ${this.welcomeSubtitle}` : this.welcomeMessage;
78
81
  saveMessagesToStorage() {
79
82
  try {
80
83
  const storageKey = this.getStorageKey();
81
- localStorage.setItem(storageKey, JSON.stringify(this.messages));
84
+ sessionStorage.setItem(storageKey, JSON.stringify(this.messages));
85
+ sessionStorage.setItem("ai-chat-last-session-id", this.sessionId);
82
86
  } catch (error) {
83
- console.warn("Failed to save messages to localStorage:", error);
87
+ console.warn("Failed to save messages to sessionStorage:", error);
84
88
  }
85
89
  }
86
90
  loadMessagesFromStorage() {
87
91
  try {
92
+ const lastSessionId = sessionStorage.getItem("ai-chat-last-session-id");
93
+ if (lastSessionId && lastSessionId !== this.sessionId) {
94
+ console.log(`\u{1F504} Session changed from "${lastSessionId}" to "${this.sessionId}", clearing old messages`);
95
+ const oldStorageKey = `ai-chat-messages-${lastSessionId}`;
96
+ sessionStorage.removeItem(oldStorageKey);
97
+ sessionStorage.setItem("ai-chat-last-session-id", this.sessionId);
98
+ return null;
99
+ }
88
100
  const storageKey = this.getStorageKey();
89
- const saved = localStorage.getItem(storageKey);
101
+ const saved = sessionStorage.getItem(storageKey);
90
102
  if (saved) {
91
103
  return JSON.parse(saved);
92
104
  }
93
105
  } catch (error) {
94
- console.warn("Failed to load messages from localStorage:", error);
106
+ console.warn("Failed to load messages from sessionStorage:", error);
95
107
  }
96
108
  return null;
97
109
  }
98
110
  clearMessagesFromStorage() {
99
111
  try {
100
112
  const storageKey = this.getStorageKey();
101
- localStorage.removeItem(storageKey);
113
+ sessionStorage.removeItem(storageKey);
102
114
  } catch (error) {
103
- console.warn("Failed to clear messages from localStorage:", error);
115
+ console.warn("Failed to clear messages from sessionStorage:", error);
104
116
  }
105
117
  }
106
118
  formatMessageContent(content) {
@@ -184,22 +196,59 @@ ${this.welcomeSubtitle}` : this.welcomeMessage;
184
196
  }
185
197
  return formattedContent;
186
198
  }
187
- connectedCallback() {
199
+ async connectedCallback() {
188
200
  super.connectedCallback();
189
201
  const savedMessages = this.loadMessagesFromStorage();
190
202
  if (this.initialMessages && this.initialMessages.length > 0) {
191
203
  this.messages = [...this.initialMessages];
192
204
  } else if (savedMessages && savedMessages.length > 0) {
193
205
  this.messages = savedMessages;
194
- } else if (this.welcomeMessage) {
195
- const welcomeText = this.welcomeSubtitle ? `${this.welcomeMessage}
206
+ } else {
207
+ let suggestedQuestions = void 0;
208
+ if (this.initialQuestionsUrl) {
209
+ try {
210
+ let fetchUrl = this.initialQuestionsUrl;
211
+ if (this.language) {
212
+ const separator = fetchUrl.includes("?") ? "&" : "?";
213
+ fetchUrl = `${fetchUrl}${separator}language=${this.language}`;
214
+ }
215
+ console.log("\u{1F4E4} Fetching initial questions from:", fetchUrl);
216
+ const response = await fetch(fetchUrl);
217
+ if (response.ok) {
218
+ const data = await response.json();
219
+ console.log("\u{1F4E5} Fetched initial questions:", data);
220
+ let questionsArray = data.questions || data.suggested_questions || data;
221
+ if (Array.isArray(questionsArray) && questionsArray.length > 0) {
222
+ if (typeof questionsArray[0] === "object" && questionsArray[0].question_text) {
223
+ suggestedQuestions = questionsArray.map((q) => ({
224
+ id: q.id,
225
+ question_type: q.question_type,
226
+ question_text: q.question_text,
227
+ category: q.category
228
+ }));
229
+ } else if (typeof questionsArray[0] === "string") {
230
+ suggestedQuestions = questionsArray.map((q) => ({
231
+ question_text: q
232
+ }));
233
+ }
234
+ }
235
+ console.log("\u2705 Processed suggested questions:", suggestedQuestions);
236
+ }
237
+ } catch (error) {
238
+ console.warn("Failed to fetch initial questions:", error);
239
+ }
240
+ }
241
+ if (this.welcomeMessage) {
242
+ const welcomeText = this.welcomeSubtitle ? `${this.welcomeMessage}
196
243
 
197
244
  ${this.welcomeSubtitle}` : this.welcomeMessage;
198
- this.messages = [{
199
- id: "welcome-" + Date.now(),
200
- role: "assistant",
201
- content: welcomeText
202
- }];
245
+ this.messages = [{
246
+ id: "welcome-" + Date.now(),
247
+ role: "assistant",
248
+ content: welcomeText,
249
+ suggestedQuestions
250
+ }];
251
+ }
203
252
  }
204
253
  }
205
254
  updated(changedProperties) {
@@ -218,14 +267,121 @@ ${this.welcomeSubtitle}` : this.welcomeMessage;
218
267
  }
219
268
  }, 100);
220
269
  }
270
+ /**
271
+ * Normalize suggested questions - converts string arrays to SuggestedQuestion objects
272
+ */
273
+ normalizeSuggestedQuestions(questions) {
274
+ if (!questions || !Array.isArray(questions) || questions.length === 0) {
275
+ return void 0;
276
+ }
277
+ if (typeof questions[0] === "object" && questions[0].question_text) {
278
+ return questions;
279
+ }
280
+ if (typeof questions[0] === "string") {
281
+ return questions.map((q) => ({
282
+ question_text: q
283
+ }));
284
+ }
285
+ return void 0;
286
+ }
221
287
  handleInput(e) {
222
288
  this.input = e.target.value;
223
289
  }
224
- handleFAQClick(question) {
290
+ async handleFAQClick(question) {
225
291
  if (this.isLoading) return;
226
- this.input = question;
227
- const submitEvent = new Event("submit", { cancelable: true });
228
- this.handleSubmit(submitEvent);
292
+ if (question.id && question.question_type) {
293
+ await this.handleSuggestedQuestionClick(question);
294
+ } else {
295
+ this.input = question.question_text;
296
+ const submitEvent = new Event("submit", { cancelable: true });
297
+ this.handleSubmit(submitEvent);
298
+ }
299
+ }
300
+ async handleSuggestedQuestionClick(question) {
301
+ if (!question.id || !question.question_type) return;
302
+ const userMessage = {
303
+ id: Date.now().toString(),
304
+ role: "user",
305
+ content: question.question_text
306
+ };
307
+ this.messages = [...this.messages, userMessage];
308
+ this.isLoading = true;
309
+ this.dispatchEvent(new CustomEvent("message-sent", {
310
+ detail: userMessage,
311
+ bubbles: true,
312
+ composed: true
313
+ }));
314
+ try {
315
+ let baseUrl = "";
316
+ if (this.initialQuestionsUrl) {
317
+ try {
318
+ const urlObj = new URL(this.initialQuestionsUrl);
319
+ baseUrl = `${urlObj.protocol}//${urlObj.host}`;
320
+ } catch {
321
+ baseUrl = "http://43.217.183.120:8080";
322
+ }
323
+ } else {
324
+ baseUrl = "http://43.217.183.120:8080";
325
+ }
326
+ const url = `${baseUrl}/api/questions/${question.id}?question_type=${question.question_type}`;
327
+ console.log("\u{1F4E4} Calling suggested question API:", url);
328
+ const response = await fetch(url, {
329
+ method: "GET",
330
+ headers: { "Content-Type": "application/json" }
331
+ });
332
+ if (!response.ok) {
333
+ const errorText = await response.text();
334
+ throw new Error(`Backend error: ${response.status} ${errorText}`);
335
+ }
336
+ const data = await response.json();
337
+ console.log("\u{1F50D} Suggested question API response:", data);
338
+ let responseText = "No response from agent";
339
+ let suggestedQuestions = void 0;
340
+ if (data && typeof data === "object") {
341
+ if (data.question && data.question.answer_text) {
342
+ responseText = data.question.answer_text;
343
+ }
344
+ if (data.related_questions && Array.isArray(data.related_questions) && data.related_questions.length > 0) {
345
+ if (typeof data.related_questions[0] === "object" && data.related_questions[0].question_text) {
346
+ suggestedQuestions = data.related_questions.map((q) => ({
347
+ id: q.id,
348
+ question_type: q.question_type,
349
+ question_text: q.question_text,
350
+ category: q.category
351
+ }));
352
+ }
353
+ }
354
+ }
355
+ const assistantMessage = {
356
+ id: (Date.now() + 1).toString(),
357
+ role: "assistant",
358
+ content: responseText,
359
+ suggestedQuestions
360
+ };
361
+ this.messages = [...this.messages, assistantMessage];
362
+ this.dispatchEvent(new CustomEvent("response-received", {
363
+ detail: assistantMessage,
364
+ bubbles: true,
365
+ composed: true
366
+ }));
367
+ } catch (err) {
368
+ console.error("Suggested question API failed:", err);
369
+ const errorMessage = {
370
+ id: (Date.now() + 1).toString(),
371
+ role: "assistant",
372
+ content: `Error: ${err instanceof Error ? err.message : "Unknown error"}
373
+
374
+ Please check your API endpoint configuration.`
375
+ };
376
+ this.messages = [...this.messages, errorMessage];
377
+ this.dispatchEvent(new CustomEvent("error", {
378
+ detail: err,
379
+ bubbles: true,
380
+ composed: true
381
+ }));
382
+ } finally {
383
+ this.isLoading = false;
384
+ }
229
385
  }
230
386
  async handleSubmit(e) {
231
387
  e.preventDefault();
@@ -274,14 +430,14 @@ ${this.welcomeSubtitle}` : this.welcomeMessage;
274
430
  if (innerData && innerData.response && typeof innerData.response === "string") {
275
431
  responseText = innerData.response;
276
432
  faqs = innerData.faq_used || innerData.faqs_used || void 0;
277
- suggestedQuestions = innerData.suggested_follow_ups || innerData.suggested_questions || void 0;
433
+ suggestedQuestions = this.normalizeSuggestedQuestions(innerData.suggested_follow_ups || innerData.suggested_questions);
278
434
  console.log("\u2705 Extracted text length:", responseText.length);
279
435
  console.log("\u2705 Extracted FAQs count:", faqs?.length || 0);
280
436
  console.log("\u2705 Extracted suggested questions count:", suggestedQuestions?.length || 0);
281
437
  } else {
282
438
  responseText = data.response;
283
439
  faqs = data.faq_used || data.faqs_used || void 0;
284
- suggestedQuestions = data.suggested_follow_ups || data.suggested_questions || void 0;
440
+ suggestedQuestions = this.normalizeSuggestedQuestions(data.suggested_follow_ups || data.suggested_questions);
285
441
  }
286
442
  } catch (parseError) {
287
443
  console.warn("\u26A0\uFE0F JSON.parse failed, using regex extraction...", parseError);
@@ -318,7 +474,8 @@ ${this.welcomeSubtitle}` : this.welcomeMessage;
318
474
  const suggestedMatch = data.response.match(suggestedPattern);
319
475
  if (suggestedMatch) {
320
476
  try {
321
- suggestedQuestions = JSON.parse(suggestedMatch[1]);
477
+ const parsedQuestions = JSON.parse(suggestedMatch[1]);
478
+ suggestedQuestions = this.normalizeSuggestedQuestions(parsedQuestions);
322
479
  console.log("\u2705 Extracted suggested questions, count:", suggestedQuestions?.length || 0);
323
480
  } catch {
324
481
  console.log("\u26A0\uFE0F Could not parse suggested questions, trying multiline...");
@@ -326,7 +483,8 @@ ${this.welcomeSubtitle}` : this.welcomeMessage;
326
483
  const suggestedMultiMatch = data.response.match(suggestedMultiPattern);
327
484
  if (suggestedMultiMatch) {
328
485
  try {
329
- suggestedQuestions = JSON.parse(suggestedMultiMatch[1]);
486
+ const parsedQuestions = JSON.parse(suggestedMultiMatch[1]);
487
+ suggestedQuestions = this.normalizeSuggestedQuestions(parsedQuestions);
330
488
  console.log("\u2705 Extracted multi-line suggested questions, count:", suggestedQuestions?.length || 0);
331
489
  } catch {
332
490
  suggestedQuestions = void 0;
@@ -339,7 +497,7 @@ ${this.welcomeSubtitle}` : this.welcomeMessage;
339
497
  console.log("\u{1F4C4} Direct text response (not JSON)");
340
498
  responseText = data.response;
341
499
  faqs = data.faq_used || data.faqs_used || void 0;
342
- suggestedQuestions = data.suggested_follow_ups || data.suggested_questions || void 0;
500
+ suggestedQuestions = this.normalizeSuggestedQuestions(data.suggested_follow_ups || data.suggested_questions);
343
501
  }
344
502
  } else if (typeof data === "string") {
345
503
  console.log("\u{1F4C4} Response is a plain string");
@@ -348,7 +506,7 @@ ${this.welcomeSubtitle}` : this.welcomeMessage;
348
506
  console.warn("\u26A0\uFE0F Unexpected format, using fallback");
349
507
  responseText = data.message || data.answer || "Error: Unexpected response format";
350
508
  faqs = data.faq_used || data.faqs_used || void 0;
351
- suggestedQuestions = data.suggested_follow_ups || data.suggested_questions || void 0;
509
+ suggestedQuestions = this.normalizeSuggestedQuestions(data.suggested_follow_ups || data.suggested_questions);
352
510
  }
353
511
  console.log("\u{1F3AF} Final responseText length:", responseText.length);
354
512
  console.log("\u{1F3AF} Final responseText preview:", responseText.substring(0, 100));
@@ -417,13 +575,13 @@ Please check your API endpoint configuration.`
417
575
  </div>
418
576
  <div class="message-content">
419
577
  <div class="message-text">${unsafeHTML(this.formatMessageContent(msg.content))}</div>
420
- ${msg.role === "assistant" && msg.faqs && msg.faqs.length > 0 ? html`
578
+ ${msg.role === "assistant" && this.showRelatedFaqs && msg.faqs && msg.faqs.length > 0 ? html`
421
579
  <div class="faq-section">
422
580
  <p class="faq-title">Related FAQs:</p>
423
581
  <ul class="faq-list">
424
582
  ${msg.faqs.map((faq) => html`
425
583
  <li class="faq-item-static">
426
- ${faq.question}
584
+ ${faq.Question}
427
585
  </li>
428
586
  `)}
429
587
  </ul>
@@ -435,7 +593,7 @@ Please check your API endpoint configuration.`
435
593
  <ul class="faq-list">
436
594
  ${msg.suggestedQuestions.map((question) => html`
437
595
  <li class="faq-item" @click=${() => this.handleFAQClick(question)}>
438
- ${question}
596
+ ${question.question_text}
439
597
  </li>
440
598
  `)}
441
599
  </ul>
@@ -601,9 +759,12 @@ AIChat.styles = css`
601
759
  }
602
760
 
603
761
  .widget-button-icon {
604
- width: auto;
605
- height: auto;
606
- object-fit: cover;
762
+ width: 100%;
763
+ height: 100%;
764
+ max-width: 60px;
765
+ max-height: 60px;
766
+ object-fit: contain;
767
+ border-radius: 50%;
607
768
  }
608
769
 
609
770
  .widget-window {
@@ -1296,7 +1457,10 @@ AIChat.properties = {
1296
1457
  userMessageBg: { type: String, attribute: "user-message-bg" },
1297
1458
  botMessageBg: { type: String, attribute: "bot-message-bg" },
1298
1459
  welcomeMessage: { type: String, attribute: "welcome-message" },
1299
- welcomeSubtitle: { type: String, attribute: "welcome-subtitle" }
1460
+ welcomeSubtitle: { type: String, attribute: "welcome-subtitle" },
1461
+ initialQuestionsUrl: { type: String, attribute: "initial-questions-url" },
1462
+ language: { type: String, attribute: "language" },
1463
+ showRelatedFaqs: { type: Boolean, attribute: "show-related-faqs" }
1300
1464
  };
1301
1465
  __decorateClass([
1302
1466
  state()