@salla.sa/ui-ai-kit-core 1.0.0 → 1.1.1
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/dist/cjs/ai-card.cjs.entry.js +2 -2
- package/dist/cjs/ai-chat-container.cjs.entry.js +84 -57
- package/dist/cjs/ai-chat-header.cjs.entry.js +27 -17
- package/dist/cjs/ai-chat-message.cjs.entry.js +1456 -21
- package/dist/cjs/ai-conversation-list.cjs.entry.js +80 -0
- package/dist/cjs/ai-conversation-summary.cjs.entry.js +33 -0
- package/dist/cjs/ai-icon.cjs.entry.js +2 -2
- package/dist/cjs/ai-link.cjs.entry.js +4 -4
- package/dist/cjs/ai-loading.cjs.entry.js +35 -22
- package/dist/cjs/ai-message-input.cjs.entry.js +48 -15
- package/dist/cjs/ai-rating.cjs.entry.js +2 -2
- package/dist/cjs/ai-route-decision.cjs.entry.js +48 -0
- package/dist/cjs/ai-suggestion.cjs.entry.js +4 -4
- package/dist/cjs/ai-voice-input.cjs.entry.js +62 -14
- package/dist/cjs/{icon-registry-dmfLA-Dj.js → icon-registry-BKb9-2Nt.js} +24 -0
- package/dist/cjs/{index-DLJcLHFH.js → index-BkNg07SW.js} +1 -1
- package/dist/cjs/loader.cjs.js +2 -2
- package/dist/cjs/ui-ai-kit.cjs.js +2 -2
- package/dist/collection/collection-manifest.json +3 -0
- package/dist/collection/components/ai-card/ai-card.css +5 -8
- package/dist/collection/components/ai-chat-container/ai-chat-container.css +17 -14
- package/dist/collection/components/ai-chat-container/ai-chat-container.js +125 -53
- package/dist/collection/components/ai-chat-header/ai-chat-header.css +50 -17
- package/dist/collection/components/ai-chat-header/ai-chat-header.js +50 -15
- package/dist/collection/components/ai-chat-message/ai-chat-message.css +47 -38
- package/dist/collection/components/ai-chat-message/ai-chat-message.js +68 -18
- package/dist/collection/components/ai-conversation-list/ai-conversation-list.css +196 -0
- package/dist/collection/components/ai-conversation-list/ai-conversation-list.js +176 -0
- package/dist/collection/components/ai-conversation-summary/ai-conversation-summary.css +103 -0
- package/dist/collection/components/ai-conversation-summary/ai-conversation-summary.js +118 -0
- package/dist/collection/components/ai-icon/ai-icon.js +1 -1
- package/dist/collection/components/ai-link/ai-link.css +3 -7
- package/dist/collection/components/ai-link/ai-link.js +1 -1
- package/dist/collection/components/ai-loading/ai-loading.css +149 -20
- package/dist/collection/components/ai-loading/ai-loading.js +100 -23
- package/dist/collection/components/ai-message-input/ai-message-input.css +41 -37
- package/dist/collection/components/ai-message-input/ai-message-input.js +100 -19
- package/dist/collection/components/ai-rating/ai-rating.css +8 -14
- package/dist/collection/components/ai-route-decision/ai-route-decision.css +132 -0
- package/dist/collection/components/ai-route-decision/ai-route-decision.js +195 -0
- package/dist/collection/components/ai-suggestion/ai-suggestion.css +5 -11
- package/dist/collection/components/ai-suggestion/ai-suggestion.js +2 -2
- package/dist/collection/components/ai-voice-input/ai-voice-input.css +26 -21
- package/dist/collection/components/ai-voice-input/ai-voice-input.js +103 -13
- package/dist/collection/utils/icon-registry.js +24 -0
- package/dist/components/ai-card.js +1 -1
- package/dist/components/ai-chat-container.js +1 -1
- package/dist/components/ai-chat-header.js +1 -1
- package/dist/components/ai-chat-message.js +2 -1
- package/dist/components/ai-conversation-list.d.ts +11 -0
- package/dist/components/ai-conversation-list.js +1 -0
- package/dist/components/ai-conversation-summary.d.ts +11 -0
- package/dist/components/ai-conversation-summary.js +1 -0
- package/dist/components/ai-icon.js +1 -1
- package/dist/components/ai-link.js +1 -1
- package/dist/components/ai-loading.js +1 -1
- package/dist/components/ai-message-input.js +1 -1
- package/dist/components/ai-rating.js +1 -1
- package/dist/components/ai-route-decision.d.ts +11 -0
- package/dist/components/ai-route-decision.js +1 -0
- package/dist/components/ai-suggestion.js +1 -1
- package/dist/components/ai-voice-input.js +1 -1
- package/dist/components/index.js +1 -1
- package/dist/components/p-DCr8F_XV.js +1 -0
- package/dist/components/p-DnO4dikr.js +1 -0
- package/dist/components/{p-CY6emva2.js → p-Dr2tAPV7.js} +1 -1
- package/dist/{ui-ai-kit/p-DYv5ef4M.js → components/p-SJZ6Ujn9.js} +1 -1
- package/dist/esm/ai-card.entry.js +2 -2
- package/dist/esm/ai-chat-container.entry.js +84 -57
- package/dist/esm/ai-chat-header.entry.js +27 -17
- package/dist/esm/ai-chat-message.entry.js +1456 -21
- package/dist/esm/ai-conversation-list.entry.js +78 -0
- package/dist/esm/ai-conversation-summary.entry.js +31 -0
- package/dist/esm/ai-icon.entry.js +2 -2
- package/dist/esm/ai-link.entry.js +4 -4
- package/dist/esm/ai-loading.entry.js +35 -22
- package/dist/esm/ai-message-input.entry.js +48 -15
- package/dist/esm/ai-rating.entry.js +2 -2
- package/dist/esm/ai-route-decision.entry.js +46 -0
- package/dist/esm/ai-suggestion.entry.js +4 -4
- package/dist/esm/ai-voice-input.entry.js +62 -14
- package/dist/esm/{icon-registry-DYv5ef4M.js → icon-registry-SJZ6Ujn9.js} +24 -0
- package/dist/esm/{index-7hrZ8FOQ.js → index-B0yIzgh4.js} +1 -1
- package/dist/esm/loader.js +3 -3
- package/dist/esm/ui-ai-kit.js +3 -3
- package/dist/types/components/ai-chat-container/ai-chat-container.d.ts +11 -1
- package/dist/types/components/ai-chat-header/ai-chat-header.d.ts +6 -1
- package/dist/types/components/ai-conversation-list/ai-conversation-list.d.ts +24 -0
- package/dist/types/components/ai-conversation-summary/ai-conversation-summary.d.ts +12 -0
- package/dist/types/components/ai-loading/ai-loading.d.ts +12 -6
- package/dist/types/components/ai-message-input/ai-message-input.d.ts +17 -3
- package/dist/types/components/ai-route-decision/ai-route-decision.d.ts +21 -0
- package/dist/types/components/ai-voice-input/ai-voice-input.d.ts +7 -0
- package/dist/types/components.d.ts +333 -9
- package/dist/types/index.d.ts +2 -0
- package/dist/types/utils/icon-registry.d.ts +1 -1
- package/dist/ui-ai-kit/p-21c4fc1f.entry.js +1 -0
- package/dist/ui-ai-kit/p-2955439f.entry.js +1 -0
- package/dist/ui-ai-kit/p-5c9e9822.entry.js +1 -0
- package/dist/ui-ai-kit/p-5caf1c38.entry.js +1 -0
- package/dist/ui-ai-kit/p-6d3505e9.entry.js +1 -0
- package/dist/ui-ai-kit/p-74c5c83f.entry.js +1 -0
- package/dist/ui-ai-kit/p-87e9739b.entry.js +1 -0
- package/dist/ui-ai-kit/p-B0yIzgh4.js +2 -0
- package/dist/{components/p-DYv5ef4M.js → ui-ai-kit/p-SJZ6Ujn9.js} +1 -1
- package/dist/ui-ai-kit/p-a099fcfb.entry.js +1 -0
- package/dist/ui-ai-kit/p-a9e4eaef.entry.js +1 -0
- package/dist/ui-ai-kit/p-b28af13a.entry.js +1 -0
- package/dist/ui-ai-kit/p-d1bb1ad0.entry.js +1 -0
- package/dist/ui-ai-kit/p-eb0c7e7a.entry.js +1 -0
- package/dist/ui-ai-kit/{p-455daa17.entry.js → p-eec6f083.entry.js} +1 -1
- package/dist/ui-ai-kit/p-ef07638f.entry.js +2 -0
- package/dist/ui-ai-kit/ui-ai-kit.css +1 -1
- package/dist/ui-ai-kit/ui-ai-kit.esm.js +1 -1
- package/package.json +5 -14
- package/dist/collection/components/ai-card/ai-card.stories.js +0 -52
- package/dist/collection/components/ai-chat-container/ai-chat-container.stories.js +0 -160
- package/dist/collection/components/ai-chat-header/ai-chat-header.stories.js +0 -138
- package/dist/collection/components/ai-chat-message/ai-chat-message.stories.js +0 -164
- package/dist/collection/components/ai-link/ai-link.stories.js +0 -79
- package/dist/collection/components/ai-loading/ai-loading.stories.js +0 -145
- package/dist/collection/components/ai-message-input/ai-message-input.stories.js +0 -125
- package/dist/collection/components/ai-rating/ai-rating.stories.js +0 -78
- package/dist/collection/components/ai-suggestion/ai-suggestion.stories.js +0 -62
- package/dist/collection/components/ai-voice-input/ai-voice-input.stories.js +0 -118
- package/dist/components/p-CWjXxYJI.js +0 -1
- package/dist/types/components/ai-card/ai-card.stories.d.ts +0 -7
- package/dist/types/components/ai-chat-container/ai-chat-container.stories.d.ts +0 -7
- package/dist/types/components/ai-chat-header/ai-chat-header.stories.d.ts +0 -8
- package/dist/types/components/ai-chat-message/ai-chat-message.stories.d.ts +0 -10
- package/dist/types/components/ai-link/ai-link.stories.d.ts +0 -8
- package/dist/types/components/ai-loading/ai-loading.stories.d.ts +0 -10
- package/dist/types/components/ai-message-input/ai-message-input.stories.d.ts +0 -13
- package/dist/types/components/ai-rating/ai-rating.stories.d.ts +0 -8
- package/dist/types/components/ai-suggestion/ai-suggestion.stories.d.ts +0 -8
- package/dist/types/components/ai-voice-input/ai-voice-input.stories.d.ts +0 -9
- package/dist/ui-ai-kit/p-11facfad.entry.js +0 -1
- package/dist/ui-ai-kit/p-128a2ed4.entry.js +0 -1
- package/dist/ui-ai-kit/p-227bdb8f.entry.js +0 -1
- package/dist/ui-ai-kit/p-56163e8c.entry.js +0 -1
- package/dist/ui-ai-kit/p-6d21d0fd.entry.js +0 -1
- package/dist/ui-ai-kit/p-6ddcd77b.entry.js +0 -1
- package/dist/ui-ai-kit/p-7hrZ8FOQ.js +0 -2
- package/dist/ui-ai-kit/p-8e90143e.entry.js +0 -1
- package/dist/ui-ai-kit/p-9938c277.entry.js +0 -1
- package/dist/ui-ai-kit/p-dc5b4a7f.entry.js +0 -1
- package/dist/ui-ai-kit/p-fb1702de.entry.js +0 -1
- package/readme.md +0 -111
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
/* ─── Custom Properties ──────────────────────────────────────────────────── */
|
|
2
2
|
:host {
|
|
3
|
-
--ai-msg-user-bg: var(--ai-user-bubble-bg
|
|
4
|
-
--ai-msg-user-
|
|
5
|
-
--ai-msg-
|
|
6
|
-
--ai-msg-agent-
|
|
7
|
-
--ai-msg-agent-color: var(--ai-text-primary, #333333);
|
|
8
|
-
--ai-msg-agent-border: 1px solid var(--ai-border-default, #eee);
|
|
3
|
+
--ai-msg-user-bg: var(--ai-user-bubble-bg);
|
|
4
|
+
--ai-msg-user-border: 1px solid var(--ai-border-default);
|
|
5
|
+
--ai-msg-agent-bg: var(--ai-agent-bubble-bg);
|
|
6
|
+
--ai-msg-agent-border: 1px solid var(--ai-border-default);
|
|
9
7
|
--ai-msg-border-radius: 16px;
|
|
10
8
|
--ai-msg-padding: 16px;
|
|
11
9
|
--ai-msg-font-size: 14px;
|
|
12
|
-
--ai-msg-action-active-bg: var(--ai-accent
|
|
13
|
-
--ai-msg-timestamp-color: var(--ai-text-secondary, #737373);
|
|
10
|
+
--ai-msg-action-active-bg: var(--ai-accent);
|
|
14
11
|
--ai-user-msg-max-width: 80%;
|
|
15
12
|
--ai-agent-msg-max-width: 80%;
|
|
16
13
|
|
|
17
14
|
display: block;
|
|
18
|
-
direction: rtl;
|
|
19
|
-
font-family: var(--ai-font-family, "PingARLT", sans-serif);
|
|
20
15
|
}
|
|
21
16
|
|
|
22
17
|
/* ─── Message Row ────────────────────────────────────────────────────────── */
|
|
@@ -33,7 +28,7 @@
|
|
|
33
28
|
|
|
34
29
|
.user-bubble {
|
|
35
30
|
background: var(--ai-msg-user-bg);
|
|
36
|
-
color: var(--ai-
|
|
31
|
+
color: var(--ai-text-primary);
|
|
37
32
|
border: var(--ai-msg-user-border);
|
|
38
33
|
border-radius: var(--ai-msg-border-radius);
|
|
39
34
|
padding: var(--ai-msg-padding);
|
|
@@ -41,12 +36,12 @@
|
|
|
41
36
|
max-width: var(--ai-user-msg-max-width);
|
|
42
37
|
line-height: 1.5;
|
|
43
38
|
word-break: break-word;
|
|
44
|
-
box-shadow:
|
|
39
|
+
box-shadow: var(--ai-shadow-sm);
|
|
45
40
|
}
|
|
46
41
|
|
|
47
42
|
.user-row .timestamp {
|
|
48
43
|
font-size: 12px;
|
|
49
|
-
color: var(--ai-
|
|
44
|
+
color: var(--ai-text-secondary);
|
|
50
45
|
margin-top: 4px;
|
|
51
46
|
}
|
|
52
47
|
|
|
@@ -68,14 +63,14 @@
|
|
|
68
63
|
|
|
69
64
|
.agent-bubble {
|
|
70
65
|
background: var(--ai-msg-agent-bg);
|
|
71
|
-
color: var(--ai-
|
|
66
|
+
color: var(--ai-text-primary);
|
|
72
67
|
border: var(--ai-msg-agent-border);
|
|
73
68
|
border-radius: var(--ai-msg-border-radius);
|
|
74
69
|
padding: var(--ai-msg-padding);
|
|
75
70
|
font-size: var(--ai-msg-font-size);
|
|
76
71
|
line-height: 1.6;
|
|
77
72
|
word-break: break-word;
|
|
78
|
-
box-shadow:
|
|
73
|
+
box-shadow: var(--ai-shadow-sm);
|
|
79
74
|
display: flex;
|
|
80
75
|
flex-direction: column;
|
|
81
76
|
gap: 12px;
|
|
@@ -115,16 +110,16 @@
|
|
|
115
110
|
}
|
|
116
111
|
|
|
117
112
|
.message-content code {
|
|
118
|
-
background: var(--ai-bg-surface
|
|
113
|
+
background: var(--ai-bg-surface);
|
|
119
114
|
border-radius: 4px;
|
|
120
115
|
padding: 1px 5px;
|
|
121
|
-
font-family:
|
|
116
|
+
font-family: var(--ai-font-family-mono);
|
|
122
117
|
font-size: 13px;
|
|
123
|
-
color: var(--ai-text-primary
|
|
118
|
+
color: var(--ai-text-primary);
|
|
124
119
|
}
|
|
125
120
|
|
|
126
121
|
.message-content pre {
|
|
127
|
-
background: var(--ai-bg-surface
|
|
122
|
+
background: var(--ai-bg-surface);
|
|
128
123
|
border-radius: 8px;
|
|
129
124
|
padding: 10px 12px;
|
|
130
125
|
overflow-x: auto;
|
|
@@ -174,17 +169,17 @@
|
|
|
174
169
|
width: 32px;
|
|
175
170
|
height: 32px;
|
|
176
171
|
border-radius: 8px;
|
|
177
|
-
background: var(--ai-bg-card
|
|
178
|
-
border: 1px solid var(--ai-border-default
|
|
172
|
+
background: var(--ai-bg-card);
|
|
173
|
+
border: 1px solid var(--ai-border-default);
|
|
179
174
|
cursor: pointer;
|
|
180
|
-
color: var(--ai-text-primary
|
|
175
|
+
color: var(--ai-text-primary);
|
|
181
176
|
transition: background 0.15s, color 0.15s;
|
|
182
177
|
padding: 0;
|
|
183
178
|
flex-shrink: 0;
|
|
184
179
|
}
|
|
185
180
|
|
|
186
181
|
.action-btn:hover {
|
|
187
|
-
background: var(--ai-bg-surface
|
|
182
|
+
background: var(--ai-bg-surface);
|
|
188
183
|
}
|
|
189
184
|
|
|
190
185
|
.action-btn.copy-success {
|
|
@@ -193,14 +188,13 @@
|
|
|
193
188
|
border-color: var(--ai-accent);
|
|
194
189
|
}
|
|
195
190
|
|
|
196
|
-
|
|
197
191
|
.feedback-group {
|
|
198
192
|
display: flex;
|
|
199
193
|
align-items: center;
|
|
200
|
-
border: 1px solid var(--ai-border-default
|
|
194
|
+
border: 1px solid var(--ai-border-default);
|
|
201
195
|
border-radius: 8px;
|
|
202
196
|
overflow: hidden;
|
|
203
|
-
background: var(--ai-bg-card
|
|
197
|
+
background: var(--ai-bg-card);
|
|
204
198
|
}
|
|
205
199
|
|
|
206
200
|
.feedback-btn {
|
|
@@ -209,11 +203,11 @@
|
|
|
209
203
|
justify-content: center;
|
|
210
204
|
height: 32px;
|
|
211
205
|
padding: 0 8px;
|
|
212
|
-
background: var(--ai-bg-card
|
|
206
|
+
background: var(--ai-bg-card);
|
|
213
207
|
border: none;
|
|
214
|
-
border-inline-start: 1px solid var(--ai-border-default
|
|
208
|
+
border-inline-start: 1px solid var(--ai-border-default);
|
|
215
209
|
cursor: pointer;
|
|
216
|
-
color: var(--ai-text-primary
|
|
210
|
+
color: var(--ai-text-primary);
|
|
217
211
|
transition: background 0.15s, color 0.15s;
|
|
218
212
|
}
|
|
219
213
|
|
|
@@ -222,7 +216,7 @@
|
|
|
222
216
|
}
|
|
223
217
|
|
|
224
218
|
.feedback-btn:hover {
|
|
225
|
-
background: var(--ai-bg-surface
|
|
219
|
+
background: var(--ai-bg-surface);
|
|
226
220
|
}
|
|
227
221
|
|
|
228
222
|
.feedback-btn.active {
|
|
@@ -244,7 +238,7 @@
|
|
|
244
238
|
align-items: center;
|
|
245
239
|
justify-content: flex-start;
|
|
246
240
|
gap: 6px;
|
|
247
|
-
color: var(--ai-text-muted
|
|
241
|
+
color: var(--ai-text-muted);
|
|
248
242
|
}
|
|
249
243
|
|
|
250
244
|
.agent-info-name,
|
|
@@ -275,16 +269,26 @@
|
|
|
275
269
|
width: 8px;
|
|
276
270
|
height: 8px;
|
|
277
271
|
border-radius: 50%;
|
|
278
|
-
background: var(--ai-text-muted
|
|
272
|
+
background: var(--ai-text-muted);
|
|
279
273
|
animation: typingBounce 1.2s ease-in-out infinite;
|
|
280
274
|
}
|
|
281
275
|
|
|
282
|
-
.typing-dot:nth-child(2) {
|
|
283
|
-
|
|
276
|
+
.typing-dot:nth-child(2) {
|
|
277
|
+
animation-delay: 0.2s;
|
|
278
|
+
}
|
|
279
|
+
.typing-dot:nth-child(3) {
|
|
280
|
+
animation-delay: 0.4s;
|
|
281
|
+
}
|
|
284
282
|
|
|
285
283
|
@keyframes typingBounce {
|
|
286
|
-
0%,
|
|
287
|
-
|
|
284
|
+
0%,
|
|
285
|
+
60%,
|
|
286
|
+
100% {
|
|
287
|
+
transform: translateY(0);
|
|
288
|
+
}
|
|
289
|
+
30% {
|
|
290
|
+
transform: translateY(-6px);
|
|
291
|
+
}
|
|
288
292
|
}
|
|
289
293
|
|
|
290
294
|
/* ─── Streaming Cursor ───────────────────────────────────────────────────── */
|
|
@@ -299,6 +303,11 @@
|
|
|
299
303
|
}
|
|
300
304
|
|
|
301
305
|
@keyframes cursorBlink {
|
|
302
|
-
0%,
|
|
303
|
-
|
|
306
|
+
0%,
|
|
307
|
+
100% {
|
|
308
|
+
opacity: 1;
|
|
309
|
+
}
|
|
310
|
+
50% {
|
|
311
|
+
opacity: 0;
|
|
312
|
+
}
|
|
304
313
|
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { Host, h } from "@stencil/core";
|
|
2
2
|
import { iconRegistry } from "../../utils/icon-registry";
|
|
3
|
+
import DOMPurify from "dompurify";
|
|
4
|
+
const SANITIZE_CONFIG = {
|
|
5
|
+
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'del', 'code', 'pre', 'ul', 'ol', 'li', 'h2', 'h3', 'a', 'blockquote', 'table', 'thead', 'tbody', 'tr', 'th', 'td'],
|
|
6
|
+
ALLOWED_ATTR: ['href', 'target', 'rel'],
|
|
7
|
+
};
|
|
3
8
|
export class AiChatMessage {
|
|
4
9
|
role = 'user';
|
|
5
10
|
content = '';
|
|
@@ -28,16 +33,59 @@ export class AiChatMessage {
|
|
|
28
33
|
parseMarkdown(text) {
|
|
29
34
|
let html = text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
30
35
|
const blocks = [];
|
|
36
|
+
// Fenced code blocks
|
|
31
37
|
html = html.replace(/```(?:\w+)?\n?([\s\S]*?)```/g, (_, code) => {
|
|
32
38
|
const idx = blocks.length;
|
|
33
39
|
blocks.push(`<pre><code>${code}</code></pre>`);
|
|
34
40
|
return `\x00BLOCK${idx}\x00`;
|
|
35
41
|
});
|
|
42
|
+
// Pipe tables (must run before paragraph wrapping)
|
|
43
|
+
html = html.replace(/((?:^[ \t]*\|.+\|\n?)+)/gm, match => {
|
|
44
|
+
const rows = match.trim().split('\n').filter(l => l.trim());
|
|
45
|
+
if (rows.length < 2)
|
|
46
|
+
return match;
|
|
47
|
+
const isSep = (r) => /^[\s|:-]+$/.test(r);
|
|
48
|
+
let tableHtml = '<table>';
|
|
49
|
+
let inBody = false;
|
|
50
|
+
rows.forEach((row, i) => {
|
|
51
|
+
if (isSep(row)) {
|
|
52
|
+
inBody = true;
|
|
53
|
+
tableHtml += '<tbody>';
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const cells = row.split('|').filter((_, j, a) => j > 0 && j < a.length - 1);
|
|
57
|
+
if (!inBody && i === 0) {
|
|
58
|
+
tableHtml += `<thead><tr>${cells.map(c => `<th>${c.trim()}</th>`).join('')}</tr></thead>`;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
tableHtml += `<tr>${cells.map(c => `<td>${c.trim()}</td>`).join('')}</tr>`;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
if (inBody)
|
|
65
|
+
tableHtml += '</tbody>';
|
|
66
|
+
tableHtml += '</table>';
|
|
67
|
+
const idx = blocks.length;
|
|
68
|
+
blocks.push(tableHtml);
|
|
69
|
+
return `\x00BLOCK${idx}\x00`;
|
|
70
|
+
});
|
|
71
|
+
// Blockquotes — > because > was already escaped
|
|
72
|
+
html = html.replace(/((?:^> .+\n?)+)/gm, match => {
|
|
73
|
+
const content = match.replace(/^> /gm, '').trim();
|
|
74
|
+
const idx = blocks.length;
|
|
75
|
+
blocks.push(`<blockquote>${content}</blockquote>`);
|
|
76
|
+
return `\x00BLOCK${idx}\x00`;
|
|
77
|
+
});
|
|
78
|
+
// Inline code
|
|
36
79
|
html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
80
|
+
// Headings
|
|
37
81
|
html = html.replace(/^## (.+)$/gm, '<h2>$1</h2>');
|
|
38
82
|
html = html.replace(/^### (.+)$/gm, '<h3>$1</h3>');
|
|
83
|
+
// Bold, italic, strikethrough, links
|
|
39
84
|
html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
|
40
85
|
html = html.replace(/\*([^*\n]+)\*/g, '<em>$1</em>');
|
|
86
|
+
html = html.replace(/~~(.+?)~~/g, '<del>$1</del>');
|
|
87
|
+
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
|
|
88
|
+
// Unordered lists
|
|
41
89
|
html = html.replace(/((?:^[ \t]*[-*] .+\n?)+)/gm, match => {
|
|
42
90
|
const items = match
|
|
43
91
|
.trim()
|
|
@@ -45,11 +93,11 @@ export class AiChatMessage {
|
|
|
45
93
|
.filter(l => l.trim())
|
|
46
94
|
.map(l => `<li>${l.replace(/^[ \t]*[-*] /, '')}</li>`)
|
|
47
95
|
.join('');
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
})()}`;
|
|
96
|
+
const idx = blocks.length;
|
|
97
|
+
blocks.push(`<ul>${items}</ul>`);
|
|
98
|
+
return `\x00BLOCK${idx}\x00`;
|
|
52
99
|
});
|
|
100
|
+
// Ordered lists
|
|
53
101
|
html = html.replace(/((?:^[ \t]*\d+\. .+\n?)+)/gm, match => {
|
|
54
102
|
const items = match
|
|
55
103
|
.trim()
|
|
@@ -57,10 +105,9 @@ export class AiChatMessage {
|
|
|
57
105
|
.filter(l => l.trim())
|
|
58
106
|
.map(l => `<li>${l.replace(/^[ \t]*\d+\. /, '')}</li>`)
|
|
59
107
|
.join('');
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
})()}`;
|
|
108
|
+
const idx = blocks.length;
|
|
109
|
+
blocks.push(`<ol>${items}</ol>`);
|
|
110
|
+
return `\x00BLOCK${idx}\x00`;
|
|
64
111
|
});
|
|
65
112
|
html = html
|
|
66
113
|
.split(/\n{2,}/)
|
|
@@ -79,8 +126,7 @@ export class AiChatMessage {
|
|
|
79
126
|
getRenderedContent() {
|
|
80
127
|
if (!this.content)
|
|
81
128
|
return '';
|
|
82
|
-
|
|
83
|
-
return html;
|
|
129
|
+
return DOMPurify.sanitize(this.parseMarkdown(this.content), SANITIZE_CONFIG);
|
|
84
130
|
}
|
|
85
131
|
getRelativeTime() {
|
|
86
132
|
if (!this.timestamp)
|
|
@@ -89,15 +135,19 @@ export class AiChatMessage {
|
|
|
89
135
|
const date = new Date(this.timestamp);
|
|
90
136
|
if (isNaN(date.getTime()))
|
|
91
137
|
return this.timestamp;
|
|
92
|
-
const
|
|
138
|
+
const diffMs = Date.now() - date.getTime();
|
|
139
|
+
const diffMin = Math.floor(diffMs / 60000);
|
|
140
|
+
const diffHour = Math.floor(diffMin / 60);
|
|
141
|
+
const diffDay = Math.floor(diffHour / 24);
|
|
142
|
+
const lang = (typeof document !== 'undefined' && document.documentElement.lang) || 'ar';
|
|
143
|
+
const rtf = new Intl.RelativeTimeFormat(lang, { numeric: 'auto' });
|
|
93
144
|
if (diffMin < 1)
|
|
94
|
-
return '
|
|
145
|
+
return rtf.format(0, 'second');
|
|
95
146
|
if (diffMin < 60)
|
|
96
|
-
return
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return `منذ ${Math.floor(hours / 24)} يوم`;
|
|
147
|
+
return rtf.format(-diffMin, 'minute');
|
|
148
|
+
if (diffHour < 24)
|
|
149
|
+
return rtf.format(-diffHour, 'hour');
|
|
150
|
+
return rtf.format(-diffDay, 'day');
|
|
101
151
|
}
|
|
102
152
|
catch {
|
|
103
153
|
return this.timestamp;
|
|
@@ -145,7 +195,7 @@ export class AiChatMessage {
|
|
|
145
195
|
} })), h("slot", null), showActions && this.renderActionsBar()), (this.agentName || this.timestamp) && (h("div", { class: "agent-info" }, this.agentName && h("span", { class: "agent-info-name" }, this.agentName), this.agentName && this.timestamp && this.renderIcon('eclipse', 10), this.timestamp && h("span", { class: "agent-info-time" }, this.getRelativeTime()))))));
|
|
146
196
|
}
|
|
147
197
|
render() {
|
|
148
|
-
return h(Host, { key: '
|
|
198
|
+
return h(Host, { key: 'a3da55a7f023469f2538b363ff691674c241227e' }, this.role === 'user' ? this.renderUserMessage() : this.renderAgentMessage());
|
|
149
199
|
}
|
|
150
200
|
static get is() { return "ai-chat-message"; }
|
|
151
201
|
static get encapsulation() { return "shadow"; }
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
display: block;
|
|
3
|
+
height: 100%;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.conversation-list {
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
gap: 8px;
|
|
10
|
+
height: 100%;
|
|
11
|
+
background: var(--ai-bg-surface);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/* ── Scrollable list ───────────────────────────────────── */
|
|
15
|
+
|
|
16
|
+
.list-scroll {
|
|
17
|
+
flex: 1;
|
|
18
|
+
overflow-y: auto;
|
|
19
|
+
padding: 0 8px 12px;
|
|
20
|
+
scrollbar-width: thin;
|
|
21
|
+
scrollbar-color: var(--ai-scrollbar-thumb) var(--ai-scrollbar-track);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.list-scroll::-webkit-scrollbar {
|
|
25
|
+
width: 4px;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.list-scroll::-webkit-scrollbar-track {
|
|
29
|
+
background: var(--ai-scrollbar-track);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.list-scroll::-webkit-scrollbar-thumb {
|
|
33
|
+
background: var(--ai-scrollbar-thumb);
|
|
34
|
+
border-radius: 4px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* ── Conversation item ─────────────────────────────────── */
|
|
38
|
+
|
|
39
|
+
.conv-item {
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: flex-start;
|
|
42
|
+
gap: 6px;
|
|
43
|
+
margin-top: 6px;
|
|
44
|
+
padding: 10px 10px 10px 6px;
|
|
45
|
+
border-radius: 10px;
|
|
46
|
+
cursor: pointer;
|
|
47
|
+
transition: background 0.15s;
|
|
48
|
+
position: relative;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.conv-item:hover {
|
|
52
|
+
background: var(--ai-bg-card);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.conv-item--active {
|
|
56
|
+
background: var(--ai-bg-card);
|
|
57
|
+
box-shadow: var(--ai-shadow-sm);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.conv-item__body {
|
|
61
|
+
flex: 1;
|
|
62
|
+
min-width: 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.conv-item__title {
|
|
66
|
+
margin: 0 0 3px;
|
|
67
|
+
font-size: 13px;
|
|
68
|
+
font-weight: 600;
|
|
69
|
+
color: var(--ai-text-primary);
|
|
70
|
+
white-space: nowrap;
|
|
71
|
+
overflow: hidden;
|
|
72
|
+
text-overflow: ellipsis;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.conv-item__preview {
|
|
76
|
+
margin: 0 0 5px;
|
|
77
|
+
font-size: 12px;
|
|
78
|
+
color: var(--ai-text-muted);
|
|
79
|
+
line-height: 1.45;
|
|
80
|
+
display: -webkit-box;
|
|
81
|
+
-webkit-line-clamp: 2;
|
|
82
|
+
line-clamp: 2;
|
|
83
|
+
-webkit-box-orient: vertical;
|
|
84
|
+
overflow: hidden;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.conv-item__meta {
|
|
88
|
+
display: flex;
|
|
89
|
+
align-items: center;
|
|
90
|
+
gap: 8px;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.conv-item__time {
|
|
94
|
+
font-size: 11px;
|
|
95
|
+
color: var(--ai-text-muted);
|
|
96
|
+
flex-shrink: 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* ── Rating dots ───────────────────────────────────────── */
|
|
100
|
+
|
|
101
|
+
.rating-dots {
|
|
102
|
+
display: flex;
|
|
103
|
+
align-items: center;
|
|
104
|
+
gap: 3px;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.rating-dot {
|
|
108
|
+
width: 6px;
|
|
109
|
+
height: 6px;
|
|
110
|
+
border-radius: 50%;
|
|
111
|
+
background: var(--ai-border-default);
|
|
112
|
+
transition: background 0.15s;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.rating-dot--filled {
|
|
116
|
+
background: var(--ai-accent-warning, #ffaf44);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* ── Delete button ─────────────────────────────────────── */
|
|
120
|
+
|
|
121
|
+
.delete-btn {
|
|
122
|
+
flex-shrink: 0;
|
|
123
|
+
display: flex;
|
|
124
|
+
align-items: center;
|
|
125
|
+
justify-content: center;
|
|
126
|
+
width: 24px;
|
|
127
|
+
height: 24px;
|
|
128
|
+
padding: 0;
|
|
129
|
+
background: transparent;
|
|
130
|
+
border: none;
|
|
131
|
+
border-radius: 6px;
|
|
132
|
+
cursor: pointer;
|
|
133
|
+
color: var(--ai-text-muted);
|
|
134
|
+
opacity: 0;
|
|
135
|
+
transition: opacity 0.15s, background 0.15s, color 0.15s;
|
|
136
|
+
margin-block-start: 2px;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.conv-item:hover .delete-btn,
|
|
140
|
+
.conv-item--active .delete-btn {
|
|
141
|
+
opacity: 1;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.delete-btn:hover {
|
|
145
|
+
background: var(--ai-status-danger-bg, rgba(239, 68, 68, 0.1));
|
|
146
|
+
color: var(--ai-status-danger, #ef4444);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.delete-btn .icon-wrap {
|
|
150
|
+
display: inline-flex;
|
|
151
|
+
align-items: center;
|
|
152
|
+
line-height: 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* ── Skeleton loading ──────────────────────────────────── */
|
|
156
|
+
|
|
157
|
+
.skeleton-list {
|
|
158
|
+
display: flex;
|
|
159
|
+
flex-direction: column;
|
|
160
|
+
gap: 4px;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.skeleton-item {
|
|
164
|
+
padding: 10px;
|
|
165
|
+
border-radius: 10px;
|
|
166
|
+
display: flex;
|
|
167
|
+
flex-direction: column;
|
|
168
|
+
gap: 6px;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.skeleton-line {
|
|
172
|
+
border-radius: 6px;
|
|
173
|
+
background: var(--ai-shimmer-gradient);
|
|
174
|
+
background-size: 200% 100%;
|
|
175
|
+
animation: shimmer 2s linear infinite;
|
|
176
|
+
height: 12px;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.skeleton-line--title {
|
|
180
|
+
width: 65%;
|
|
181
|
+
height: 13px;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.skeleton-line--preview {
|
|
185
|
+
width: 100%;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.skeleton-line--meta {
|
|
189
|
+
width: 35%;
|
|
190
|
+
height: 10px;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
@keyframes shimmer {
|
|
194
|
+
0% { background-position: 200% 0; }
|
|
195
|
+
100% { background-position: -200% 0; }
|
|
196
|
+
}
|