@a.izzuddin/ai-chat 0.2.22 → 0.2.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.
package/dist/index.mjs CHANGED
@@ -5371,7 +5371,7 @@ MarkdownIt.prototype.renderInline = function(src, env) {
5371
5371
  var lib_default = MarkdownIt;
5372
5372
 
5373
5373
  // src/components/ai-chat.ts
5374
- console.log("Chatbot Ver = 0.2.22-beta.0");
5374
+ console.log("Chatbot Ver = 0.2.24-beta.0");
5375
5375
  var md = new lib_default({
5376
5376
  html: false,
5377
5377
  // Disable HTML tags in source for security
@@ -5448,6 +5448,43 @@ ${this.welcomeSubtitle}` : this.welcomeMessage;
5448
5448
  getStorageKey() {
5449
5449
  return `ai-chat-messages-${this.sessionId}`;
5450
5450
  }
5451
+ getPendingRequestKey() {
5452
+ return `ai-chat-pending-${this.sessionId}`;
5453
+ }
5454
+ savePendingRequest(question, type = "ask", questionData) {
5455
+ try {
5456
+ const pendingRequest = {
5457
+ question,
5458
+ type,
5459
+ questionData,
5460
+ timestamp: Date.now()
5461
+ };
5462
+ const key = this.getPendingRequestKey();
5463
+ localStorage.setItem(key, JSON.stringify(pendingRequest));
5464
+ } catch (error2) {
5465
+ console.warn("Failed to save pending request to localStorage:", error2);
5466
+ }
5467
+ }
5468
+ loadPendingRequest() {
5469
+ try {
5470
+ const key = this.getPendingRequestKey();
5471
+ const saved = localStorage.getItem(key);
5472
+ if (saved) {
5473
+ return JSON.parse(saved);
5474
+ }
5475
+ } catch (error2) {
5476
+ console.warn("Failed to load pending request from localStorage:", error2);
5477
+ }
5478
+ return null;
5479
+ }
5480
+ clearPendingRequest() {
5481
+ try {
5482
+ const key = this.getPendingRequestKey();
5483
+ localStorage.removeItem(key);
5484
+ } catch (error2) {
5485
+ console.warn("Failed to clear pending request from localStorage:", error2);
5486
+ }
5487
+ }
5451
5488
  saveMessagesToStorage() {
5452
5489
  try {
5453
5490
  const storageKey = this.getStorageKey();
@@ -5545,6 +5582,19 @@ ${this.welcomeSubtitle}` : this.welcomeMessage;
5545
5582
  }];
5546
5583
  }
5547
5584
  }
5585
+ const pendingRequest = this.loadPendingRequest();
5586
+ if (pendingRequest) {
5587
+ const messagesWithoutErrors = this.messages.filter((msg) => {
5588
+ if (msg.role === "assistant" && msg.content === this.errorMessage) {
5589
+ return false;
5590
+ }
5591
+ return true;
5592
+ });
5593
+ this.messages = messagesWithoutErrors;
5594
+ setTimeout(() => {
5595
+ this.retryPendingRequest(pendingRequest);
5596
+ }, 500);
5597
+ }
5548
5598
  }
5549
5599
  updated(changedProperties) {
5550
5600
  super.updated(changedProperties);
@@ -5678,7 +5728,7 @@ ${this.welcomeSubtitle}` : this.welcomeMessage;
5678
5728
  const results = await Promise.all(fetchPromises);
5679
5729
  return results.filter((q) => q !== null);
5680
5730
  }
5681
- async handleFAQClick(question) {
5731
+ async handleFAQClick(question, event) {
5682
5732
  if (this.isLoading) return;
5683
5733
  if (question.Id && question.QuestionType || question.id && question.question_type) {
5684
5734
  await this.handleSuggestedQuestionClick(question);
@@ -5688,6 +5738,233 @@ ${this.welcomeSubtitle}` : this.welcomeMessage;
5688
5738
  this.handleSubmit(submitEvent);
5689
5739
  }
5690
5740
  }
