@salesforcedevs/docs-components 0.0.32-chat → 0.0.33-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 +1 -1
- package/src/modules/doc/chat/chat.css +162 -87
- package/src/modules/doc/chat/chat.html +63 -19
- package/src/modules/doc/chat/chat.ts +98 -62
package/package.json
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
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
9
|
}
|
|
9
10
|
|
|
10
11
|
/* Apply content shift to main page content when chat is open */
|
|
@@ -36,26 +37,26 @@ body.chat-closed .global-header {
|
|
|
36
37
|
position: fixed;
|
|
37
38
|
bottom: 20px;
|
|
38
39
|
right: 20px;
|
|
39
|
-
width:
|
|
40
|
-
height:
|
|
40
|
+
width: 64px;
|
|
41
|
+
height: 64px;
|
|
41
42
|
border-radius: 50%;
|
|
42
|
-
background
|
|
43
|
+
background: linear-gradient(135deg, #0176d3 0%, #005fb2 100%);
|
|
43
44
|
color: white;
|
|
44
45
|
border: none;
|
|
45
46
|
cursor: pointer;
|
|
46
|
-
box-shadow: 0
|
|
47
|
+
box-shadow: 0 8px 24px rgb(1 118 211 / 25%), 0 2px 8px rgb(1 118 211 / 15%);
|
|
47
48
|
z-index: 1001;
|
|
48
49
|
display: flex;
|
|
49
50
|
align-items: center;
|
|
50
51
|
justify-content: center;
|
|
51
|
-
transition: all 0.3s
|
|
52
|
-
animation: pulse
|
|
52
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
53
|
+
animation: pulse 3s infinite;
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
.chat-trigger-button:hover {
|
|
56
|
-
background
|
|
57
|
-
transform: scale(1.
|
|
58
|
-
box-shadow: 0
|
|
57
|
+
background: linear-gradient(135deg, #005fb2 0%, #003e73 100%);
|
|
58
|
+
transform: scale(1.05);
|
|
59
|
+
box-shadow: 0 12px 32px rgb(1 118 211 / 35%), 0 4px 12px rgb(1 118 211 / 25%);
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
.chat-trigger-button:active {
|
|
@@ -79,13 +80,15 @@ body.chat-closed .global-header {
|
|
|
79
80
|
|
|
80
81
|
@keyframes pulse {
|
|
81
82
|
0% {
|
|
82
|
-
box-shadow: 0
|
|
83
|
+
box-shadow: 0 8px 24px rgb(1 118 211 / 25%), 0 2px 8px rgb(1 118 211 / 15%), 0 0 0 0 rgb(1 118 211 / 40%);
|
|
83
84
|
}
|
|
85
|
+
|
|
84
86
|
70% {
|
|
85
|
-
box-shadow: 0
|
|
87
|
+
box-shadow: 0 8px 24px rgb(1 118 211 / 25%), 0 2px 8px rgb(1 118 211 / 15%), 0 0 0 16px rgb(1 118 211 / 0%);
|
|
86
88
|
}
|
|
89
|
+
|
|
87
90
|
100% {
|
|
88
|
-
box-shadow: 0
|
|
91
|
+
box-shadow: 0 8px 24px rgb(1 118 211 / 25%), 0 2px 8px rgb(1 118 211 / 15%), 0 0 0 0 rgb(1 118 211 / 0%);
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
94
|
|
|
@@ -93,32 +96,46 @@ body.chat-closed .global-header {
|
|
|
93
96
|
.chat-container {
|
|
94
97
|
height: 100vh;
|
|
95
98
|
width: 400px;
|
|
96
|
-
background-color: #
|
|
97
|
-
box-shadow: -
|
|
99
|
+
background-color: #fff;
|
|
100
|
+
box-shadow: -8px 0 32px rgb(0 0 0 / 12%), -2px 0 8px rgb(0 0 0 / 8%);
|
|
98
101
|
transform: translateX(100%);
|
|
99
|
-
transition: transform 0.
|
|
102
|
+
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
100
103
|
display: flex;
|
|
101
104
|
flex-direction: column;
|
|
102
|
-
border-left: 1px solid #
|
|
105
|
+
border-left: 1px solid #d8dde6;
|
|
106
|
+
backdrop-filter: blur(20px);
|
|
103
107
|
}
|
|
104
108
|
|
|
105
|
-
.chat-
|
|
109
|
+
.chat-container_open {
|
|
106
110
|
transform: translateX(0);
|
|
107
111
|
}
|
|
108
112
|
|
|
109
|
-
.chat-
|
|
113
|
+
.chat-container_disabled {
|
|
110
114
|
opacity: 0.6;
|
|
111
115
|
pointer-events: none;
|
|
112
116
|
}
|
|
113
117
|
|
|
114
118
|
.chat-header {
|
|
115
|
-
background
|
|
116
|
-
padding:
|
|
117
|
-
border-bottom: 1px solid
|
|
119
|
+
background: linear-gradient(135deg, #0176d3 0%, #005fb2 100%);
|
|
120
|
+
padding: 20px 24px;
|
|
121
|
+
border-bottom: 1px solid rgb(1 118 211 / 20%);
|
|
118
122
|
display: flex;
|
|
119
123
|
align-items: center;
|
|
120
124
|
justify-content: space-between;
|
|
121
125
|
flex-shrink: 0;
|
|
126
|
+
backdrop-filter: blur(10px);
|
|
127
|
+
position: relative;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.chat-header::before {
|
|
131
|
+
content: "";
|
|
132
|
+
position: absolute;
|
|
133
|
+
top: 0;
|
|
134
|
+
left: 0;
|
|
135
|
+
right: 0;
|
|
136
|
+
bottom: 0;
|
|
137
|
+
background: linear-gradient(135deg, rgb(255 255 255 / 5%) 0%, rgb(255 255 255 / 1%) 100%);
|
|
138
|
+
pointer-events: none;
|
|
122
139
|
}
|
|
123
140
|
|
|
124
141
|
.chat-header-actions {
|
|
@@ -129,28 +146,31 @@ body.chat-closed .global-header {
|
|
|
129
146
|
|
|
130
147
|
.chat-close-button,
|
|
131
148
|
.chat-clear-button {
|
|
132
|
-
width:
|
|
133
|
-
height:
|
|
149
|
+
width: 36px;
|
|
150
|
+
height: 36px;
|
|
134
151
|
border: none;
|
|
135
|
-
background:
|
|
152
|
+
background: rgb(255 255 255 / 15%);
|
|
136
153
|
cursor: pointer;
|
|
137
154
|
display: flex;
|
|
138
155
|
align-items: center;
|
|
139
156
|
justify-content: center;
|
|
140
|
-
border-radius:
|
|
141
|
-
transition:
|
|
142
|
-
color:
|
|
157
|
+
border-radius: 8px;
|
|
158
|
+
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
159
|
+
color: white;
|
|
160
|
+
position: relative;
|
|
161
|
+
z-index: 1;
|
|
143
162
|
}
|
|
144
163
|
|
|
145
164
|
.chat-close-button:hover,
|
|
146
165
|
.chat-clear-button:hover {
|
|
147
|
-
background
|
|
148
|
-
|
|
166
|
+
background: rgb(255 255 255 / 25%);
|
|
167
|
+
transform: scale(1.05);
|
|
168
|
+
color: white;
|
|
149
169
|
}
|
|
150
170
|
|
|
151
171
|
.chat-clear-button:hover {
|
|
152
|
-
background
|
|
153
|
-
color: #
|
|
172
|
+
background: rgb(255 82 82 / 20%);
|
|
173
|
+
color: #ff6b6b;
|
|
154
174
|
}
|
|
155
175
|
|
|
156
176
|
.close-icon,
|
|
@@ -161,24 +181,29 @@ body.chat-closed .global-header {
|
|
|
161
181
|
|
|
162
182
|
.chat-title {
|
|
163
183
|
margin: 0;
|
|
164
|
-
font-size:
|
|
165
|
-
font-weight:
|
|
166
|
-
color:
|
|
184
|
+
font-size: 20px;
|
|
185
|
+
font-weight: 700;
|
|
186
|
+
color: white;
|
|
167
187
|
flex: 1;
|
|
188
|
+
position: relative;
|
|
189
|
+
z-index: 1;
|
|
190
|
+
letter-spacing: -0.02em;
|
|
168
191
|
}
|
|
169
192
|
|
|
170
193
|
.chat-messages {
|
|
171
194
|
flex: 1;
|
|
172
195
|
overflow-y: auto;
|
|
173
|
-
padding:
|
|
196
|
+
padding: 24px 20px;
|
|
174
197
|
scroll-behavior: smooth;
|
|
175
198
|
min-height: 0;
|
|
199
|
+
background: linear-gradient(180deg, #fafbfc 0%, #f7f9fb 100%);
|
|
176
200
|
}
|
|
177
201
|
|
|
178
202
|
.message-wrapper {
|
|
179
|
-
margin-bottom:
|
|
203
|
+
margin-bottom: 20px;
|
|
180
204
|
display: flex;
|
|
181
205
|
align-items: flex-start;
|
|
206
|
+
animation: message-slide-in 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
182
207
|
}
|
|
183
208
|
|
|
184
209
|
.message-wrapper[data-sender="user"] {
|
|
@@ -201,35 +226,43 @@ body.chat-closed .global-header {
|
|
|
201
226
|
}
|
|
202
227
|
|
|
203
228
|
.message-bubble {
|
|
204
|
-
padding:
|
|
205
|
-
border-radius:
|
|
229
|
+
padding: 14px 18px;
|
|
230
|
+
border-radius: 20px;
|
|
206
231
|
word-wrap: break-word;
|
|
207
232
|
max-width: 100%;
|
|
233
|
+
position: relative;
|
|
234
|
+
backdrop-filter: blur(10px);
|
|
235
|
+
box-shadow: 0 2px 8px rgb(0 0 0 / 8%);
|
|
208
236
|
}
|
|
209
237
|
|
|
210
238
|
.message-wrapper[data-sender="user"] .message-bubble {
|
|
211
|
-
background
|
|
239
|
+
background: linear-gradient(135deg, #0176d3 0%, #005fb2 100%);
|
|
212
240
|
color: white;
|
|
241
|
+
border-bottom-right-radius: 8px;
|
|
213
242
|
}
|
|
214
243
|
|
|
215
244
|
.message-wrapper[data-sender="assistant"] .message-bubble {
|
|
216
|
-
background
|
|
217
|
-
color: #
|
|
245
|
+
background: white;
|
|
246
|
+
color: #181818;
|
|
247
|
+
border: 1px solid #e5e7ea;
|
|
248
|
+
border-bottom-left-radius: 8px;
|
|
218
249
|
}
|
|
219
250
|
|
|
220
251
|
.message-text {
|
|
221
252
|
margin: 0;
|
|
222
|
-
line-height: 1.
|
|
223
|
-
font-size:
|
|
253
|
+
line-height: 1.5;
|
|
254
|
+
font-size: 15px;
|
|
255
|
+
font-weight: 400;
|
|
224
256
|
}
|
|
225
257
|
|
|
226
258
|
.message-timestamp {
|
|
227
259
|
font-size: 12px;
|
|
228
|
-
color: #
|
|
229
|
-
margin-top:
|
|
260
|
+
color: #706e6b;
|
|
261
|
+
margin-top: 6px;
|
|
230
262
|
display: flex;
|
|
231
263
|
align-items: center;
|
|
232
264
|
gap: 4px;
|
|
265
|
+
font-weight: 400;
|
|
233
266
|
}
|
|
234
267
|
|
|
235
268
|
.message-sender {
|
|
@@ -252,7 +285,7 @@ body.chat-closed .global-header {
|
|
|
252
285
|
width: 8px;
|
|
253
286
|
height: 8px;
|
|
254
287
|
border-radius: 50%;
|
|
255
|
-
background-color: #
|
|
288
|
+
background-color: #0176d3;
|
|
256
289
|
animation: typing 1.4s infinite ease-in-out both;
|
|
257
290
|
}
|
|
258
291
|
|
|
@@ -265,21 +298,37 @@ body.chat-closed .global-header {
|
|
|
265
298
|
}
|
|
266
299
|
|
|
267
300
|
@keyframes typing {
|
|
268
|
-
0%,
|
|
301
|
+
0%,
|
|
302
|
+
80%,
|
|
303
|
+
100% {
|
|
269
304
|
transform: scale(0);
|
|
270
305
|
opacity: 0.5;
|
|
271
306
|
}
|
|
307
|
+
|
|
272
308
|
40% {
|
|
273
309
|
transform: scale(1);
|
|
274
310
|
opacity: 1;
|
|
275
311
|
}
|
|
276
312
|
}
|
|
277
313
|
|
|
314
|
+
@keyframes message-slide-in {
|
|
315
|
+
from {
|
|
316
|
+
opacity: 0;
|
|
317
|
+
transform: translateY(12px);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
to {
|
|
321
|
+
opacity: 1;
|
|
322
|
+
transform: translateY(0);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
278
326
|
.chat-input-area {
|
|
279
|
-
border-top: 1px solid #
|
|
280
|
-
padding:
|
|
281
|
-
background
|
|
327
|
+
border-top: 1px solid #e5e7ea;
|
|
328
|
+
padding: 20px 24px;
|
|
329
|
+
background: white;
|
|
282
330
|
flex-shrink: 0;
|
|
331
|
+
backdrop-filter: blur(20px);
|
|
283
332
|
}
|
|
284
333
|
|
|
285
334
|
.chat-input-container {
|
|
@@ -290,18 +339,22 @@ body.chat-closed .global-header {
|
|
|
290
339
|
|
|
291
340
|
.chat-input {
|
|
292
341
|
flex: 1;
|
|
293
|
-
padding:
|
|
294
|
-
border:
|
|
295
|
-
border-radius:
|
|
296
|
-
font-size:
|
|
342
|
+
padding: 14px 18px;
|
|
343
|
+
border: 2px solid #d8dde6;
|
|
344
|
+
border-radius: 24px;
|
|
345
|
+
font-size: 15px;
|
|
297
346
|
outline: none;
|
|
298
|
-
background-color: #
|
|
299
|
-
transition:
|
|
347
|
+
background-color: #fafbfc;
|
|
348
|
+
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
349
|
+
font-family: inherit;
|
|
350
|
+
line-height: 1.4;
|
|
300
351
|
}
|
|
301
352
|
|
|
302
353
|
.chat-input:focus {
|
|
303
|
-
border-color: #
|
|
304
|
-
background-color: #
|
|
354
|
+
border-color: #0176d3;
|
|
355
|
+
background-color: #fff;
|
|
356
|
+
box-shadow: 0 0 0 3px rgb(1 118 211 / 10%);
|
|
357
|
+
transform: translateY(-1px);
|
|
305
358
|
}
|
|
306
359
|
|
|
307
360
|
.chat-input:disabled {
|
|
@@ -310,26 +363,32 @@ body.chat-closed .global-header {
|
|
|
310
363
|
}
|
|
311
364
|
|
|
312
365
|
.chat-send-button {
|
|
313
|
-
width:
|
|
314
|
-
height:
|
|
366
|
+
width: 44px;
|
|
367
|
+
height: 44px;
|
|
315
368
|
border: none;
|
|
316
369
|
border-radius: 50%;
|
|
317
|
-
background
|
|
370
|
+
background: linear-gradient(135deg, #0176d3 0%, #005fb2 100%);
|
|
318
371
|
color: white;
|
|
319
372
|
cursor: pointer;
|
|
320
373
|
display: flex;
|
|
321
374
|
align-items: center;
|
|
322
375
|
justify-content: center;
|
|
323
|
-
transition:
|
|
376
|
+
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
377
|
+
box-shadow: 0 4px 12px rgb(1 118 211 / 25%);
|
|
378
|
+
margin-left: 12px;
|
|
324
379
|
}
|
|
325
380
|
|
|
326
381
|
.chat-send-button:hover:not(:disabled) {
|
|
327
|
-
background
|
|
382
|
+
background: linear-gradient(135deg, #005fb2 0%, #003e73 100%);
|
|
383
|
+
transform: scale(1.05);
|
|
384
|
+
box-shadow: 0 6px 16px rgb(1 118 211 / 35%);
|
|
328
385
|
}
|
|
329
386
|
|
|
330
387
|
.chat-send-button:disabled {
|
|
331
|
-
background
|
|
388
|
+
background: #d8dde6;
|
|
332
389
|
cursor: not-allowed;
|
|
390
|
+
transform: none;
|
|
391
|
+
box-shadow: none;
|
|
333
392
|
}
|
|
334
393
|
|
|
335
394
|
.send-icon {
|
|
@@ -340,10 +399,10 @@ body.chat-closed .global-header {
|
|
|
340
399
|
/* Responsive design */
|
|
341
400
|
@media (max-width: 768px) {
|
|
342
401
|
.chat-trigger-button {
|
|
343
|
-
width:
|
|
344
|
-
height:
|
|
345
|
-
bottom:
|
|
346
|
-
right:
|
|
402
|
+
width: 56px;
|
|
403
|
+
height: 56px;
|
|
404
|
+
bottom: 16px;
|
|
405
|
+
right: 16px;
|
|
347
406
|
}
|
|
348
407
|
|
|
349
408
|
.chat-icon {
|
|
@@ -362,12 +421,28 @@ body.chat-closed .global-header {
|
|
|
362
421
|
transform: translateX(100%);
|
|
363
422
|
}
|
|
364
423
|
|
|
365
|
-
.chat-
|
|
424
|
+
.chat-container_open {
|
|
366
425
|
transform: translateX(0);
|
|
367
426
|
}
|
|
368
427
|
|
|
428
|
+
.chat-header {
|
|
429
|
+
padding: 16px 20px;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.chat-title {
|
|
433
|
+
font-size: 18px;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.chat-messages {
|
|
437
|
+
padding: 20px 16px;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.chat-input-area {
|
|
441
|
+
padding: 16px 20px;
|
|
442
|
+
}
|
|
443
|
+
|
|
369
444
|
.message-content {
|
|
370
|
-
max-width:
|
|
445
|
+
max-width: 85%;
|
|
371
446
|
}
|
|
372
447
|
}
|
|
373
448
|
|
|
@@ -378,29 +453,29 @@ body.chat-closed .global-header {
|
|
|
378
453
|
bottom: 10px;
|
|
379
454
|
right: 10px;
|
|
380
455
|
}
|
|
381
|
-
|
|
456
|
+
|
|
382
457
|
.chat-icon {
|
|
383
458
|
width: 20px;
|
|
384
459
|
height: 20px;
|
|
385
460
|
}
|
|
386
|
-
|
|
461
|
+
|
|
387
462
|
.chat-trigger-text {
|
|
388
463
|
font-size: 7px;
|
|
389
464
|
margin-top: 16px;
|
|
390
465
|
}
|
|
391
|
-
|
|
466
|
+
|
|
392
467
|
.chat-container {
|
|
393
468
|
width: 100%;
|
|
394
469
|
}
|
|
395
|
-
|
|
470
|
+
|
|
396
471
|
.chat-header {
|
|
397
472
|
padding: 12px;
|
|
398
473
|
}
|
|
399
|
-
|
|
474
|
+
|
|
400
475
|
.chat-messages {
|
|
401
476
|
padding: 12px;
|
|
402
477
|
}
|
|
403
|
-
|
|
478
|
+
|
|
404
479
|
.chat-input-area {
|
|
405
480
|
padding: 12px;
|
|
406
481
|
}
|
|
@@ -411,11 +486,11 @@ body.chat-closed .global-header {
|
|
|
411
486
|
.chat-trigger-button {
|
|
412
487
|
animation: none;
|
|
413
488
|
}
|
|
414
|
-
|
|
489
|
+
|
|
415
490
|
.typing-dot {
|
|
416
491
|
animation: none;
|
|
417
492
|
}
|
|
418
|
-
|
|
493
|
+
|
|
419
494
|
.chat-messages {
|
|
420
495
|
scroll-behavior: auto;
|
|
421
496
|
}
|
|
@@ -424,17 +499,17 @@ body.chat-closed .global-header {
|
|
|
424
499
|
/* High contrast mode support */
|
|
425
500
|
@media (prefers-contrast: high) {
|
|
426
501
|
.chat-container {
|
|
427
|
-
border: 2px solid #
|
|
502
|
+
border: 2px solid #000;
|
|
428
503
|
}
|
|
429
|
-
|
|
504
|
+
|
|
430
505
|
.message-wrapper[data-sender="user"] .message-bubble {
|
|
431
|
-
background-color: #
|
|
432
|
-
color: #
|
|
506
|
+
background-color: #000;
|
|
507
|
+
color: #fff;
|
|
433
508
|
}
|
|
434
|
-
|
|
509
|
+
|
|
435
510
|
.message-wrapper[data-sender="assistant"] .message-bubble {
|
|
436
|
-
background-color: #
|
|
437
|
-
color: #
|
|
438
|
-
border: 1px solid #
|
|
511
|
+
background-color: #fff;
|
|
512
|
+
color: #000;
|
|
513
|
+
border: 1px solid #000;
|
|
439
514
|
}
|
|
440
|
-
}
|
|
515
|
+
}
|
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<!-- Floating trigger button - visible when chat is closed -->
|
|
3
3
|
<template lwc:if={showTriggerButton}>
|
|
4
|
-
<button
|
|
4
|
+
<button
|
|
5
5
|
class="chat-trigger-button"
|
|
6
6
|
onclick={handleOpenClick}
|
|
7
7
|
aria-label="Open chat"
|
|
8
8
|
>
|
|
9
|
-
<svg
|
|
10
|
-
|
|
9
|
+
<svg
|
|
10
|
+
class="chat-icon"
|
|
11
|
+
viewBox="0 0 24 24"
|
|
12
|
+
fill="none"
|
|
13
|
+
stroke="currentColor"
|
|
14
|
+
>
|
|
15
|
+
<path
|
|
16
|
+
stroke-linecap="round"
|
|
17
|
+
stroke-linejoin="round"
|
|
18
|
+
stroke-width="2"
|
|
19
|
+
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
|
|
20
|
+
/>
|
|
11
21
|
</svg>
|
|
12
22
|
<!-- <span class="chat-trigger-text">Chat</span> -->
|
|
13
23
|
</button>
|
|
@@ -18,31 +28,55 @@
|
|
|
18
28
|
<div class="chat-header">
|
|
19
29
|
<h3 class="chat-title">{title}</h3>
|
|
20
30
|
<div class="chat-header-actions">
|
|
21
|
-
<button
|
|
31
|
+
<button
|
|
22
32
|
class="chat-clear-button"
|
|
23
33
|
onclick={handleClearClick}
|
|
24
34
|
aria-label="Clear chat messages"
|
|
25
35
|
title="Clear chat messages"
|
|
26
36
|
>
|
|
27
|
-
<svg
|
|
28
|
-
|
|
37
|
+
<svg
|
|
38
|
+
class="clear-icon"
|
|
39
|
+
viewBox="0 0 24 24"
|
|
40
|
+
fill="none"
|
|
41
|
+
stroke="currentColor"
|
|
42
|
+
>
|
|
43
|
+
<path
|
|
44
|
+
stroke-linecap="round"
|
|
45
|
+
stroke-linejoin="round"
|
|
46
|
+
stroke-width="2"
|
|
47
|
+
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
|
|
48
|
+
/>
|
|
29
49
|
</svg>
|
|
30
50
|
</button>
|
|
31
|
-
<button
|
|
51
|
+
<button
|
|
32
52
|
class="chat-close-button"
|
|
33
53
|
onclick={handleCloseClick}
|
|
34
54
|
aria-label="Close chat"
|
|
35
55
|
>
|
|
36
|
-
<svg
|
|
37
|
-
|
|
56
|
+
<svg
|
|
57
|
+
class="close-icon"
|
|
58
|
+
viewBox="0 0 24 24"
|
|
59
|
+
fill="none"
|
|
60
|
+
stroke="currentColor"
|
|
61
|
+
>
|
|
62
|
+
<path
|
|
63
|
+
stroke-linecap="round"
|
|
64
|
+
stroke-linejoin="round"
|
|
65
|
+
stroke-width="2"
|
|
66
|
+
d="M6 18L18 6M6 6l12 12"
|
|
67
|
+
/>
|
|
38
68
|
</svg>
|
|
39
69
|
</button>
|
|
40
70
|
</div>
|
|
41
71
|
</div>
|
|
42
|
-
|
|
72
|
+
|
|
43
73
|
<div class="chat-messages">
|
|
44
74
|
<template for:each={messagesWithTyping} for:item="message">
|
|
45
|
-
<div
|
|
75
|
+
<div
|
|
76
|
+
key={message.id}
|
|
77
|
+
class="message-wrapper"
|
|
78
|
+
data-sender={message.sender}
|
|
79
|
+
>
|
|
46
80
|
<div class="message-content">
|
|
47
81
|
<div class="message-bubble">
|
|
48
82
|
<template lwc:if={message.isTyping}>
|
|
@@ -72,29 +106,39 @@
|
|
|
72
106
|
</div>
|
|
73
107
|
</template>
|
|
74
108
|
</div>
|
|
75
|
-
|
|
109
|
+
|
|
76
110
|
<div class="chat-input-area">
|
|
77
111
|
<div class="chat-input-container">
|
|
78
|
-
<input
|
|
79
|
-
type="text"
|
|
80
|
-
class="chat-input"
|
|
112
|
+
<input
|
|
113
|
+
type="text"
|
|
114
|
+
class="chat-input"
|
|
81
115
|
placeholder={placeholder}
|
|
82
116
|
value={currentMessage}
|
|
83
117
|
oninput={handleInputChange}
|
|
84
118
|
onkeydown={handleKeyDown}
|
|
85
119
|
disabled={disabled}
|
|
86
120
|
/>
|
|
87
|
-
<button
|
|
121
|
+
<button
|
|
88
122
|
class="chat-send-button"
|
|
89
123
|
onclick={handleSendClick}
|
|
90
124
|
disabled={sendButtonDisabled}
|
|
91
125
|
aria-label="Send message"
|
|
92
126
|
>
|
|
93
|
-
<svg
|
|
94
|
-
|
|
127
|
+
<svg
|
|
128
|
+
class="send-icon"
|
|
129
|
+
viewBox="0 0 24 24"
|
|
130
|
+
fill="none"
|
|
131
|
+
stroke="currentColor"
|
|
132
|
+
>
|
|
133
|
+
<path
|
|
134
|
+
stroke-linecap="round"
|
|
135
|
+
stroke-linejoin="round"
|
|
136
|
+
stroke-width="2"
|
|
137
|
+
d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"
|
|
138
|
+
/>
|
|
95
139
|
</svg>
|
|
96
140
|
</button>
|
|
97
141
|
</div>
|
|
98
142
|
</div>
|
|
99
143
|
</div>
|
|
100
|
-
</template>
|
|
144
|
+
</template>
|
|
@@ -16,7 +16,7 @@ export default class Chat extends LightningElement {
|
|
|
16
16
|
@api placeholder: string = "Type your message...";
|
|
17
17
|
@api assistantName: string = "Assistant";
|
|
18
18
|
@api maxHeight: string = "400px";
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
@api
|
|
21
21
|
get disabled() {
|
|
22
22
|
return this._disabled;
|
|
@@ -47,42 +47,42 @@ export default class Chat extends LightningElement {
|
|
|
47
47
|
@track messages: ChatMessage[] = [];
|
|
48
48
|
@track currentMessage: string = "";
|
|
49
49
|
@track isAssistantTyping: boolean = false;
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
private _disabled: boolean = false;
|
|
52
52
|
private _showTimestamp: boolean = false;
|
|
53
53
|
private _isOpen: boolean = false;
|
|
54
54
|
private messageIdCounter: number = 0;
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
// localStorage keys for persisting messages and open state
|
|
57
|
-
private static readonly STORAGE_KEY =
|
|
58
|
-
private static readonly OPEN_STATE_KEY =
|
|
57
|
+
private static readonly STORAGE_KEY = "doc-chat-messages";
|
|
58
|
+
private static readonly OPEN_STATE_KEY = "doc-chat-should-open";
|
|
59
59
|
|
|
60
60
|
connectedCallback() {
|
|
61
61
|
// Load existing messages from localStorage
|
|
62
62
|
this.loadMessages();
|
|
63
|
-
|
|
63
|
+
|
|
64
64
|
// Add welcome message only if no existing messages
|
|
65
65
|
if (this.messages.length === 0) {
|
|
66
66
|
this.addMessage("Hello! How can I help you today?", "assistant");
|
|
67
67
|
}
|
|
68
|
-
|
|
68
|
+
|
|
69
69
|
// Check if chat should be opened after reload
|
|
70
70
|
this.checkAndOpenAfterReload();
|
|
71
|
-
|
|
71
|
+
|
|
72
72
|
// Ensure body has proper class state
|
|
73
73
|
this.updateBodyClass();
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
disconnectedCallback() {
|
|
77
77
|
// Clean up body classes when component is destroyed
|
|
78
|
-
document.body.classList.remove(
|
|
78
|
+
document.body.classList.remove("chat-open", "chat-closed");
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
get chatContainerClass() {
|
|
82
82
|
return cx(
|
|
83
83
|
"chat-container",
|
|
84
|
-
this.disabled && "chat-
|
|
85
|
-
this.isOpen && "chat-
|
|
84
|
+
this.disabled && "chat-container_disabled",
|
|
85
|
+
this.isOpen && "chat-container_open"
|
|
86
86
|
);
|
|
87
87
|
}
|
|
88
88
|
|
|
@@ -123,7 +123,8 @@ export default class Chat extends LightningElement {
|
|
|
123
123
|
private scrollToBottom() {
|
|
124
124
|
// Use setTimeout to ensure DOM is updated
|
|
125
125
|
setTimeout(() => {
|
|
126
|
-
const messagesContainer =
|
|
126
|
+
const messagesContainer =
|
|
127
|
+
this.template.querySelector(".chat-messages");
|
|
127
128
|
if (messagesContainer) {
|
|
128
129
|
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
|
129
130
|
}
|
|
@@ -133,26 +134,29 @@ export default class Chat extends LightningElement {
|
|
|
133
134
|
private updateBodyClass() {
|
|
134
135
|
// Update body classes to trigger content shift
|
|
135
136
|
if (this.isOpen) {
|
|
136
|
-
document.body.classList.remove(
|
|
137
|
-
document.body.classList.add(
|
|
137
|
+
document.body.classList.remove("chat-closed");
|
|
138
|
+
document.body.classList.add("chat-open");
|
|
138
139
|
} else {
|
|
139
|
-
document.body.classList.remove(
|
|
140
|
-
document.body.classList.add(
|
|
140
|
+
document.body.classList.remove("chat-open");
|
|
141
|
+
document.body.classList.add("chat-closed");
|
|
141
142
|
}
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
private saveMessages() {
|
|
145
146
|
try {
|
|
146
|
-
const messagesToSave = this.messages.map(msg => ({
|
|
147
|
+
const messagesToSave = this.messages.map((msg) => ({
|
|
147
148
|
id: msg.id,
|
|
148
149
|
text: msg.text,
|
|
149
150
|
timestamp: msg.timestamp.toISOString(),
|
|
150
151
|
sender: msg.sender,
|
|
151
152
|
formattedTime: msg.formattedTime
|
|
152
153
|
}));
|
|
153
|
-
localStorage.setItem(
|
|
154
|
+
localStorage.setItem(
|
|
155
|
+
Chat.STORAGE_KEY,
|
|
156
|
+
JSON.stringify(messagesToSave)
|
|
157
|
+
);
|
|
154
158
|
} catch (error) {
|
|
155
|
-
console.warn(
|
|
159
|
+
console.warn("Failed to save messages to localStorage:", error);
|
|
156
160
|
}
|
|
157
161
|
}
|
|
158
162
|
|
|
@@ -168,14 +172,22 @@ export default class Chat extends LightningElement {
|
|
|
168
172
|
sender: msg.sender,
|
|
169
173
|
formattedTime: msg.formattedTime
|
|
170
174
|
}));
|
|
171
|
-
|
|
175
|
+
|
|
172
176
|
// Update message counter to avoid ID conflicts
|
|
173
|
-
const lastId =
|
|
174
|
-
|
|
177
|
+
const lastId =
|
|
178
|
+
this.messages.length > 0
|
|
179
|
+
? Math.max(
|
|
180
|
+
...this.messages.map(
|
|
181
|
+
(m) =>
|
|
182
|
+
parseInt(m.id.replace("msg-", ""), 10) ||
|
|
183
|
+
0
|
|
184
|
+
)
|
|
185
|
+
)
|
|
186
|
+
: 0;
|
|
175
187
|
this.messageIdCounter = lastId + 1;
|
|
176
188
|
}
|
|
177
189
|
} catch (error) {
|
|
178
|
-
console.warn(
|
|
190
|
+
console.warn("Failed to load messages from localStorage:", error);
|
|
179
191
|
this.messages = [];
|
|
180
192
|
}
|
|
181
193
|
}
|
|
@@ -188,27 +200,32 @@ export default class Chat extends LightningElement {
|
|
|
188
200
|
// Add welcome message after clearing
|
|
189
201
|
this.addMessage("Hello! How can I help you today?", "assistant");
|
|
190
202
|
} catch (error) {
|
|
191
|
-
console.warn(
|
|
203
|
+
console.warn("Failed to clear messages from localStorage:", error);
|
|
192
204
|
}
|
|
193
205
|
}
|
|
194
206
|
|
|
195
207
|
private checkAndOpenAfterReload() {
|
|
196
208
|
try {
|
|
197
209
|
const shouldOpen = localStorage.getItem(Chat.OPEN_STATE_KEY);
|
|
198
|
-
if (shouldOpen ===
|
|
210
|
+
if (shouldOpen === "true") {
|
|
199
211
|
// Clear the flag
|
|
200
212
|
localStorage.removeItem(Chat.OPEN_STATE_KEY);
|
|
201
213
|
// Open the chat
|
|
202
|
-
this.
|
|
214
|
+
this._isOpen = true;
|
|
203
215
|
this.updateBodyClass();
|
|
204
|
-
|
|
216
|
+
|
|
205
217
|
// Dispatch custom event to notify parent components
|
|
206
|
-
this.dispatchEvent(
|
|
207
|
-
|
|
208
|
-
|
|
218
|
+
this.dispatchEvent(
|
|
219
|
+
new CustomEvent("chatopened", {
|
|
220
|
+
detail: { opened: true }
|
|
221
|
+
})
|
|
222
|
+
);
|
|
209
223
|
}
|
|
210
224
|
} catch (error) {
|
|
211
|
-
console.warn(
|
|
225
|
+
console.warn(
|
|
226
|
+
"Failed to check open state from localStorage:",
|
|
227
|
+
error
|
|
228
|
+
);
|
|
212
229
|
}
|
|
213
230
|
}
|
|
214
231
|
|
|
@@ -218,7 +235,7 @@ export default class Chat extends LightningElement {
|
|
|
218
235
|
}
|
|
219
236
|
|
|
220
237
|
handleKeyDown(event: KeyboardEvent) {
|
|
221
|
-
if (event.key ===
|
|
238
|
+
if (event.key === "Enter" && !event.shiftKey) {
|
|
222
239
|
event.preventDefault();
|
|
223
240
|
this.sendMessage();
|
|
224
241
|
}
|
|
@@ -239,14 +256,16 @@ export default class Chat extends LightningElement {
|
|
|
239
256
|
this.currentMessage = "";
|
|
240
257
|
|
|
241
258
|
// Clear input
|
|
242
|
-
const input = this.template.querySelector(
|
|
259
|
+
const input = this.template.querySelector(
|
|
260
|
+
".chat-input"
|
|
261
|
+
) as HTMLInputElement;
|
|
243
262
|
if (input) {
|
|
244
263
|
input.value = "";
|
|
245
264
|
}
|
|
246
265
|
|
|
247
266
|
// Simulate assistant typing
|
|
248
267
|
this.isAssistantTyping = true;
|
|
249
|
-
|
|
268
|
+
|
|
250
269
|
// Simulate assistant response after a delay
|
|
251
270
|
setTimeout(() => {
|
|
252
271
|
this.isAssistantTyping = false;
|
|
@@ -257,22 +276,34 @@ export default class Chat extends LightningElement {
|
|
|
257
276
|
private simulateAssistantResponse(userMessage: string) {
|
|
258
277
|
// Simple response simulation - in a real implementation, this would call an API
|
|
259
278
|
let response = `I received your message: "${userMessage}". `;
|
|
260
|
-
|
|
261
|
-
if (
|
|
279
|
+
|
|
280
|
+
if (
|
|
281
|
+
userMessage.toLowerCase().includes("hello") ||
|
|
282
|
+
userMessage.toLowerCase().includes("hi")
|
|
283
|
+
) {
|
|
262
284
|
response = "Hello! Nice to meet you. How can I assist you today?";
|
|
263
285
|
} else if (userMessage.toLowerCase().includes("help")) {
|
|
264
|
-
response =
|
|
265
|
-
|
|
266
|
-
|
|
286
|
+
response =
|
|
287
|
+
"I'm here to help! Feel free to ask me any questions about the documentation or topics you're interested in.";
|
|
288
|
+
} else if (
|
|
289
|
+
userMessage.toLowerCase().includes("documentation") ||
|
|
290
|
+
userMessage.toLowerCase().includes("docs")
|
|
291
|
+
) {
|
|
292
|
+
response =
|
|
293
|
+
"I can help you navigate the documentation. What specific topic are you looking for?";
|
|
267
294
|
} else {
|
|
268
|
-
response =
|
|
295
|
+
response =
|
|
296
|
+
"That's an interesting question. Let me help you with that. Could you provide more details about what you're looking for?";
|
|
269
297
|
}
|
|
270
298
|
|
|
271
299
|
this.addMessage(response, "assistant");
|
|
272
300
|
}
|
|
273
301
|
|
|
274
302
|
formatTimestamp(timestamp: Date) {
|
|
275
|
-
return timestamp.toLocaleTimeString([], {
|
|
303
|
+
return timestamp.toLocaleTimeString([], {
|
|
304
|
+
hour: "2-digit",
|
|
305
|
+
minute: "2-digit"
|
|
306
|
+
});
|
|
276
307
|
}
|
|
277
308
|
|
|
278
309
|
get sendButtonDisabled() {
|
|
@@ -280,32 +311,35 @@ export default class Chat extends LightningElement {
|
|
|
280
311
|
}
|
|
281
312
|
|
|
282
313
|
handleCloseClick() {
|
|
283
|
-
this.
|
|
314
|
+
this._isOpen = false;
|
|
284
315
|
this.updateBodyClass();
|
|
285
|
-
|
|
316
|
+
|
|
286
317
|
// Dispatch custom event to notify parent components
|
|
287
|
-
this.dispatchEvent(
|
|
288
|
-
|
|
289
|
-
|
|
318
|
+
this.dispatchEvent(
|
|
319
|
+
new CustomEvent("chatclosed", {
|
|
320
|
+
detail: { closed: true }
|
|
321
|
+
})
|
|
322
|
+
);
|
|
290
323
|
}
|
|
291
324
|
|
|
292
325
|
handleClearClick() {
|
|
293
326
|
this.clearMessages();
|
|
294
|
-
|
|
327
|
+
|
|
295
328
|
// Dispatch custom event to notify parent components
|
|
296
|
-
this.dispatchEvent(
|
|
297
|
-
|
|
298
|
-
|
|
329
|
+
this.dispatchEvent(
|
|
330
|
+
new CustomEvent("chatcleared", {
|
|
331
|
+
detail: { cleared: true }
|
|
332
|
+
})
|
|
333
|
+
);
|
|
299
334
|
}
|
|
300
335
|
|
|
301
336
|
handleOpenClick() {
|
|
302
337
|
try {
|
|
303
338
|
// Set flag to open chat after reload
|
|
304
|
-
localStorage.setItem(Chat.OPEN_STATE_KEY,
|
|
339
|
+
localStorage.setItem(Chat.OPEN_STATE_KEY, "true");
|
|
305
340
|
} catch (error) {
|
|
306
|
-
console.warn(
|
|
341
|
+
console.warn("Failed to set open state in localStorage:", error);
|
|
307
342
|
}
|
|
308
|
-
|
|
309
343
|
// Hard reload the page to clear cache when opening chat
|
|
310
344
|
window.location.reload();
|
|
311
345
|
}
|
|
@@ -313,22 +347,24 @@ export default class Chat extends LightningElement {
|
|
|
313
347
|
openChat() {
|
|
314
348
|
try {
|
|
315
349
|
// Set flag to open chat after reload
|
|
316
|
-
localStorage.setItem(Chat.OPEN_STATE_KEY,
|
|
350
|
+
localStorage.setItem(Chat.OPEN_STATE_KEY, "true");
|
|
317
351
|
} catch (error) {
|
|
318
|
-
console.warn(
|
|
352
|
+
console.warn("Failed to set open state in localStorage:", error);
|
|
319
353
|
}
|
|
320
|
-
|
|
354
|
+
|
|
321
355
|
// Hard reload the page to clear cache when opening chat
|
|
322
356
|
window.location.reload();
|
|
323
357
|
}
|
|
324
358
|
|
|
325
359
|
closeChat() {
|
|
326
|
-
this.
|
|
360
|
+
this._isOpen = false;
|
|
327
361
|
this.updateBodyClass();
|
|
328
|
-
|
|
362
|
+
|
|
329
363
|
// Dispatch custom event to notify parent components
|
|
330
|
-
this.dispatchEvent(
|
|
331
|
-
|
|
332
|
-
|
|
364
|
+
this.dispatchEvent(
|
|
365
|
+
new CustomEvent("chatclosed", {
|
|
366
|
+
detail: { closed: true }
|
|
367
|
+
})
|
|
368
|
+
);
|
|
333
369
|
}
|
|
334
|
-
}
|
|
370
|
+
}
|