@salesforcedevs/docs-components 0.0.35-chat → 0.0.37-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.35-chat",
3
+ "version": "0.0.37-chat",
4
4
  "description": "Docs Lightning web components for DSC",
5
5
  "license": "MIT",
6
6
  "main": "index.js",
@@ -5,7 +5,9 @@
5
5
  right: 0;
6
6
  height: 100vh;
7
7
  z-index: 100000;
8
- font-family: "Salesforce Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
8
+ font-family: "Salesforce Sans", -apple-system, BlinkMacSystemFont,
9
+ "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue",
10
+ sans-serif;
9
11
  }
10
12
 
11
13
  /* Apply content shift to main page content when chat is open */
@@ -50,6 +52,59 @@ body.chat-closed .global-header {
50
52
  padding: 0;
51
53
  }
52
54
 
55
+ /* Tooltip styling */
56
+ .chat-trigger-button::before {
57
+ content: attr(data-tooltip);
58
+ position: absolute;
59
+ right: 100%;
60
+ top: 50%;
61
+ transform: translateY(-50%);
62
+ background: linear-gradient(135deg, #0176d3 0%, #005fb2 100%);
63
+ color: white;
64
+ padding: 12px 16px;
65
+ border-radius: 12px;
66
+ font-size: 14px;
67
+ font-weight: 500;
68
+ white-space: nowrap;
69
+ opacity: 0;
70
+ visibility: hidden;
71
+ margin-right: 16px;
72
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
73
+ box-shadow: 0 8px 24px rgb(1 118 211 / 25%), 0 2px 8px rgb(1 118 211 / 15%);
74
+ backdrop-filter: blur(10px);
75
+ z-index: 1002;
76
+ pointer-events: none;
77
+ letter-spacing: 0.02em;
78
+ }
79
+
80
+ /* Tooltip arrow */
81
+ .chat-trigger-button::after {
82
+ content: "";
83
+ position: absolute;
84
+ right: 100%;
85
+ top: 50%;
86
+ transform: translateY(-50%);
87
+ margin-right: 8px;
88
+ width: 0;
89
+ height: 0;
90
+ border-left: 8px solid #0176d3;
91
+ border-top: 8px solid transparent;
92
+ border-bottom: 8px solid transparent;
93
+ opacity: 0;
94
+ visibility: hidden;
95
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
96
+ z-index: 1002;
97
+ pointer-events: none;
98
+ }
99
+
100
+ /* Show tooltip on hover */
101
+ .chat-trigger-button:hover::before,
102
+ .chat-trigger-button:hover::after {
103
+ opacity: 1;
104
+ visibility: visible;
105
+ transform: translateY(-50%) translateX(-4px);
106
+ }
107
+
53
108
  .chat-trigger-button:hover .chat-gif {
54
109
  transform: scale(1.1);
55
110
  }
@@ -65,8 +120,6 @@ body.chat-closed .global-header {
65
120
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
66
121
  }
67
122
 
68
-
69
-
70
123
  @keyframes pulse {
71
124
  0% {
72
125
  transform: scale(1);
@@ -123,7 +176,11 @@ body.chat-closed .global-header {
123
176
  left: 0;
124
177
  right: 0;
125
178
  bottom: 0;
126
- background: linear-gradient(135deg, rgb(255 255 255 / 5%) 0%, rgb(255 255 255 / 1%) 100%);
179
+ background: linear-gradient(
180
+ 135deg,
181
+ rgb(255 255 255 / 5%) 0%,
182
+ rgb(255 255 255 / 1%) 100%
183
+ );
127
184
  pointer-events: none;
128
185
  }
129
186
 
@@ -240,7 +297,11 @@ body.chat-closed .global-header {
240
297
  content: "";
241
298
  position: absolute;
242
299
  inset: 0;
243
- background: linear-gradient(135deg, rgb(255 255 255 / 20%) 0%, rgb(255 255 255 / 10%) 100%);
300
+ background: linear-gradient(
301
+ 135deg,
302
+ rgb(255 255 255 / 20%) 0%,
303
+ rgb(255 255 255 / 10%) 100%
304
+ );
244
305
  border-radius: 50%;
245
306
  }
246
307
 
@@ -269,56 +330,61 @@ body.chat-closed .global-header {
269
330
  }
270
331
 
271
332
  @keyframes thinking-avatar-pulse {
272
- 0%, 100% {
273
- transform: scale(1);
333
+ 0%,
334
+ 100% {
335
+ transform: scale(1);
274
336
  box-shadow: 0 2px 8px rgb(0 0 0 / 10%);
275
337
  }
276
338
 
277
- 50% {
278
- transform: scale(1.08);
339
+ 50% {
340
+ transform: scale(1.08);
279
341
  box-shadow: 0 4px 16px rgb(0 0 0 / 20%);
280
342
  }
281
343
  }
282
344
 
283
345
  @keyframes thinking-breathe {
284
- 0%, 100% {
285
- transform: scale(1);
346
+ 0%,
347
+ 100% {
348
+ transform: scale(1);
286
349
  }
287
350
 
288
- 50% {
289
- transform: scale(1.05);
351
+ 50% {
352
+ transform: scale(1.05);
290
353
  }
291
354
  }
292
355
 
293
356
  @keyframes thinking-smile-animation {
294
- 0%, 100% {
295
- transform: translateY(0);
357
+ 0%,
358
+ 100% {
359
+ transform: translateY(0);
296
360
  }
297
361
 
298
- 50% {
299
- transform: translateY(-2px);
362
+ 50% {
363
+ transform: translateY(-2px);
300
364
  }
301
365
  }
302
366
 
303
367
  @keyframes think-blink {
304
- 0%, 85%, 100% {
305
- transform: scaleY(1);
368
+ 0%,
369
+ 85%,
370
+ 100% {
371
+ transform: scaleY(1);
306
372
  }
307
373
 
308
- 90% {
309
- transform: scaleY(0.6);
374
+ 90% {
375
+ transform: scaleY(0.6);
310
376
  }
311
377
 
312
- 92% {
313
- transform: scaleY(0.2);
378
+ 92% {
379
+ transform: scaleY(0.2);
314
380
  }
315
381
 
316
- 94% {
317
- transform: scaleY(0.6);
382
+ 94% {
383
+ transform: scaleY(0.6);
318
384
  }
319
385
 
320
- 96% {
321
- transform: scaleY(1);
386
+ 96% {
387
+ transform: scaleY(1);
322
388
  }
323
389
  }
324
390
 
@@ -510,40 +576,52 @@ body.chat-closed .global-header {
510
576
  bottom: 16px;
511
577
  right: 16px;
512
578
  }
513
-
579
+
514
580
  .chat-gif {
515
581
  width: 56px;
516
582
  height: 56px;
517
583
  }
518
-
519
584
 
520
-
585
+ /* Adjust tooltip for mobile */
586
+ .chat-trigger-button::before {
587
+ font-size: 13px;
588
+ padding: 10px 14px;
589
+ margin-right: 12px;
590
+ }
591
+
592
+ .chat-trigger-button::after {
593
+ margin-right: 6px;
594
+ border-left-width: 6px;
595
+ border-top-width: 6px;
596
+ border-bottom-width: 6px;
597
+ }
598
+
521
599
  .chat-container {
522
600
  width: 100%;
523
601
  right: 0;
524
602
  transform: translateX(100%);
525
603
  }
526
-
604
+
527
605
  .chat-container_open {
528
606
  transform: translateX(0);
529
607
  }
530
-
608
+
531
609
  .chat-header {
532
610
  padding: 16px 20px;
533
611
  }
534
-
612
+
535
613
  .chat-title {
536
614
  font-size: 18px;
537
615
  }
538
-
616
+
539
617
  .chat-messages {
540
618
  padding: 20px 16px;
541
619
  }
542
-
620
+
543
621
  .chat-input-area {
544
622
  padding: 16px 20px;
545
623
  }
546
-
624
+
547
625
  .message-content {
548
626
  max-width: 85%;
549
627
  }
@@ -574,7 +652,20 @@ body.chat-closed .global-header {
574
652
  height: 48px;
575
653
  }
576
654
 
655
+ /* Smaller tooltip for small screens */
656
+ .chat-trigger-button::before {
657
+ font-size: 12px;
658
+ padding: 8px 12px;
659
+ margin-right: 10px;
660
+ border-radius: 8px;
661
+ }
577
662
 
663
+ .chat-trigger-button::after {
664
+ margin-right: 4px;
665
+ border-left-width: 5px;
666
+ border-top-width: 5px;
667
+ border-bottom-width: 5px;
668
+ }
578
669
 
579
670
  .chat-container {
580
671
  width: 100%;
@@ -638,6 +729,12 @@ body.chat-closed .global-header {
638
729
  .thinking-eye {
639
730
  animation: none !important;
640
731
  }
732
+
733
+ /* Disable tooltip animations for reduced motion users */
734
+ .chat-trigger-button::before,
735
+ .chat-trigger-button::after {
736
+ transition: none;
737
+ }
641
738
  }
642
739
 
643
740
  /* High contrast mode support */
@@ -5,10 +5,11 @@
5
5
  class="chat-trigger-button"
6
6
  onclick={handleOpenClick}
7
7
  aria-label="Open chat"
8
+ data-tooltip="Chat with us!"
8
9
  >
9
10
  <img
10
11
  class="chat-gif"
11
- src="/hi_bot.gif"
12
+ src="https://a.sfdcstatic.com/developer-website/sfdocs/hack-smart-agent/hi_bot_bg.gif"
12
13
  alt="Hi Bot"
13
14
  />
14
15
  <!-- <span class="chat-trigger-text">Chat</span> -->
@@ -70,7 +71,10 @@
70
71
  data-sender={message.sender}
71
72
  >
72
73
  <!-- AI Avatar for assistant messages only -->
73
- <div class="message-avatar-wrapper" data-sender={message.sender}>
74
+ <div
75
+ class="message-avatar-wrapper"
76
+ data-sender={message.sender}
77
+ >
74
78
  <div class="avatar-container">
75
79
  <svg
76
80
  class="avatar-icon"
@@ -84,25 +88,78 @@
84
88
  <line x1="230" y1="20" x2="230" y2="60" />
85
89
  </g>
86
90
  <circle cx="70" cy="20" r="10" fill="#3A98D8" />
87
- <circle cx="230" cy="20" r="10" fill="#3A98D8" />
91
+ <circle
92
+ cx="230"
93
+ cy="20"
94
+ r="10"
95
+ fill="#3A98D8"
96
+ />
88
97
 
89
98
  <!-- Robot Body -->
90
99
  <g class="thinking-body">
91
- <rect x="40" y="60" width="220" height="240" rx="110" ry="110" fill="#3A98D8" />
100
+ <rect
101
+ x="40"
102
+ y="60"
103
+ width="220"
104
+ height="240"
105
+ rx="110"
106
+ ry="110"
107
+ fill="#3A98D8"
108
+ />
92
109
 
93
110
  <!-- Forehead Dots -->
94
- <circle cx="150" cy="90" r="6" fill="#9ED4E6" />
95
- <rect x="135" y="100" width="30" height="10" rx="5" ry="5" fill="#9ED4E6" />
111
+ <circle
112
+ cx="150"
113
+ cy="90"
114
+ r="6"
115
+ fill="#9ED4E6"
116
+ />
117
+ <rect
118
+ x="135"
119
+ y="100"
120
+ width="30"
121
+ height="10"
122
+ rx="5"
123
+ ry="5"
124
+ fill="#9ED4E6"
125
+ />
96
126
 
97
127
  <!-- Face Panel -->
98
- <rect x="70" y="130" width="160" height="80" rx="40" ry="40" fill="#577C86" />
128
+ <rect
129
+ x="70"
130
+ y="130"
131
+ width="160"
132
+ height="80"
133
+ rx="40"
134
+ ry="40"
135
+ fill="#577C86"
136
+ />
99
137
 
100
138
  <!-- Eyes (Thinking animation) -->
101
- <circle class="thinking-eye" cx="115" cy="170" r="10" fill="#86D3BD" />
102
- <circle class="thinking-eye" cx="185" cy="170" r="10" fill="#86D3BD" />
139
+ <circle
140
+ class="thinking-eye"
141
+ cx="115"
142
+ cy="170"
143
+ r="10"
144
+ fill="#86D3BD"
145
+ />
146
+ <circle
147
+ class="thinking-eye"
148
+ cx="185"
149
+ cy="170"
150
+ r="10"
151
+ fill="#86D3BD"
152
+ />
103
153
 
104
154
  <!-- Thinking Smile -->
105
- <path class="thinking-smile" d="M110 240 Q150 260 190 240" stroke="#2F435A" stroke-width="6" fill="none" stroke-linecap="round" />
155
+ <path
156
+ class="thinking-smile"
157
+ d="M110 240 Q150 260 190 240"
158
+ stroke="#2F435A"
159
+ stroke-width="6"
160
+ fill="none"
161
+ stroke-linecap="round"
162
+ />
106
163
  </g>
107
164
  </svg>
108
165
  </div>
@@ -53,12 +53,19 @@ export default class Chat extends LightningElement {
53
53
  private _isOpen: boolean = false;
54
54
  private messageIdCounter: number = 0;
55
55
 
56
+ // API Configuration
57
+ private static readonly API_URL = "https://276ca7264b79.ngrok-free.app/api/llm/search";
58
+ private static readonly MAX_RESULTS = 5;
59
+
60
+ // Development mode flag - set to true if running in development
61
+ private static readonly IS_DEVELOPMENT = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
62
+
56
63
  // localStorage keys for persisting messages and open state
57
64
  private static readonly STORAGE_KEY = "doc-chat-messages";
58
65
  private static readonly OPEN_STATE_KEY = "doc-chat-should-open";
59
66
 
60
67
  connectedCallback() {
61
- console.log('---- Connected callback ------');
68
+ console.log("---- Connected callback ------");
62
69
  // Load existing messages from localStorage
63
70
  this.loadMessages();
64
71
 
@@ -264,40 +271,91 @@ export default class Chat extends LightningElement {
264
271
  input.value = "";
265
272
  }
266
273
 
267
- // Simulate assistant typing
274
+ // Show typing indicator
268
275
  this.isAssistantTyping = true;
269
276
 
270
- // Simulate assistant response after a delay
277
+ // Get real assistant response from API
278
+ // Add a small delay to show the typing indicator
271
279
  setTimeout(() => {
272
- this.isAssistantTyping = false;
273
- this.simulateAssistantResponse(userMessage);
274
- }, 1000 + Math.random() * 2000);
280
+ this.getAssistantResponse(userMessage);
281
+ }, 500);
275
282
  }
276
283
 
277
- private simulateAssistantResponse(userMessage: string) {
278
- // Simple response simulation - in a real implementation, this would call an API
279
- let response = `I received your message: "${userMessage}". `;
280
-
281
- if (
282
- userMessage.toLowerCase().includes("hello") ||
283
- userMessage.toLowerCase().includes("hi")
284
- ) {
285
- response = "Hello! Nice to meet you. How can I assist you today?";
286
- } else if (userMessage.toLowerCase().includes("help")) {
287
- response =
288
- "I'm here to help! Feel free to ask me any questions about the documentation or topics you're interested in.";
289
- } else if (
290
- userMessage.toLowerCase().includes("documentation") ||
291
- userMessage.toLowerCase().includes("docs")
292
- ) {
293
- response =
294
- "I can help you navigate the documentation. What specific topic are you looking for?";
295
- } else {
296
- response =
297
- "That's an interesting question. Let me help you with that. Could you provide more details about what you're looking for?";
284
+ private async callChatAPI(userMessage: string): Promise<string> {
285
+ try {
286
+ // Format chat history for API (exclude the current user message)
287
+ const chatHistory = this.messages.map(msg => ({
288
+ text: msg.text,
289
+ sender: msg.sender
290
+ }));
291
+
292
+ const requestBody = {
293
+ query: userMessage,
294
+ maxResults: Chat.MAX_RESULTS,
295
+ chatHistory: chatHistory
296
+ };
297
+
298
+ console.log('Sending API request:', requestBody);
299
+
300
+ const response = await fetch(Chat.API_URL, {
301
+ method: 'POST',
302
+ mode: 'cors', // Explicitly set CORS mode
303
+ headers: {
304
+ 'Content-Type': 'application/json',
305
+ 'ngrok-skip-browser-warning': 'true', // Skip ngrok browser warning
306
+ ...(Chat.IS_DEVELOPMENT && { 'Access-Control-Allow-Origin': '*' })
307
+ },
308
+ body: JSON.stringify(requestBody)
309
+ });
310
+
311
+ if (!response.ok) {
312
+ const errorText = await response.text();
313
+ console.error('API Error Response:', errorText);
314
+ throw new Error(`API request failed with status: ${response.status}`);
315
+ }
316
+
317
+ const data = await response.json();
318
+ console.log('API Response:', data);
319
+
320
+ // Handle different possible response formats
321
+ const responseText = data.answer || data.text || data.response || data.result || data.message;
322
+
323
+ if (!responseText) {
324
+ console.warn('No response text found in API response:', data);
325
+ return 'I received your message but couldn\'t generate a proper response.';
326
+ }
327
+
328
+ return responseText;
329
+
330
+ } catch (error) {
331
+ console.error('Chat API error:', error);
332
+
333
+ // Provide more specific error messages for CORS issues
334
+ if (error instanceof TypeError && error.message.includes('fetch')) {
335
+ return 'I\'m having trouble connecting to the service. This might be a CORS configuration issue. Please check your internet connection and try again.';
336
+ }
337
+
338
+ if (error instanceof Error && (error.message.includes('CORS') || error.message.includes('cross-origin'))) {
339
+ return 'I\'m having trouble connecting due to browser security restrictions. Please ask your administrator to configure CORS headers on the backend API.';
340
+ }
341
+
342
+ return 'I apologize, but I\'m having trouble connecting to the service right now. Please try again later.';
298
343
  }
344
+ }
299
345
 
300
- this.addMessage(response, "assistant");
346
+ private async getAssistantResponse(userMessage: string) {
347
+ try {
348
+ const response = await this.callChatAPI(userMessage);
349
+ this.addMessage(response, "assistant");
350
+ } catch (error) {
351
+ console.error('Error getting assistant response:', error);
352
+ this.addMessage(
353
+ 'I apologize, but I encountered an error while processing your request. Please try again.',
354
+ "assistant"
355
+ );
356
+ } finally {
357
+ this.isAssistantTyping = false;
358
+ }
301
359
  }
302
360
 
303
361
  formatTimestamp(timestamp: Date) {
Binary file