5741
+ /**
5742
+ * Retry a pending request without adding duplicate user messages
5743
+ * The user message is already in the chat history from before the interruption
5744
+ */
5745
+ async retryPendingRequest(pendingRequest) {
5746
+ this.isLoading = true;
5747
+ if (pendingRequest.type === "suggested" && pendingRequest.questionData) {
5748
+ try {
5749
+ const { questionId, questionType } = pendingRequest.questionData;
5750
+ let baseUrl = "";
5751
+ if (this.initialQuestionsUrl) {
5752
+ try {
5753
+ const urlObj = new URL(this.initialQuestionsUrl);
5754
+ baseUrl = `${urlObj.protocol}//${urlObj.host}`;
5755
+ } catch {
5756
+ baseUrl = "http://43.217.183.120:8080";
5757
+ }
5758
+ } else {
5759
+ baseUrl = "http://43.217.183.120:8080";
5760
+ }
5761
+ const url = `${baseUrl}/api/questions/${questionId}?question_type=${questionType}&language=${this.language}`;
5762
+ const response = await fetch(url, {
5763
+ method: "GET",
5764
+ headers: { "Content-Type": "application/json" }
5765
+ });
5766
+ if (!response.ok) {
5767
+ const errorText = await response.text();
5768
+ throw new Error(`Backend error: ${response.status} ${errorText}`);
5769
+ }
5770
+ const data = await response.json();
5771
+ let responseText = "No response from agent";
5772
+ let suggestedQuestions = void 0;
5773
+ if (data && typeof data === "object") {
5774
+ if (data.question && data.question.answer_text) {
5775
+ responseText = data.question.answer_text;
5776
+ }
5777
+ if (data.related_questions && Array.isArray(data.related_questions) && data.related_questions.length > 0) {
5778
+ if (typeof data.related_questions[0] === "object" && data.related_questions[0].question_text) {
5779
+ suggestedQuestions = data.related_questions.map((q) => ({
5780
+ id: q.id,
5781
+ question_type: q.question_type,
5782
+ question_text: q.question_text,
5783
+ category: q.category
5784
+ }));
5785
+ }
5786
+ }
5787
+ }
5788
+ const assistantMessage = {
5789
+ id: (Date.now() + 1).toString(),
5790
+ role: "assistant",
5791
+ content: responseText,
5792
+ suggestedQuestions
5793
+ };
5794
+ this.messages = [...this.messages, assistantMessage];
5795
+ this.clearPendingRequest();
5796
+ this.dispatchEvent(new CustomEvent("response-received", {
5797
+ detail: assistantMessage,
5798
+ bubbles: true,
5799
+ composed: true
5800
+ }));
5801
+ } catch (err) {
5802
+ console.error("Retry suggested question API failed:", err);
5803
+ const errorMessage = {
5804
+ id: (Date.now() + 1).toString(),
5805
+ role: "assistant",
5806
+ content: this.errorMessage
5807
+ };
5808
+ this.messages = [...this.messages, errorMessage];
5809
+ this.dispatchEvent(new CustomEvent("error", {
5810
+ detail: err,
5811
+ bubbles: true,
5812
+ composed: true
5813
+ }));
5814
+ } finally {
5815
+ this.isLoading = false;
5816
+ }
5817
+ } else {
5818
+ try {
5819
+ const response = await fetch(`${this.apiUrl}/ask`, {
5820
+ method: "POST",
5821
+ headers: { "Content-Type": "application/json" },
5822
+ body: JSON.stringify({
5823
+ session_id: this.sessionId,
5824
+ question: pendingRequest.question
5825
+ })
5826
+ });
5827
+ if (!response.ok) {
5828
+ const errorText = await response.text();
5829
+ throw new Error(`Backend error: ${response.status} ${errorText}`);
5830
+ }
5831
+ const data = await response.json();
5832
+ let responseText = "No response from agent";
5833
+ let faqs = void 0;
5834
+ let suggestedQuestions = void 0;
5835
+ let confidence = void 0;
5836
+ let responseLanguage = void 0;
5837
+ if (data && typeof data === "object" && data.response && typeof data.response === "string") {
5838
+ const trimmedResponse = data.response.trim();
5839
+ if (trimmedResponse.startsWith("{") || trimmedResponse.startsWith("[")) {
5840
+ try {
5841
+ const innerData = JSON.parse(data.response);
5842
+ if (innerData && innerData.response && typeof innerData.response === "string") {
5843
+ responseText = innerData.response;
5844
+ faqs = innerData.faq_used || innerData.faqs_used || void 0;
5845
+ suggestedQuestions = this.normalizeSuggestedQuestions(innerData.suggested_follow_ups || innerData.suggested_questions);
5846
+ confidence = innerData.confident || innerData.confidence || "true";
5847
+ responseLanguage = innerData.language || void 0;
5848
+ } else {
5849
+ responseText = data.response;
5850
+ faqs = data.faq_used || data.faqs_used || void 0;
5851
+ suggestedQuestions = this.normalizeSuggestedQuestions(data.suggested_follow_ups || data.suggested_questions);
5852
+ confidence = data.confident || data.confidence || void 0;
5853
+ responseLanguage = data.language || void 0;
5854
+ }
5855
+ } catch (parseError) {
5856
+ const responsePattern = /"response"\s*:\s*"([^"]*(?:\\.[^"]*)*)"/s;
5857
+ const responseMatch = data.response.match(responsePattern);
5858
+ if (responseMatch) {
5859
+ responseText = responseMatch[1].replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\r/g, "\r").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
5860
+ } else {
5861
+ responseText = "Error: Could not parse response";
5862
+ }
5863
+ const faqsPattern = /"(?:faq_used|faqs_used)"\s*:\s*(\[[^\]]*\])/s;
5864
+ const faqsMatch = data.response.match(faqsPattern);
5865
+ if (faqsMatch) {
5866
+ try {
5867
+ faqs = JSON.parse(faqsMatch[1]);
5868
+ } catch {
5869
+ const faqsMultiPattern = /"(?:faq_used|faqs_used)"\s*:\s*(\[[\s\S]*?\n\s*\])/;
5870
+ const faqsMultiMatch = data.response.match(faqsMultiPattern);
5871
+ if (faqsMultiMatch) {
5872
+ try {
5873
+ faqs = JSON.parse(faqsMultiMatch[1]);
5874
+ } catch {
5875
+ faqs = void 0;
5876
+ }
5877
+ }
5878
+ }
5879
+ }
5880
+ const suggestedPattern = /"(?:suggested_follow_ups|suggested_questions)"\s*:\s*(\[[^\]]*\])/s;
5881
+ const suggestedMatch = data.response.match(suggestedPattern);
5882
+ if (suggestedMatch) {
5883
+ try {
5884
+ const parsedQuestions = JSON.parse(suggestedMatch[1]);
5885
+ suggestedQuestions = this.normalizeSuggestedQuestions(parsedQuestions);
5886
+ } catch {
5887
+ const suggestedMultiPattern = /"(?:suggested_follow_ups|suggested_questions)"\s*:\s*(\[[\s\S]*?\n\s*\])/;
5888
+ const suggestedMultiMatch = data.response.match(suggestedMultiPattern);
5889
+ if (suggestedMultiMatch) {
5890
+ try {
5891
+ const parsedQuestions = JSON.parse(suggestedMultiMatch[1]);
5892
+ suggestedQuestions = this.normalizeSuggestedQuestions(parsedQuestions);
5893
+ } catch {
5894
+ suggestedQuestions = void 0;
5895
+ }
5896
+ }
5897
+ }
5898
+ }
5899
+ }
5900
+ } else {
5901
+ responseText = data.response;
5902
+ faqs = data.faq_used || data.faqs_used || void 0;
5903
+ suggestedQuestions = this.normalizeSuggestedQuestions(data.suggested_follow_ups || data.suggested_questions);
5904
+ confidence = data.confident || data.confidence || void 0;
5905
+ responseLanguage = data.language || void 0;
5906
+ }
5907
+ } else if (typeof data === "string") {
5908
+ responseText = data;
5909
+ } else if (data && typeof data === "object") {
5910
+ responseText = data.message || data.answer || "Error: Unexpected response format";
5911
+ faqs = data.faq_used || data.faqs_used || void 0;
5912
+ suggestedQuestions = this.normalizeSuggestedQuestions(data.suggested_follow_ups || data.suggested_questions);
5913
+ confidence = data.confident || data.confidence || void 0;
5914
+ responseLanguage = data.language || void 0;
5915
+ }
5916
+ if (suggestedQuestions && suggestedQuestions.length > 0) {
5917
+ const questionsNeedingText = suggestedQuestions.filter(
5918
+ (q) => (q.Id || q.id) && (q.QuestionType || q.question_type) && !q.question_text
5919
+ );
5920
+ if (questionsNeedingText.length > 0) {
5921
+ const questionsToFetch = questionsNeedingText.map((q) => ({
5922
+ Id: q.Id || q.id?.toString(),
5923
+ QuestionType: q.QuestionType || q.question_type
5924
+ }));
5925
+ const fetchedQuestions = await this.fetchQuestionTexts(questionsToFetch);
5926
+ suggestedQuestions = [
5927
+ ...suggestedQuestions.filter((q) => q.question_text),
5928
+ ...fetchedQuestions
5929
+ ];
5930
+ }
5931
+ }
5932
+ if (responseLanguage) {
5933
+ this.language = responseLanguage;
5934
+ }
5935
+ const assistantMessage = {
5936
+ id: (Date.now() + 1).toString(),
5937
+ role: "assistant",
5938
+ content: responseText,
5939
+ faqs,
5940
+ suggestedQuestions,
5941
+ confidence
5942
+ };
5943
+ this.messages = [...this.messages, assistantMessage];
5944
+ this.clearPendingRequest();
5945
+ this.dispatchEvent(new CustomEvent("response-received", {
5946
+ detail: assistantMessage,
5947
+ bubbles: true,
5948
+ composed: true
5949
+ }));
5950
+ } catch (err) {
5951
+ console.error("Retry backend connection failed:", err);
5952
+ const errorMessage = {
5953
+ id: (Date.now() + 1).toString(),
5954
+ role: "assistant",
5955
+ content: this.errorMessage
5956
+ };
5957
+ this.messages = [...this.messages, errorMessage];
5958
+ this.dispatchEvent(new CustomEvent("error", {
5959
+ detail: err,
5960
+ bubbles: true,
5961
+ composed: true
5962
+ }));
5963
+ } finally {
5964
+ this.isLoading = false;
5965
+ }
5966
+ }
5967
+ }
5691
5968
  async handleSuggestedQuestionClick(question) {
5692
5969
  const questionId = question.Id || question.id;
5693
5970
  const questionType = question.QuestionType || question.question_type;
@@ -5699,6 +5976,7 @@ ${this.welcomeSubtitle}` : this.welcomeMessage;
5699
5976
  };
5700
5977
  this.messages = [...this.messages, userMessage];
5701
5978
  this.isLoading = true;
5979
+ this.savePendingRequest(question.question_text, "suggested", { questionId, questionType });
5702
5980
  this.dispatchEvent(new CustomEvent("message-sent", {
5703
5981
  detail: userMessage,
5704
5982
  bubbles: true,
@@ -5750,6 +6028,7 @@ ${this.welcomeSubtitle}` : this.welcomeMessage;
5750
6028
  suggestedQuestions
5751
6029
  };
