@salesforcedevs/docs-components 0.0.38-chat → 0.0.39-chat

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforcedevs/docs-components",
3
- "version": "0.0.38-chat",
3
+ "version": "0.0.39-chat",
4
4
  "description": "Docs Lightning web components for DSC",
5
5
  "license": "MIT",
6
6
  "main": "index.js",
@@ -137,7 +137,7 @@ body.chat-closed .global-header {
137
137
  /* Chat container */
138
138
  .chat-container {
139
139
  height: 100vh;
140
- width: 400px;
140
+ width: 700px;
141
141
  background-color: #fff;
142
142
  box-shadow: -8px 0 32px rgb(0 0 0 / 12%), -2px 0 8px rgb(0 0 0 / 8%);
143
143
  transform: translateX(100%);
@@ -176,7 +176,11 @@
176
176
  </template>
177
177
  <template lwc:else>
178
178
  <template lwc:if={message.isHTML}>
179
- <div class="message-text html-content" lwc:dom="manual" data-message-id={message.id}></div>
179
+ <div
180
+ class="message-text html-content"
181
+ lwc:dom="manual"
182
+ data-message-id={message.id}
183
+ ></div>
180
184
  </template>
181
185
  <template lwc:else>
182
186
  <p class="message-text">{message.text}</p>
@@ -55,11 +55,14 @@ export default class Chat extends LightningElement {
55
55
  private messageIdCounter: number = 0;
56
56
 
57
57
  // API Configuration
58
- private static readonly API_URL = "https://276ca7264b79.ngrok-free.app/api/llm/search";
58
+ private static readonly API_URL =
59
+ "https://276ca7264b79.ngrok-free.app/api/llm/search";
59
60
  private static readonly MAX_RESULTS = 5;
60
-
61
+
61
62
  // Development mode flag - set to true if running in development
62
- private static readonly IS_DEVELOPMENT = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
63
+ private static readonly IS_DEVELOPMENT =
64
+ window.location.hostname === "localhost" ||
65
+ window.location.hostname === "127.0.0.1";
63
66
 
64
67
  // localStorage keys for persisting messages and open state
65
68
  private static readonly STORAGE_KEY = "doc-chat-messages";
@@ -93,24 +96,31 @@ export default class Chat extends LightningElement {
93
96
  }
94
97
 
95
98
  private renderHTMLMessages() {
96
- console.log('=== renderHTMLMessages called ===');
97
- console.log('All messages:', this.messages);
98
- console.log('HTML messages:', this.messages.filter(msg => msg.isHTML));
99
-
99
+ console.log("=== renderHTMLMessages called ===");
100
+ console.log("All messages:", this.messages);
101
+ console.log(
102
+ "HTML messages:",
103
+ this.messages.filter((msg) => msg.isHTML)
104
+ );
105
+
100
106
  // Use a simpler selector approach
101
- const htmlMessageElements = this.template.querySelectorAll('.html-content[data-message-id]');
102
- console.log('Found HTML elements:', htmlMessageElements.length);
103
-
107
+ const htmlMessageElements = this.template.querySelectorAll(
108
+ ".html-content[data-message-id]"
109
+ );
110
+ console.log("Found HTML elements:", htmlMessageElements.length);
111
+
104
112
  htmlMessageElements.forEach((element) => {
105
- const messageId = element.getAttribute('data-message-id');
106
- console.log('Processing element with ID:', messageId);
107
-
108
- const message = this.messages.find(msg => msg.id === messageId && msg.isHTML);
109
- console.log('Found message:', message);
110
-
113
+ const messageId = element.getAttribute("data-message-id");
114
+ console.log("Processing element with ID:", messageId);
115
+
116
+ const message = this.messages.find(
117
+ (msg) => msg.id === messageId && msg.isHTML
118
+ );
119
+ console.log("Found message:", message);
120
+
111
121
  if (message) {
112
- console.log('Setting innerHTML for message:', messageId);
113
- console.log('Content:', message.text);
122
+ console.log("Setting innerHTML for message:", messageId);
123
+ console.log("Content:", message.text);
114
124
  element.innerHTML = message.text;
115
125
  }
116
126
  });
@@ -146,11 +156,17 @@ export default class Chat extends LightningElement {
146
156
 
147
157
  // Helper method to get HTML content for a message
148
158
  getMessageHTML(messageId: string): string {
149
- const message = this.messages.find(msg => msg.id === messageId && msg.isHTML);
150
- return message ? message.text : '';
159
+ const message = this.messages.find(
160
+ (msg) => msg.id === messageId && msg.isHTML
161
+ );
162
+ return message ? message.text : "";
151
163
  }
152
164
 
153
- private addMessage(text: string, sender: "user" | "assistant", isHTML: boolean = false) {
165
+ private addMessage(
166
+ text: string,
167
+ sender: "user" | "assistant",
168
+ isHTML: boolean = false
169
+ ) {
154
170
  const timestamp = new Date();
155
171
  const message: ChatMessage = {
156
172
  id: `msg-${this.messageIdCounter++}`,
@@ -323,7 +339,7 @@ export default class Chat extends LightningElement {
323
339
  private async callChatAPI(userMessage: string): Promise<string> {
324
340
  try {
325
341
  // Format chat history for API (exclude the current user message)
326
- const chatHistory = this.messages.map(msg => ({
342
+ const chatHistory = this.messages.map((msg) => ({
327
343
  text: msg.text,
328
344
  sender: msg.sender
329
345
  }));
@@ -334,71 +350,93 @@ export default class Chat extends LightningElement {
334
350
  chatHistory: chatHistory
335
351
  };
336
352
 
337
- console.log('Sending API request:', requestBody);
353
+ console.log("Sending API request:", requestBody);
338
354
 
339
355
  const response = await fetch(Chat.API_URL, {
340
- method: 'POST',
356
+ method: "POST",
341
357
  headers: {
342
- 'Content-Type': 'application/json',
343
- 'ngrok-skip-browser-warning': 'true', // Skip ngrok browser warning
358
+ "Content-Type": "application/json",
359
+ "ngrok-skip-browser-warning": "true" // Skip ngrok browser warning
344
360
  },
345
361
  body: JSON.stringify(requestBody)
346
362
  });
347
363
 
348
364
  if (!response.ok) {
349
365
  const errorText = await response.text();
350
- console.error('API Error Response:', errorText);
351
- throw new Error(`API request failed with status: ${response.status}`);
366
+ console.error("API Error Response:", errorText);
367
+ throw new Error(
368
+ `API request failed with status: ${response.status}`
369
+ );
352
370
  }
353
371
 
354
372
  const data = await response.json();
355
- console.log('API Response:', data);
356
-
373
+ console.log("API Response:", data);
374
+
357
375
  // Handle the new API response format with "data" field
358
- const responseText = data.data || data.answer || data.text || data.response || data.result || data.message;
359
- console.log('Response Text:', responseText);
360
-
376
+ const responseText =
377
+ data.data ||
378
+ data.answer ||
379
+ data.text ||
380
+ data.response ||
381
+ data.result ||
382
+ data.message;
383
+ console.log("Response Text:", responseText);
384
+
361
385
  if (!responseText) {
362
- console.warn('No response text found in API response:', data);
363
- return 'I received your message but couldn\'t generate a proper response.';
386
+ console.warn("No response text found in API response:", data);
387
+ return "I received your message but couldn't generate a proper response.";
364
388
  }
365
-
389
+
366
390
  return responseText;
367
-
368
391
  } catch (error) {
369
- console.error('Chat API error:', error);
370
-
392
+ console.error("Chat API error:", error);
393
+
371
394
  // Provide more specific error messages for CORS issues
372
- if (error instanceof TypeError && error.message.includes('fetch')) {
373
- return 'I\'m having trouble connecting to the service. This might be a CORS configuration issue. Please check your internet connection and try again.';
395
+ if (error instanceof TypeError && error.message.includes("fetch")) {
396
+ return "I'm having trouble connecting to the service. This might be a CORS configuration issue. Please check your internet connection and try again.";
374
397
  }
375
-
376
- if (error instanceof Error && (error.message.includes('CORS') || error.message.includes('cross-origin'))) {
377
- return 'I\'m having trouble connecting due to browser security restrictions. Please ask your administrator to configure CORS headers on the backend API.';
398
+
399
+ if (
400
+ error instanceof Error &&
401
+ (error.message.includes("CORS") ||
402
+ error.message.includes("cross-origin"))
403
+ ) {
404
+ return "I'm having trouble connecting due to browser security restrictions. Please ask your administrator to configure CORS headers on the backend API.";
378
405
  }
379
-
380
- return 'I apologize, but I\'m having trouble connecting to the service right now. Please try again later.';
406
+
407
+ return "I apologize, but I'm having trouble connecting to the service right now. Please try again later.";
381
408
  }
382
409
  }
383
410
 
384
411
  private parseMarkdownToHTML(text: string): string {
385
412
  // Convert markdown-like text to HTML
386
413
  let html = text;
387
-
388
- console.log('Original text:', text);
389
-
414
+
415
+ console.log("Original text:", text);
416
+
390
417
  // First, convert markdown links [text](url) to HTML links
391
- html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer" class="chat-link">$1</a>');
392
-
418
+ // Make sure links are absolute (relative to root) by adding leading slash if missing
419
+ html = html.replace(
420
+ /\[([^\]]+)\]\(([^)]+)\)/g,
421
+ (match, linkText, url) => {
422
+ // If URL doesn't start with http, https, or /, make it absolute
423
+ let absoluteUrl = url;
424
+ if (!url.startsWith('http') && !url.startsWith('/')) {
425
+ absoluteUrl = '/' + url;
426
+ }
427
+ return `<a href="${absoluteUrl}" target="_blank" rel="noopener noreferrer" class="chat-link">${linkText}</a>`;
428
+ }
429
+ );
430
+
393
431
  // Handle numbered lists with descriptions (more complex pattern)
394
432
  // Split into lines to process each line
395
- const lines = html.split('\n');
433
+ const lines = html.split("\n");
396
434
  const processedLines = [];
397
435
  let inList = false;
398
-
436
+
399
437
  for (let i = 0; i < lines.length; i++) {
400
438
  const line = lines[i];
401
-
439
+
402
440
  // Check if this is a numbered list item (starts with number and dot)
403
441
  if (/^\d+\.\s+/.test(line)) {
404
442
  if (!inList) {
@@ -406,50 +444,52 @@ export default class Chat extends LightningElement {
406
444
  inList = true;
407
445
  }
408
446
  // Extract the content after the number
409
- const content = line.replace(/^\d+\.\s+/, '');
447
+ const content = line.replace(/^\d+\.\s+/, "");
410
448
  processedLines.push(`<li>${content}`);
411
-
449
+
412
450
  // Check if next line is indented (description)
413
451
  if (i + 1 < lines.length && /^\s{2,}/.test(lines[i + 1])) {
414
452
  i++; // Skip to next line
415
453
  const description = lines[i].trim();
416
- processedLines.push(`<br><span class="chat-description">${description}</span></li>`);
454
+ processedLines.push(
455
+ `<br><span class="chat-description">${description}</span></li>`
456
+ );
417
457
  } else {
418
- processedLines.push('</li>');
458
+ processedLines.push("</li>");
419
459
  }
420
- } else if (line.trim() === '' && inList) {
460
+ } else if (line.trim() === "" && inList) {
421
461
  // Empty line might end the list
422
462
  continue;
423
463
  } else {
424
- if (inList && line.trim() !== '') {
425
- processedLines.push('</ol>');
464
+ if (inList && line.trim() !== "") {
465
+ processedLines.push("</ol>");
426
466
  inList = false;
427
467
  }
428
- if (line.trim() !== '') {
468
+ if (line.trim() !== "") {
429
469
  processedLines.push(line);
430
470
  }
431
471
  }
432
472
  }
433
-
473
+
434
474
  // Close any open list
435
475
  if (inList) {
436
- processedLines.push('</ol>');
476
+ processedLines.push("</ol>");
437
477
  }
438
-
439
- html = processedLines.join('<br>');
440
-
478
+
479
+ html = processedLines.join("<br>");
480
+
441
481
  // Clean up extra breaks
442
- html = html.replace(/<br><br>/g, '<br>');
443
- html = html.replace(/^<br>/, '');
444
-
482
+ html = html.replace(/<br><br>/g, "<br>");
483
+ html = html.replace(/^<br>/, "");
484
+
445
485
  // Convert **bold** to <strong>
446
- html = html.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
447
-
486
+ html = html.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
487
+
448
488
  // Convert *italic* to <em>
449
- html = html.replace(/\*([^*]+)\*/g, '<em>$1</em>');
450
-
451
- console.log('Processed HTML:', html);
452
-
489
+ html = html.replace(/\*([^*]+)\*/g, "<em>$1</em>");
490
+
491
+ console.log("Processed HTML:", html);
492
+
453
493
  return html;
454
494
  }
455
495
 
@@ -459,15 +499,15 @@ export default class Chat extends LightningElement {
459
499
  // Convert markdown to HTML for rich content display
460
500
  const htmlContent = this.parseMarkdownToHTML(response);
461
501
  this.addMessage(htmlContent, "assistant", true); // true indicates HTML content
462
-
502
+
463
503
  // Force a re-render after adding the message
464
504
  setTimeout(() => {
465
505
  this.renderHTMLMessages();
466
506
  }, 100);
467
507
  } catch (error) {
468
- console.error('Error getting assistant response:', error);
508
+ console.error("Error getting assistant response:", error);
469
509
  this.addMessage(
470
- 'I apologize, but I encountered an error while processing your request. Please try again.',
510
+ "I apologize, but I encountered an error while processing your request. Please try again.",
471
511
  "assistant"
472
512
  );
473
513
  } finally {