@salesforcedevs/docs-components 0.0.31-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 +126 -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,38 +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
|
-
|
|
56
|
-
// localStorage
|
|
57
|
-
private static readonly STORAGE_KEY =
|
|
55
|
+
|
|
56
|
+
// localStorage keys for persisting messages and open state
|
|
57
|
+
private static readonly STORAGE_KEY = "doc-chat-messages";
|
|
58
|
+
private static readonly OPEN_STATE_KEY = "doc-chat-should-open";
|
|
58
59
|
|
|
59
60
|
connectedCallback() {
|
|
60
61
|
// Load existing messages from localStorage
|
|
61
62
|
this.loadMessages();
|
|
62
|
-
|
|
63
|
+
|
|
63
64
|
// Add welcome message only if no existing messages
|
|
64
65
|
if (this.messages.length === 0) {
|
|
65
66
|
this.addMessage("Hello! How can I help you today?", "assistant");
|
|
66
67
|
}
|
|
67
|
-
|
|
68
|
+
|
|
69
|
+
// Check if chat should be opened after reload
|
|
70
|
+
this.checkAndOpenAfterReload();
|
|
71
|
+
|
|
68
72
|
// Ensure body has proper class state
|
|
69
73
|
this.updateBodyClass();
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
disconnectedCallback() {
|
|
73
77
|
// Clean up body classes when component is destroyed
|
|
74
|
-
document.body.classList.remove(
|
|
78
|
+
document.body.classList.remove("chat-open", "chat-closed");
|
|
75
79
|
}
|
|
76
80
|
|
|
77
81
|
get chatContainerClass() {
|
|
78
82
|
return cx(
|
|
79
83
|
"chat-container",
|
|
80
|
-
this.disabled && "chat-
|
|
81
|
-
this.isOpen && "chat-
|
|
84
|
+
this.disabled && "chat-container_disabled",
|
|
85
|
+
this.isOpen && "chat-container_open"
|
|
82
86
|
);
|
|
83
87
|
}
|
|
84
88
|
|
|
@@ -119,7 +123,8 @@ export default class Chat extends LightningElement {
|
|
|
119
123
|
private scrollToBottom() {
|
|
120
124
|
// Use setTimeout to ensure DOM is updated
|
|
121
125
|
setTimeout(() => {
|
|
122
|
-
const messagesContainer =
|
|
126
|
+
const messagesContainer =
|
|
127
|
+
this.template.querySelector(".chat-messages");
|
|
123
128
|
if (messagesContainer) {
|
|
124
129
|
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
|
125
130
|
}
|
|
@@ -129,26 +134,29 @@ export default class Chat extends LightningElement {
|
|
|
129
134
|
private updateBodyClass() {
|
|
130
135
|
// Update body classes to trigger content shift
|
|
131
136
|
if (this.isOpen) {
|
|
132
|
-
document.body.classList.remove(
|
|
133
|
-
document.body.classList.add(
|
|
137
|
+
document.body.classList.remove("chat-closed");
|
|
138
|
+
document.body.classList.add("chat-open");
|
|
134
139
|
} else {
|
|
135
|
-
document.body.classList.remove(
|
|
136
|
-
document.body.classList.add(
|
|
140
|
+
document.body.classList.remove("chat-open");
|
|
141
|
+
document.body.classList.add("chat-closed");
|
|
137
142
|
}
|
|
138
143
|
}
|
|
139
144
|
|
|
140
145
|
private saveMessages() {
|
|
141
146
|
try {
|
|
142
|
-
const messagesToSave = this.messages.map(msg => ({
|
|
147
|
+
const messagesToSave = this.messages.map((msg) => ({
|
|
143
148
|
id: msg.id,
|
|
144
149
|
text: msg.text,
|
|
145
150
|
timestamp: msg.timestamp.toISOString(),
|
|
146
151
|
sender: msg.sender,
|
|
147
152
|
formattedTime: msg.formattedTime
|
|
148
153
|
}));
|
|
149
|
-
localStorage.setItem(
|
|
154
|
+
localStorage.setItem(
|
|
155
|
+
Chat.STORAGE_KEY,
|
|
156
|
+
JSON.stringify(messagesToSave)
|
|
157
|
+
);
|
|
150
158
|
} catch (error) {
|
|
151
|
-
console.warn(
|
|
159
|
+
console.warn("Failed to save messages to localStorage:", error);
|
|
152
160
|
}
|
|
153
161
|
}
|
|
154
162
|
|
|
@@ -164,14 +172,22 @@ export default class Chat extends LightningElement {
|
|
|
164
172
|
sender: msg.sender,
|
|
165
173
|
formattedTime: msg.formattedTime
|
|
166
174
|
}));
|
|
167
|
-
|
|
175
|
+
|
|
168
176
|
// Update message counter to avoid ID conflicts
|
|
169
|
-
const lastId =
|
|
170
|
-
|
|
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;
|
|
171
187
|
this.messageIdCounter = lastId + 1;
|
|
172
188
|
}
|
|
173
189
|
} catch (error) {
|
|
174
|
-
console.warn(
|
|
190
|
+
console.warn("Failed to load messages from localStorage:", error);
|
|
175
191
|
this.messages = [];
|
|
176
192
|
}
|
|
177
193
|
}
|
|
@@ -184,7 +200,32 @@ export default class Chat extends LightningElement {
|
|
|
184
200
|
// Add welcome message after clearing
|
|
185
201
|
this.addMessage("Hello! How can I help you today?", "assistant");
|
|
186
202
|
} catch (error) {
|
|
187
|
-
console.warn(
|
|
203
|
+
console.warn("Failed to clear messages from localStorage:", error);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private checkAndOpenAfterReload() {
|
|
208
|
+
try {
|
|
209
|
+
const shouldOpen = localStorage.getItem(Chat.OPEN_STATE_KEY);
|
|
210
|
+
if (shouldOpen === "true") {
|
|
211
|
+
// Clear the flag
|
|
212
|
+
localStorage.removeItem(Chat.OPEN_STATE_KEY);
|
|
213
|
+
// Open the chat
|
|
214
|
+
this._isOpen = true;
|
|
215
|
+
this.updateBodyClass();
|
|
216
|
+
|
|
217
|
+
// Dispatch custom event to notify parent components
|
|
218
|
+
this.dispatchEvent(
|
|
219
|
+
new CustomEvent("chatopened", {
|
|
220
|
+
detail: { opened: true }
|
|
221
|
+
})
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
} catch (error) {
|
|
225
|
+
console.warn(
|
|
226
|
+
"Failed to check open state from localStorage:",
|
|
227
|
+
error
|
|
228
|
+
);
|
|
188
229
|
}
|
|
189
230
|
}
|
|
190
231
|
|
|
@@ -194,7 +235,7 @@ export default class Chat extends LightningElement {
|
|
|
194
235
|
}
|
|
195
236
|
|
|
196
237
|
handleKeyDown(event: KeyboardEvent) {
|
|
197
|
-
if (event.key ===
|
|
238
|
+
if (event.key === "Enter" && !event.shiftKey) {
|
|
198
239
|
event.preventDefault();
|
|
199
240
|
this.sendMessage();
|
|
200
241
|
}
|
|
@@ -215,14 +256,16 @@ export default class Chat extends LightningElement {
|
|
|
215
256
|
this.currentMessage = "";
|
|
216
257
|
|
|
217
258
|
// Clear input
|
|
218
|
-
const input = this.template.querySelector(
|
|
259
|
+
const input = this.template.querySelector(
|
|
260
|
+
".chat-input"
|
|
261
|
+
) as HTMLInputElement;
|
|
219
262
|
if (input) {
|
|
220
263
|
input.value = "";
|
|
221
264
|
}
|
|
222
265
|
|
|
223
266
|
// Simulate assistant typing
|
|
224
267
|
this.isAssistantTyping = true;
|
|
225
|
-
|
|
268
|
+
|
|
226
269
|
// Simulate assistant response after a delay
|
|
227
270
|
setTimeout(() => {
|
|
228
271
|
this.isAssistantTyping = false;
|
|
@@ -233,22 +276,34 @@ export default class Chat extends LightningElement {
|
|
|
233
276
|
private simulateAssistantResponse(userMessage: string) {
|
|
234
277
|
// Simple response simulation - in a real implementation, this would call an API
|
|
235
278
|
let response = `I received your message: "${userMessage}". `;
|
|
236
|
-
|
|
237
|
-
if (
|
|
279
|
+
|
|
280
|
+
if (
|
|
281
|
+
userMessage.toLowerCase().includes("hello") ||
|
|
282
|
+
userMessage.toLowerCase().includes("hi")
|
|
283
|
+
) {
|
|
238
284
|
response = "Hello! Nice to meet you. How can I assist you today?";
|
|
239
285
|
} else if (userMessage.toLowerCase().includes("help")) {
|
|
240
|
-
response =
|
|
241
|
-
|
|
242
|
-
|
|
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?";
|
|
243
294
|
} else {
|
|
244
|
-
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?";
|
|
245
297
|
}
|
|
246
298
|
|
|
247
299
|
this.addMessage(response, "assistant");
|
|
248
300
|
}
|
|
249
301
|
|
|
250
302
|
formatTimestamp(timestamp: Date) {
|
|
251
|
-
return timestamp.toLocaleTimeString([], {
|
|
303
|
+
return timestamp.toLocaleTimeString([], {
|
|
304
|
+
hour: "2-digit",
|
|
305
|
+
minute: "2-digit"
|
|
306
|
+
});
|
|
252
307
|
}
|
|
253
308
|
|
|
254
309
|
get sendButtonDisabled() {
|
|
@@ -256,51 +311,60 @@ export default class Chat extends LightningElement {
|
|
|
256
311
|
}
|
|
257
312
|
|
|
258
313
|
handleCloseClick() {
|
|
259
|
-
this.
|
|
314
|
+
this._isOpen = false;
|
|
260
315
|
this.updateBodyClass();
|
|
261
|
-
|
|
316
|
+
|
|
262
317
|
// Dispatch custom event to notify parent components
|
|
263
|
-
this.dispatchEvent(
|
|
264
|
-
|
|
265
|
-
|
|
318
|
+
this.dispatchEvent(
|
|
319
|
+
new CustomEvent("chatclosed", {
|
|
320
|
+
detail: { closed: true }
|
|
321
|
+
})
|
|
322
|
+
);
|
|
266
323
|
}
|
|
267
324
|
|
|
268
325
|
handleClearClick() {
|
|
269
326
|
this.clearMessages();
|
|
270
|
-
|
|
327
|
+
|
|
271
328
|
// Dispatch custom event to notify parent components
|
|
272
|
-
this.dispatchEvent(
|
|
273
|
-
|
|
274
|
-
|
|
329
|
+
this.dispatchEvent(
|
|
330
|
+
new CustomEvent("chatcleared", {
|
|
331
|
+
detail: { cleared: true }
|
|
332
|
+
})
|
|
333
|
+
);
|
|
275
334
|
}
|
|
276
335
|
|
|
277
336
|
handleOpenClick() {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
337
|
+
try {
|
|
338
|
+
// Set flag to open chat after reload
|
|
339
|
+
localStorage.setItem(Chat.OPEN_STATE_KEY, "true");
|
|
340
|
+
} catch (error) {
|
|
341
|
+
console.warn("Failed to set open state in localStorage:", error);
|
|
342
|
+
}
|
|
343
|
+
// Hard reload the page to clear cache when opening chat
|
|
344
|
+
window.location.reload();
|
|
285
345
|
}
|
|
286
346
|
|
|
287
347
|
openChat() {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
348
|
+
try {
|
|
349
|
+
// Set flag to open chat after reload
|
|
350
|
+
localStorage.setItem(Chat.OPEN_STATE_KEY, "true");
|
|
351
|
+
} catch (error) {
|
|
352
|
+
console.warn("Failed to set open state in localStorage:", error);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Hard reload the page to clear cache when opening chat
|
|
356
|
+
window.location.reload();
|
|
295
357
|
}
|
|
296
358
|
|
|
297
359
|
closeChat() {
|
|
298
|
-
this.
|
|
360
|
+
this._isOpen = false;
|
|
299
361
|
this.updateBodyClass();
|
|
300
|
-
|
|
362
|
+
|
|
301
363
|
// Dispatch custom event to notify parent components
|
|
302
|
-
this.dispatchEvent(
|
|
303
|
-
|
|
304
|
-
|
|
364
|
+
this.dispatchEvent(
|
|
365
|
+
new CustomEvent("chatclosed", {
|
|
366
|
+
detail: { closed: true }
|
|
367
|
+
})
|
|
368
|
+
);
|
|
305
369
|
}
|
|
306
|
-
}
|
|
370
|
+
}
|