5752
6030
  this.messages = [...this.messages, assistantMessage];
6031
+ this.clearPendingRequest();
5753
6032
  this.dispatchEvent(new CustomEvent("response-received", {
5754
6033
  detail: assistantMessage,
5755
6034
  bubbles: true,
@@ -5784,6 +6063,7 @@ ${this.welcomeSubtitle}` : this.welcomeMessage;
5784
6063
  const questionText = this.input.trim();
5785
6064
  this.input = "";
5786
6065
  this.isLoading = true;
6066
+ this.savePendingRequest(questionText, "ask");
5787
6067
  this.dispatchEvent(new CustomEvent("message-sent", {
5788
6068
  detail: userMessage,
5789
6069
  bubbles: true,
@@ -5917,6 +6197,7 @@ ${this.welcomeSubtitle}` : this.welcomeMessage;
5917
6197
  confidence
5918
6198
  };
5919
6199
  this.messages = [...this.messages, assistantMessage];
6200
+ this.clearPendingRequest();
5920
6201
  this.dispatchEvent(new CustomEvent("response-received", {
5921
6202
  detail: assistantMessage,
5922
6203
  bubbles: true,
@@ -5990,7 +6271,7 @@ ${this.welcomeSubtitle}` : this.welcomeMessage;
5990
6271
  <p class="faq-title">Cadangan Soalan:</p>
5991
6272
  <ul class="faq-list">
5992
6273
  ${msg.suggestedQuestions.map((question) => html`
5993
- <li class="faq-item" @click=${() => this.handleFAQClick(question)}>
6274
+ <li class="faq-item" @click=${(e) => this.handleFAQClick(question, e)}>
5994
6275
  ${question.question_text}
5995
6276
  </li>
5996
6277
  `)}
@@ -6261,6 +6542,21 @@ AIChat.styles = css`
6261
6542
  }
6262
6543
  }
6263
6544
 
6545
+ /* Desktop responsive styles - reduce excessive margins on large screens */
6546
+ @media (min-width: 1025px) {
6547
+ .message.user {
6548
+ margin-left: 2rem;
6549
+ }
6550
+
6551
+ .message.assistant {
6552
+ margin-right: 1.5rem;
6553
+ }
6554
+
6555
+ .contact-support-wrapper {
6556
+ margin-left: 1.5rem;
6557
+ }
6558
+ }
6559
+
6264
6560
  /* Mobile responsive styles for all modes */
6265
6561
  @media (max-width: 768px) {
6266
6562
  .contact-support-wrapper {
@@ -6304,7 +6600,7 @@ AIChat.styles = css`
6304
6600
 
6305
6601
  .message-content {
6306
6602
  max-width: 100%;
6307
- font-size: 0.9375rem;
6603
+ font-size: 1.2rem;
6308
6604
  padding: 0.875rem 1.25rem;
6309
6605
  }
6310
6606
 
@@ -6460,8 +6756,8 @@ AIChat.styles = css`
6460
6756
  }
6461
6757
 
6462
6758
  .header-avatar {
6463
- width: 2.5rem;
6464
- height: 2.5rem;
6759
+ width: 3.5rem;
6760
+ height: 3.5rem;
6465
6761
  border-radius: 50%;
6466
6762
  background: #fff;
6467
6763
  display: flex;
@@ -6610,8 +6906,8 @@ AIChat.styles = css`
6610
6906
  }
6611
6907
 
6612
6908
  .avatar {
6613
- width: 2.5rem;
6614
- height: 2.5rem;
6909
+ width: 3.5rem;
6910
+ height: 3.5rem;
6615
6911
  border-radius: 9999px;
6616
6912
  background: #E5E7EB;
6617
6913
  display: flex;
@@ -6637,12 +6933,13 @@ AIChat.styles = css`
6637
6933
 
6638
6934
  .message-content {
6639
6935
  max-width: 36rem;
6640
- padding: 1rem 1.5rem;
6641
6936
  border-radius: 1.25rem;
6642
6937
  line-height: 1.6;
6643
6938
  overflow-wrap: break-word;
6644
6939
  word-wrap: break-word;
6645
6940
  min-width: 0;
6941
+ font-size: 1.2rem;
6942
+ padding: 1rem 1.5rem;
6646
6943
  }
6647
6944
 
6648
6945
  .message.user .message-content {
@@ -6784,7 +7081,7 @@ AIChat.styles = css`
6784
7081
  }
6785
7082
 
6786
7083
  .faq-title {
6787
- font-size: 0.875rem;
7084
+ font-size: 1.1rem;
6788
7085
  font-weight: 600;
6789
7086
  color: var(--primary-color, #3681D3);
6790
7087
  margin: 0 0 0.375rem 0;
@@ -6804,7 +7101,7 @@ AIChat.styles = css`
6804
7101
  }
6805
7102
 
6806
7103
  .faq-item {
6807
- font-size: 0.875rem;
7104
+ font-size: 1.2rem;
6808
7105
  color: var(--primary-color, #3681D3);
6809
7106
  padding: 0;
6810
7107
  border-radius: 0.5rem;
@@ -6960,7 +7257,7 @@ AIChat.styles = css`
6960
7257
  padding: 0 1rem;
6961
7258
  border: 1px solid #d1d5db;
6962
7259
  border-radius: 1.5rem;
6963
- font-size: 0.9375rem;
7260
+ font-size: 1.2rem;
6964
7261
  font-family: inherit;
6965
7262
  background: #fff;
6966
7263
  color: #374151;