@salesforcedevs/docs-components 0.0.37-chat → 0.0.38-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
|
@@ -429,6 +429,68 @@ body.chat-closed .global-header {
|
|
|
429
429
|
font-weight: 400;
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
+
/* HTML content styling for rich messages */
|
|
433
|
+
.message-text .chat-link {
|
|
434
|
+
color: #0176d3;
|
|
435
|
+
text-decoration: none;
|
|
436
|
+
border-bottom: 1px solid transparent;
|
|
437
|
+
transition: all 0.2s ease;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.message-text .chat-link:hover {
|
|
441
|
+
color: #005fb2;
|
|
442
|
+
border-bottom-color: #005fb2;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.message-text .chat-list {
|
|
446
|
+
margin: 12px 0;
|
|
447
|
+
padding-left: 20px;
|
|
448
|
+
counter-reset: none;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.message-text .chat-list li {
|
|
452
|
+
margin: 12px 0;
|
|
453
|
+
line-height: 1.6;
|
|
454
|
+
font-weight: 500;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
.message-text .chat-description {
|
|
458
|
+
font-weight: 400;
|
|
459
|
+
color: #666;
|
|
460
|
+
font-size: 14px;
|
|
461
|
+
line-height: 1.5;
|
|
462
|
+
margin-top: 4px;
|
|
463
|
+
display: block;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.message-text strong {
|
|
467
|
+
font-weight: 600;
|
|
468
|
+
color: #181818;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.message-text em {
|
|
472
|
+
font-style: italic;
|
|
473
|
+
color: #555;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/* Style for line breaks in HTML content */
|
|
477
|
+
.message-text br {
|
|
478
|
+
margin: 4px 0;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/* Ensure proper spacing for HTML content */
|
|
482
|
+
.message-text p {
|
|
483
|
+
margin: 8px 0;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
.message-text p:first-child {
|
|
487
|
+
margin-top: 0;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.message-text p:last-child {
|
|
491
|
+
margin-bottom: 0;
|
|
492
|
+
}
|
|
493
|
+
|
|
432
494
|
.message-timestamp {
|
|
433
495
|
font-size: 12px;
|
|
434
496
|
color: #706e6b;
|
|
@@ -175,7 +175,12 @@
|
|
|
175
175
|
</div>
|
|
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>
|
|
180
|
+
</template>
|
|
181
|
+
<template lwc:else>
|
|
182
|
+
<p class="message-text">{message.text}</p>
|
|
183
|
+
</template>
|
|
179
184
|
</template>
|
|
180
185
|
</div>
|
|
181
186
|
<template lwc:if={showTimestamp}>
|
|
@@ -9,6 +9,7 @@ interface ChatMessage {
|
|
|
9
9
|
sender: "user" | "assistant";
|
|
10
10
|
isTyping?: boolean;
|
|
11
11
|
formattedTime?: string;
|
|
12
|
+
isHTML?: boolean; // Flag to indicate if content should be rendered as HTML
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export default class Chat extends LightningElement {
|
|
@@ -86,6 +87,35 @@ export default class Chat extends LightningElement {
|
|
|
86
87
|
document.body.classList.remove("chat-open", "chat-closed");
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
renderedCallback() {
|
|
91
|
+
// Handle HTML content rendering for messages with isHTML flag
|
|
92
|
+
this.renderHTMLMessages();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
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
|
+
|
|
100
|
+
// 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
|
+
|
|
104
|
+
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
|
+
|
|
111
|
+
if (message) {
|
|
112
|
+
console.log('Setting innerHTML for message:', messageId);
|
|
113
|
+
console.log('Content:', message.text);
|
|
114
|
+
element.innerHTML = message.text;
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
89
119
|
get chatContainerClass() {
|
|
90
120
|
return cx(
|
|
91
121
|
"chat-container",
|
|
@@ -114,14 +144,21 @@ export default class Chat extends LightningElement {
|
|
|
114
144
|
return messages;
|
|
115
145
|
}
|
|
116
146
|
|
|
117
|
-
|
|
147
|
+
// Helper method to get HTML content for a message
|
|
148
|
+
getMessageHTML(messageId: string): string {
|
|
149
|
+
const message = this.messages.find(msg => msg.id === messageId && msg.isHTML);
|
|
150
|
+
return message ? message.text : '';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private addMessage(text: string, sender: "user" | "assistant", isHTML: boolean = false) {
|
|
118
154
|
const timestamp = new Date();
|
|
119
155
|
const message: ChatMessage = {
|
|
120
156
|
id: `msg-${this.messageIdCounter++}`,
|
|
121
157
|
text,
|
|
122
158
|
timestamp,
|
|
123
159
|
sender,
|
|
124
|
-
formattedTime: this.formatTimestamp(timestamp)
|
|
160
|
+
formattedTime: this.formatTimestamp(timestamp),
|
|
161
|
+
isHTML
|
|
125
162
|
};
|
|
126
163
|
this.messages = [...this.messages, message];
|
|
127
164
|
this.saveMessages();
|
|
@@ -157,7 +194,8 @@ export default class Chat extends LightningElement {
|
|
|
157
194
|
text: msg.text,
|
|
158
195
|
timestamp: msg.timestamp.toISOString(),
|
|
159
196
|
sender: msg.sender,
|
|
160
|
-
formattedTime: msg.formattedTime
|
|
197
|
+
formattedTime: msg.formattedTime,
|
|
198
|
+
isHTML: msg.isHTML || false
|
|
161
199
|
}));
|
|
162
200
|
localStorage.setItem(
|
|
163
201
|
Chat.STORAGE_KEY,
|
|
@@ -178,7 +216,8 @@ export default class Chat extends LightningElement {
|
|
|
178
216
|
text: msg.text,
|
|
179
217
|
timestamp: new Date(msg.timestamp),
|
|
180
218
|
sender: msg.sender,
|
|
181
|
-
formattedTime: msg.formattedTime
|
|
219
|
+
formattedTime: msg.formattedTime,
|
|
220
|
+
isHTML: msg.isHTML || false
|
|
182
221
|
}));
|
|
183
222
|
|
|
184
223
|
// Update message counter to avoid ID conflicts
|
|
@@ -299,11 +338,9 @@ export default class Chat extends LightningElement {
|
|
|
299
338
|
|
|
300
339
|
const response = await fetch(Chat.API_URL, {
|
|
301
340
|
method: 'POST',
|
|
302
|
-
mode: 'cors', // Explicitly set CORS mode
|
|
303
341
|
headers: {
|
|
304
342
|
'Content-Type': 'application/json',
|
|
305
343
|
'ngrok-skip-browser-warning': 'true', // Skip ngrok browser warning
|
|
306
|
-
...(Chat.IS_DEVELOPMENT && { 'Access-Control-Allow-Origin': '*' })
|
|
307
344
|
},
|
|
308
345
|
body: JSON.stringify(requestBody)
|
|
309
346
|
});
|
|
@@ -317,8 +354,9 @@ export default class Chat extends LightningElement {
|
|
|
317
354
|
const data = await response.json();
|
|
318
355
|
console.log('API Response:', data);
|
|
319
356
|
|
|
320
|
-
// Handle
|
|
321
|
-
const responseText = data.answer || data.text || data.response || data.result || data.message;
|
|
357
|
+
// 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);
|
|
322
360
|
|
|
323
361
|
if (!responseText) {
|
|
324
362
|
console.warn('No response text found in API response:', data);
|
|
@@ -343,10 +381,89 @@ export default class Chat extends LightningElement {
|
|
|
343
381
|
}
|
|
344
382
|
}
|
|
345
383
|
|
|
384
|
+
private parseMarkdownToHTML(text: string): string {
|
|
385
|
+
// Convert markdown-like text to HTML
|
|
386
|
+
let html = text;
|
|
387
|
+
|
|
388
|
+
console.log('Original text:', text);
|
|
389
|
+
|
|
390
|
+
// 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
|
+
|
|
393
|
+
// Handle numbered lists with descriptions (more complex pattern)
|
|
394
|
+
// Split into lines to process each line
|
|
395
|
+
const lines = html.split('\n');
|
|
396
|
+
const processedLines = [];
|
|
397
|
+
let inList = false;
|
|
398
|
+
|
|
399
|
+
for (let i = 0; i < lines.length; i++) {
|
|
400
|
+
const line = lines[i];
|
|
401
|
+
|
|
402
|
+
// Check if this is a numbered list item (starts with number and dot)
|
|
403
|
+
if (/^\d+\.\s+/.test(line)) {
|
|
404
|
+
if (!inList) {
|
|
405
|
+
processedLines.push('<ol class="chat-list">');
|
|
406
|
+
inList = true;
|
|
407
|
+
}
|
|
408
|
+
// Extract the content after the number
|
|
409
|
+
const content = line.replace(/^\d+\.\s+/, '');
|
|
410
|
+
processedLines.push(`<li>${content}`);
|
|
411
|
+
|
|
412
|
+
// Check if next line is indented (description)
|
|
413
|
+
if (i + 1 < lines.length && /^\s{2,}/.test(lines[i + 1])) {
|
|
414
|
+
i++; // Skip to next line
|
|
415
|
+
const description = lines[i].trim();
|
|
416
|
+
processedLines.push(`<br><span class="chat-description">${description}</span></li>`);
|
|
417
|
+
} else {
|
|
418
|
+
processedLines.push('</li>');
|
|
419
|
+
}
|
|
420
|
+
} else if (line.trim() === '' && inList) {
|
|
421
|
+
// Empty line might end the list
|
|
422
|
+
continue;
|
|
423
|
+
} else {
|
|
424
|
+
if (inList && line.trim() !== '') {
|
|
425
|
+
processedLines.push('</ol>');
|
|
426
|
+
inList = false;
|
|
427
|
+
}
|
|
428
|
+
if (line.trim() !== '') {
|
|
429
|
+
processedLines.push(line);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Close any open list
|
|
435
|
+
if (inList) {
|
|
436
|
+
processedLines.push('</ol>');
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
html = processedLines.join('<br>');
|
|
440
|
+
|
|
441
|
+
// Clean up extra breaks
|
|
442
|
+
html = html.replace(/<br><br>/g, '<br>');
|
|
443
|
+
html = html.replace(/^<br>/, '');
|
|
444
|
+
|
|
445
|
+
// Convert **bold** to <strong>
|
|
446
|
+
html = html.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
|
447
|
+
|
|
448
|
+
// Convert *italic* to <em>
|
|
449
|
+
html = html.replace(/\*([^*]+)\*/g, '<em>$1</em>');
|
|
450
|
+
|
|
451
|
+
console.log('Processed HTML:', html);
|
|
452
|
+
|
|
453
|
+
return html;
|
|
454
|
+
}
|
|
455
|
+
|
|
346
456
|
private async getAssistantResponse(userMessage: string) {
|
|
347
457
|
try {
|
|
348
458
|
const response = await this.callChatAPI(userMessage);
|
|
349
|
-
|
|
459
|
+
// Convert markdown to HTML for rich content display
|
|
460
|
+
const htmlContent = this.parseMarkdownToHTML(response);
|
|
461
|
+
this.addMessage(htmlContent, "assistant", true); // true indicates HTML content
|
|
462
|
+
|
|
463
|
+
// Force a re-render after adding the message
|
|
464
|
+
setTimeout(() => {
|
|
465
|
+
this.renderHTMLMessages();
|
|
466
|
+
}, 100);
|
|
350
467
|
} catch (error) {
|
|
351
468
|
console.error('Error getting assistant response:', error);
|
|
352
469
|
this.addMessage(
|