@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
|
@@ -13,32 +13,89 @@ export class ChatContainer {
|
|
|
13
13
|
showWatermark = false;
|
|
14
14
|
/** Height of the panel when position='float' */
|
|
15
15
|
floatHeight = '600px';
|
|
16
|
+
/** Color theme for the panel */
|
|
17
|
+
theme = 'light';
|
|
16
18
|
isMobile = false;
|
|
19
|
+
floatLeft = '24px';
|
|
20
|
+
floatTop = '24px';
|
|
17
21
|
containerRef;
|
|
22
|
+
messagesAreaRef;
|
|
18
23
|
dragState = null;
|
|
19
|
-
|
|
24
|
+
// Named arrow so we can reliably add/remove it without leaking
|
|
25
|
+
dragListener = (e) => {
|
|
26
|
+
const container = this.containerRef;
|
|
27
|
+
if (!container)
|
|
28
|
+
return;
|
|
29
|
+
const { clientX, clientY } = e.detail;
|
|
30
|
+
const rect = container.getBoundingClientRect();
|
|
31
|
+
this.dragState = {
|
|
32
|
+
startX: clientX,
|
|
33
|
+
startY: clientY,
|
|
34
|
+
initLeft: rect.left,
|
|
35
|
+
initTop: rect.top,
|
|
36
|
+
};
|
|
37
|
+
// Capture current rendered position into state so subsequent re-renders don't jump
|
|
38
|
+
this.floatLeft = `${rect.left}px`;
|
|
39
|
+
this.floatTop = `${rect.top}px`;
|
|
40
|
+
const onMove = (me) => {
|
|
41
|
+
if (!this.dragState)
|
|
42
|
+
return;
|
|
43
|
+
const dx = me.clientX - this.dragState.startX;
|
|
44
|
+
const dy = me.clientY - this.dragState.startY;
|
|
45
|
+
let newLeft = this.dragState.initLeft + dx;
|
|
46
|
+
let newTop = this.dragState.initTop + dy;
|
|
47
|
+
const vw = window.innerWidth;
|
|
48
|
+
const vh = window.innerHeight;
|
|
49
|
+
const w = container.offsetWidth;
|
|
50
|
+
const h = container.offsetHeight;
|
|
51
|
+
newLeft = Math.max(0, Math.min(newLeft, vw - w));
|
|
52
|
+
newTop = Math.max(0, Math.min(newTop, vh - h));
|
|
53
|
+
this.floatLeft = `${newLeft}px`;
|
|
54
|
+
this.floatTop = `${newTop}px`;
|
|
55
|
+
};
|
|
56
|
+
const onUp = () => {
|
|
57
|
+
this.dragState = null;
|
|
58
|
+
document.removeEventListener('pointermove', onMove);
|
|
59
|
+
document.removeEventListener('pointerup', onUp);
|
|
60
|
+
};
|
|
61
|
+
document.addEventListener('pointermove', onMove);
|
|
62
|
+
document.addEventListener('pointerup', onUp);
|
|
63
|
+
};
|
|
64
|
+
constructor() {
|
|
65
|
+
// Bind so window.removeEventListener can match the reference
|
|
66
|
+
this.checkMobile = this.checkMobile.bind(this);
|
|
67
|
+
}
|
|
20
68
|
componentWillLoad() {
|
|
21
69
|
this.checkMobile();
|
|
22
70
|
}
|
|
23
71
|
componentDidLoad() {
|
|
24
72
|
if (typeof window !== 'undefined') {
|
|
25
|
-
this.
|
|
26
|
-
this.checkMobile();
|
|
27
|
-
});
|
|
28
|
-
this.resizeObserver.observe(document.body);
|
|
73
|
+
window.addEventListener('resize', this.checkMobile);
|
|
29
74
|
if (this.position === 'float') {
|
|
30
75
|
this.setupDrag();
|
|
31
76
|
}
|
|
32
77
|
}
|
|
33
78
|
}
|
|
79
|
+
isOpenChanged(newVal) {
|
|
80
|
+
if (newVal && this.autoScroll && this.messagesAreaRef) {
|
|
81
|
+
requestAnimationFrame(() => {
|
|
82
|
+
this.messagesAreaRef.scrollTop = this.messagesAreaRef.scrollHeight;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
34
86
|
positionChanged(newVal) {
|
|
35
87
|
if (newVal === 'float') {
|
|
36
88
|
this.setupDrag();
|
|
37
89
|
}
|
|
38
90
|
}
|
|
39
91
|
disconnectedCallback() {
|
|
40
|
-
|
|
41
|
-
|
|
92
|
+
window.removeEventListener('resize', this.checkMobile);
|
|
93
|
+
this.el.removeEventListener('headerDragStart', this.dragListener);
|
|
94
|
+
}
|
|
95
|
+
/** Programmatically scroll the messages area to the bottom */
|
|
96
|
+
async scrollToBottom() {
|
|
97
|
+
if (this.messagesAreaRef) {
|
|
98
|
+
this.messagesAreaRef.scrollTop = this.messagesAreaRef.scrollHeight;
|
|
42
99
|
}
|
|
43
100
|
}
|
|
44
101
|
checkMobile() {
|
|
@@ -47,50 +104,9 @@ export class ChatContainer {
|
|
|
47
104
|
}
|
|
48
105
|
}
|
|
49
106
|
setupDrag() {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
// Listen on the host element so composed events from ai-chat-header's shadow DOM
|
|
54
|
-
// are received. (composedPath() hides cross-shadow-root elements from shadow listeners.)
|
|
55
|
-
this.el.addEventListener('headerDragStart', (e) => {
|
|
56
|
-
const { clientX, clientY } = e.detail;
|
|
57
|
-
const rect = container.getBoundingClientRect();
|
|
58
|
-
this.dragState = {
|
|
59
|
-
startX: clientX,
|
|
60
|
-
startY: clientY,
|
|
61
|
-
initLeft: rect.left,
|
|
62
|
-
initTop: rect.top,
|
|
63
|
-
};
|
|
64
|
-
// Switch from bottom/right anchoring to top/left so we can move freely
|
|
65
|
-
container.style.right = 'auto';
|
|
66
|
-
container.style.bottom = 'auto';
|
|
67
|
-
container.style.left = `${rect.left}px`;
|
|
68
|
-
container.style.top = `${rect.top}px`;
|
|
69
|
-
const onMove = (me) => {
|
|
70
|
-
if (!this.dragState)
|
|
71
|
-
return;
|
|
72
|
-
const dx = me.clientX - this.dragState.startX;
|
|
73
|
-
const dy = me.clientY - this.dragState.startY;
|
|
74
|
-
let newLeft = this.dragState.initLeft + dx;
|
|
75
|
-
let newTop = this.dragState.initTop + dy;
|
|
76
|
-
// Clamp to viewport edges
|
|
77
|
-
const vw = window.innerWidth;
|
|
78
|
-
const vh = window.innerHeight;
|
|
79
|
-
const w = container.offsetWidth;
|
|
80
|
-
const h = container.offsetHeight;
|
|
81
|
-
newLeft = Math.max(0, Math.min(newLeft, vw - w));
|
|
82
|
-
newTop = Math.max(0, Math.min(newTop, vh - h));
|
|
83
|
-
container.style.left = `${newLeft}px`;
|
|
84
|
-
container.style.top = `${newTop}px`;
|
|
85
|
-
};
|
|
86
|
-
const onUp = () => {
|
|
87
|
-
this.dragState = null;
|
|
88
|
-
document.removeEventListener('pointermove', onMove);
|
|
89
|
-
document.removeEventListener('pointerup', onUp);
|
|
90
|
-
};
|
|
91
|
-
document.addEventListener('pointermove', onMove);
|
|
92
|
-
document.addEventListener('pointerup', onUp);
|
|
93
|
-
});
|
|
107
|
+
// Remove any existing listener before adding to prevent accumulation
|
|
108
|
+
this.el.removeEventListener('headerDragStart', this.dragListener);
|
|
109
|
+
this.el.addEventListener('headerDragStart', this.dragListener);
|
|
94
110
|
}
|
|
95
111
|
getContainerClasses() {
|
|
96
112
|
const classes = ['chat-container', this.position];
|
|
@@ -110,14 +126,24 @@ export class ChatContainer {
|
|
|
110
126
|
if (this.position === 'float') {
|
|
111
127
|
style.width = this.width;
|
|
112
128
|
style.height = this.floatHeight;
|
|
129
|
+
style.left = this.floatLeft;
|
|
130
|
+
style.top = this.floatTop;
|
|
113
131
|
}
|
|
114
132
|
else {
|
|
115
133
|
style.width = this.width;
|
|
116
134
|
}
|
|
117
135
|
return style;
|
|
118
136
|
}
|
|
137
|
+
isDark() {
|
|
138
|
+
if (this.theme === 'dark')
|
|
139
|
+
return true;
|
|
140
|
+
if (this.theme === 'auto' && typeof window !== 'undefined') {
|
|
141
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
119
145
|
render() {
|
|
120
|
-
return (h(Host, { key: '
|
|
146
|
+
return (h(Host, { key: 'dd656127f6974b6eef12f64c78c072f2c1f92a6a', class: { dark: this.isDark() } }, h("div", { key: '0eb43bc5c8b80e5576146f01998e5279eb860162', class: this.getContainerClasses(), style: this.getContainerStyle(), ref: el => (this.containerRef = el) }, this.showWatermark && (h("div", { key: '363046f0e73ab7aa0209d5f41562031fb34d6c2d', class: "watermark" }, h("ai-icon", { key: '1f3c26ba6a145f8532f6e1a40ea344249e8e5b43', name: "watermark", size: 133 }))), h("slot", { key: '3ec10548660ff721c094be36a15a152fdf6dac78', name: "header" }), h("div", { key: '9babb87041287ac400322b910aa64f7a7b1743a5', class: "messages-area", ref: el => (this.messagesAreaRef = el) }, h("slot", { key: '937c0c0bd675e5a619d46883e7c1f890754aae4b' })), h("slot", { key: '0982b8bc96537c6cecca757926d5849ba386bd69', name: "footer" }))));
|
|
121
147
|
}
|
|
122
148
|
static get is() { return "ai-chat-container"; }
|
|
123
149
|
static get encapsulation() { return "shadow"; }
|
|
@@ -252,17 +278,63 @@ export class ChatContainer {
|
|
|
252
278
|
"reflect": false,
|
|
253
279
|
"attribute": "float-height",
|
|
254
280
|
"defaultValue": "'600px'"
|
|
281
|
+
},
|
|
282
|
+
"theme": {
|
|
283
|
+
"type": "string",
|
|
284
|
+
"mutable": false,
|
|
285
|
+
"complexType": {
|
|
286
|
+
"original": "'light' | 'dark' | 'auto'",
|
|
287
|
+
"resolved": "\"auto\" | \"dark\" | \"light\"",
|
|
288
|
+
"references": {}
|
|
289
|
+
},
|
|
290
|
+
"required": false,
|
|
291
|
+
"optional": false,
|
|
292
|
+
"docs": {
|
|
293
|
+
"tags": [],
|
|
294
|
+
"text": "Color theme for the panel"
|
|
295
|
+
},
|
|
296
|
+
"getter": false,
|
|
297
|
+
"setter": false,
|
|
298
|
+
"reflect": false,
|
|
299
|
+
"attribute": "theme",
|
|
300
|
+
"defaultValue": "'light'"
|
|
255
301
|
}
|
|
256
302
|
};
|
|
257
303
|
}
|
|
258
304
|
static get states() {
|
|
259
305
|
return {
|
|
260
|
-
"isMobile": {}
|
|
306
|
+
"isMobile": {},
|
|
307
|
+
"floatLeft": {},
|
|
308
|
+
"floatTop": {}
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
static get methods() {
|
|
312
|
+
return {
|
|
313
|
+
"scrollToBottom": {
|
|
314
|
+
"complexType": {
|
|
315
|
+
"signature": "() => Promise<void>",
|
|
316
|
+
"parameters": [],
|
|
317
|
+
"references": {
|
|
318
|
+
"Promise": {
|
|
319
|
+
"location": "global",
|
|
320
|
+
"id": "global::Promise"
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
"return": "Promise<void>"
|
|
324
|
+
},
|
|
325
|
+
"docs": {
|
|
326
|
+
"text": "Programmatically scroll the messages area to the bottom",
|
|
327
|
+
"tags": []
|
|
328
|
+
}
|
|
329
|
+
}
|
|
261
330
|
};
|
|
262
331
|
}
|
|
263
332
|
static get elementRef() { return "el"; }
|
|
264
333
|
static get watchers() {
|
|
265
334
|
return [{
|
|
335
|
+
"propName": "isOpen",
|
|
336
|
+
"methodName": "isOpenChanged"
|
|
337
|
+
}, {
|
|
266
338
|
"propName": "position",
|
|
267
339
|
"methodName": "positionChanged"
|
|
268
340
|
}];
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
:host {
|
|
2
2
|
display: block;
|
|
3
|
-
font-family: var(--ai-font-family, "PingARLT", sans-serif);
|
|
4
3
|
}
|
|
5
4
|
|
|
6
5
|
/* ── Container ──────────────────────────────────────────────────── */
|
|
@@ -9,16 +8,12 @@
|
|
|
9
8
|
align-items: center;
|
|
10
9
|
gap: 8px;
|
|
11
10
|
padding: 16px;
|
|
12
|
-
background: var(--ai-bg-card
|
|
13
|
-
border-bottom: 1px solid var(--ai-border-light
|
|
11
|
+
background: var(--ai-bg-card);
|
|
12
|
+
border-bottom: 1px solid var(--ai-border-light);
|
|
14
13
|
width: 100%;
|
|
15
14
|
box-sizing: border-box;
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
.header-container {
|
|
19
|
-
direction: rtl;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
17
|
/* ── Action & Back Buttons ──────────────────────────────────────── */
|
|
23
18
|
.drag-btn {
|
|
24
19
|
cursor: grab;
|
|
@@ -37,7 +32,7 @@
|
|
|
37
32
|
display: flex;
|
|
38
33
|
align-items: center;
|
|
39
34
|
justify-content: center;
|
|
40
|
-
background: var(--ai-bg-card
|
|
35
|
+
background: var(--ai-bg-card);
|
|
41
36
|
border: none;
|
|
42
37
|
border-radius: 8px;
|
|
43
38
|
cursor: pointer;
|
|
@@ -48,7 +43,7 @@
|
|
|
48
43
|
|
|
49
44
|
.action-btn:hover,
|
|
50
45
|
.back-btn:hover {
|
|
51
|
-
background: var(--ai-bg-surface
|
|
46
|
+
background: var(--ai-bg-surface);
|
|
52
47
|
}
|
|
53
48
|
|
|
54
49
|
.action-btn:focus-visible,
|
|
@@ -59,7 +54,7 @@
|
|
|
59
54
|
|
|
60
55
|
.action-btn:active,
|
|
61
56
|
.back-btn:active {
|
|
62
|
-
background: var(--ai-bg-surface
|
|
57
|
+
background: var(--ai-bg-surface);
|
|
63
58
|
}
|
|
64
59
|
|
|
65
60
|
/* ── Actions Group ──────────────────────────────────────────────── */
|
|
@@ -110,7 +105,7 @@
|
|
|
110
105
|
.title {
|
|
111
106
|
font-size: 16px;
|
|
112
107
|
font-weight: 700;
|
|
113
|
-
color: var(--ai-text-primary
|
|
108
|
+
color: var(--ai-text-primary);
|
|
114
109
|
white-space: nowrap;
|
|
115
110
|
line-height: normal;
|
|
116
111
|
min-width: 0;
|
|
@@ -130,15 +125,30 @@
|
|
|
130
125
|
width: 40px;
|
|
131
126
|
height: 40px;
|
|
132
127
|
border-radius: 9999px;
|
|
133
|
-
border: 1px solid var(--ai-border-default
|
|
128
|
+
border: 1px solid var(--ai-border-default);
|
|
134
129
|
object-fit: cover;
|
|
135
130
|
display: block;
|
|
136
131
|
}
|
|
137
132
|
|
|
133
|
+
.avatar-fallback {
|
|
134
|
+
width: 40px;
|
|
135
|
+
height: 40px;
|
|
136
|
+
border-radius: 9999px;
|
|
137
|
+
border: 1px solid var(--ai-border-default);
|
|
138
|
+
background: var(--ai-bg-surface);
|
|
139
|
+
color: var(--ai-text-primary);
|
|
140
|
+
font-size: 16px;
|
|
141
|
+
font-weight: 600;
|
|
142
|
+
display: flex;
|
|
143
|
+
align-items: center;
|
|
144
|
+
justify-content: center;
|
|
145
|
+
flex-shrink: 0;
|
|
146
|
+
}
|
|
147
|
+
|
|
138
148
|
.online-dot {
|
|
139
149
|
position: absolute;
|
|
140
150
|
bottom: 0;
|
|
141
|
-
|
|
151
|
+
inset-inline-end: 0;
|
|
142
152
|
width: 10px;
|
|
143
153
|
height: 10px;
|
|
144
154
|
display: inline-flex;
|
|
@@ -147,20 +157,36 @@
|
|
|
147
157
|
color: var(--ai-bg-card); /* used as stroke to match card bg in both themes */
|
|
148
158
|
}
|
|
149
159
|
|
|
160
|
+
/* Status indicator color variants */
|
|
161
|
+
.online-dot.status-online {
|
|
162
|
+
color: #22c55e; /* green */
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.online-dot.status-offline {
|
|
166
|
+
color: #9ca3af; /* gray */
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.online-dot.status-busy {
|
|
170
|
+
color: #ef4444; /* red */
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.online-dot.status-away {
|
|
174
|
+
color: #f59e0b; /* amber */
|
|
175
|
+
}
|
|
176
|
+
|
|
150
177
|
/* ── Text Block (human mode) ────────────────────────────────────── */
|
|
151
178
|
.text-block {
|
|
152
179
|
display: flex;
|
|
153
180
|
flex-direction: column;
|
|
154
|
-
align-items: flex-
|
|
181
|
+
align-items: flex-start;
|
|
155
182
|
gap: 0;
|
|
156
183
|
flex-shrink: 0;
|
|
157
184
|
}
|
|
158
185
|
|
|
159
|
-
|
|
160
186
|
.agent-name {
|
|
161
187
|
font-size: 14px;
|
|
162
188
|
font-weight: 500;
|
|
163
|
-
color: var(--ai-text-primary
|
|
189
|
+
color: var(--ai-text-primary);
|
|
164
190
|
line-height: 20px;
|
|
165
191
|
white-space: nowrap;
|
|
166
192
|
}
|
|
@@ -168,7 +194,7 @@
|
|
|
168
194
|
.agent-status {
|
|
169
195
|
font-size: 14px;
|
|
170
196
|
font-weight: 400;
|
|
171
|
-
color: var(--ai-text-secondary
|
|
197
|
+
color: var(--ai-text-secondary);
|
|
172
198
|
line-height: 20px;
|
|
173
199
|
white-space: nowrap;
|
|
174
200
|
}
|
|
@@ -184,3 +210,10 @@
|
|
|
184
210
|
.icon-wrap svg {
|
|
185
211
|
display: block;
|
|
186
212
|
}
|
|
213
|
+
|
|
214
|
+
/* ── Directional icon flip (back-btn arrow points right in RTL = "back";
|
|
215
|
+
in LTR it must flip to point left) ──────────────────────────────────── */
|
|
216
|
+
:host-context([dir='ltr']) .back-btn .icon-wrap,
|
|
217
|
+
:host([dir='ltr']) .back-btn .icon-wrap {
|
|
218
|
+
transform: scaleX(-1);
|
|
219
|
+
}
|
|
@@ -14,13 +14,16 @@ export class AiChatHeader {
|
|
|
14
14
|
/** Human mode: show the back button */
|
|
15
15
|
showBack = true;
|
|
16
16
|
isDraggable = false;
|
|
17
|
+
/** Human mode: status indicator variant */
|
|
18
|
+
statusIndicator = 'online';
|
|
19
|
+
avatarError = false;
|
|
17
20
|
/** Cancel / close button */
|
|
18
21
|
closeClick;
|
|
19
22
|
/** Pencil-edit button (agent mode) */
|
|
20
23
|
editClick;
|
|
21
24
|
/** Title / chevron click → open conversation list (agent mode) */
|
|
22
25
|
dropdownClick;
|
|
23
|
-
/**
|
|
26
|
+
/** More options button */
|
|
24
27
|
moreClick;
|
|
25
28
|
/** Back-arrow button (human mode) */
|
|
26
29
|
backClick;
|
|
@@ -36,28 +39,35 @@ export class AiChatHeader {
|
|
|
36
39
|
const svg = `<svg width="${width}" height="${height}" viewBox="${icon.viewBox}" fill="none" xmlns="http://www.w3.org/2000/svg">${icon.content}</svg>`;
|
|
37
40
|
return h("span", { class: "icon-wrap", innerHTML: svg });
|
|
38
41
|
}
|
|
42
|
+
renderAvatar() {
|
|
43
|
+
const fallbackLetter = this.agentName ? this.agentName.charAt(0) : '?';
|
|
44
|
+
if (!this.agentAvatar || this.avatarError) {
|
|
45
|
+
return h("div", { class: "avatar-fallback" }, fallbackLetter);
|
|
46
|
+
}
|
|
47
|
+
return (h("img", { class: "avatar", src: this.agentAvatar, alt: this.agentName, onError: () => { this.avatarError = true; } }));
|
|
48
|
+
}
|
|
49
|
+
renderDragBtn() {
|
|
50
|
+
return this.isDraggable && (h("button", { class: "action-btn drag-btn", "aria-label": "\u0633\u062D\u0628 / Drag", onPointerDown: (e) => {
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
this.headerDragStart.emit({ clientX: e.clientX, clientY: e.clientY });
|
|
53
|
+
} }, this.renderIcon('drag', 11, 15)));
|
|
54
|
+
}
|
|
39
55
|
renderAgentMode() {
|
|
40
56
|
return [
|
|
41
|
-
this.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
} }, this.renderIcon('drag', 11, 15))),
|
|
45
|
-
h("div", { class: "content agent" }, h("span", { class: "title" }, this.conversation), h("button", { class: "dropdown-trigger", onClick: () => this.dropdownClick.emit() }, this.renderIcon('chevron-down', 24, 24))),
|
|
46
|
-
h("div", { class: "actions" }, h("button", { class: "action-btn", onClick: () => this.editClick.emit() }, this.renderIcon('pencil-edit', 22, 22)), h("button", { class: "action-btn", onClick: () => this.moreClick.emit() }, this.renderIcon('hand', 22, 22)), h("button", { class: "action-btn", onClick: () => this.closeClick.emit() }, this.renderIcon('cancel', 22, 22))),
|
|
57
|
+
this.renderDragBtn(),
|
|
58
|
+
h("div", { class: "content agent dropdown-trigger", role: "button", onClick: () => this.dropdownClick.emit(), "aria-haspopup": "listbox", "aria-expanded": false, "aria-label": `${this.conversation}, افتح قائمة المحادثات` }, h("span", { class: "title" }, this.conversation), h("span", { class: "dropdown-chevron" }, this.renderIcon('chevron-down', 24, 24))),
|
|
59
|
+
h("div", { class: "actions" }, h("button", { class: "action-btn", "aria-label": "\u062A\u0639\u062F\u064A\u0644 / Edit", onClick: () => this.editClick.emit() }, this.renderIcon('pencil-edit', 22, 22)), h("button", { class: "action-btn", "aria-label": "\u0627\u0644\u0645\u0632\u064A\u062F / More", onClick: () => this.moreClick.emit() }, this.renderIcon('ellipsis', 22, 22)), h("button", { class: "action-btn", "aria-label": "\u0625\u063A\u0644\u0627\u0642 / Close", onClick: () => this.closeClick.emit() }, this.renderIcon('cancel', 22, 22))),
|
|
47
60
|
];
|
|
48
61
|
}
|
|
49
62
|
renderHumanMode() {
|
|
50
63
|
return [
|
|
51
|
-
this.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
} }, this.renderIcon('drag', 11, 15))),
|
|
55
|
-
h("div", { class: "content human" }, this.showBack && (h("button", { class: "back-btn", onClick: () => this.backClick.emit() }, this.renderIcon('arrow-right', 24, 24))), h("div", { class: "avatar-wrapper" }, h("img", { class: "avatar", src: this.agentAvatar, alt: this.agentName }), h("span", { class: "online-dot" }, this.renderIcon('online-dot', 10, 10))), h("div", { class: "text-block" }, h("span", { class: "agent-name" }, this.agentName), h("span", { class: "agent-status" }, this.agentStatus))),
|
|
56
|
-
h("div", { class: "actions" }, h("button", { class: "action-btn", onClick: () => this.moreClick.emit() }, this.renderIcon('hand', 22, 22)), h("button", { class: "action-btn", onClick: () => this.closeClick.emit() }, this.renderIcon('cancel', 22, 22))),
|
|
64
|
+
this.renderDragBtn(),
|
|
65
|
+
h("div", { class: "content human" }, this.showBack && (h("button", { class: "back-btn", "aria-label": "\u0631\u062C\u0648\u0639 / Back", onClick: () => this.backClick.emit() }, this.renderIcon('arrow-right', 24, 24))), h("div", { class: "avatar-wrapper" }, this.renderAvatar(), h("span", { class: `online-dot status-${this.statusIndicator}` }, this.renderIcon('online-dot', 10, 10))), h("div", { class: "text-block" }, h("span", { class: "agent-name" }, this.agentName), this.agentStatus && h("span", { class: "agent-status" }, this.agentStatus))),
|
|
66
|
+
h("div", { class: "actions" }, h("button", { class: "action-btn", "aria-label": "\u0627\u0644\u0645\u0632\u064A\u062F / More", onClick: () => this.moreClick.emit() }, this.renderIcon('ellipsis', 22, 22)), h("button", { class: "action-btn", "aria-label": "\u0625\u063A\u0644\u0627\u0642 / Close", onClick: () => this.closeClick.emit() }, this.renderIcon('cancel', 22, 22))),
|
|
57
67
|
];
|
|
58
68
|
}
|
|
59
69
|
render() {
|
|
60
|
-
return (h(Host, { key: '
|
|
70
|
+
return (h(Host, { key: 'f0b6caccf925bc1388b2680b49e4aec3a44fa353' }, h("div", { key: 'fd144266fb338cbb5d70a3fb1ae9c1b39710ffc5', class: "header-container" }, this.mode === 'agent' ? this.renderAgentMode() : this.renderHumanMode())));
|
|
61
71
|
}
|
|
62
72
|
static get is() { return "ai-chat-header"; }
|
|
63
73
|
static get encapsulation() { return "shadow"; }
|
|
@@ -212,9 +222,34 @@ export class AiChatHeader {
|
|
|
212
222
|
"reflect": false,
|
|
213
223
|
"attribute": "is-draggable",
|
|
214
224
|
"defaultValue": "false"
|
|
225
|
+
},
|
|
226
|
+
"statusIndicator": {
|
|
227
|
+
"type": "string",
|
|
228
|
+
"mutable": false,
|
|
229
|
+
"complexType": {
|
|
230
|
+
"original": "'online' | 'offline' | 'busy' | 'away'",
|
|
231
|
+
"resolved": "\"away\" | \"busy\" | \"offline\" | \"online\"",
|
|
232
|
+
"references": {}
|
|
233
|
+
},
|
|
234
|
+
"required": false,
|
|
235
|
+
"optional": false,
|
|
236
|
+
"docs": {
|
|
237
|
+
"tags": [],
|
|
238
|
+
"text": "Human mode: status indicator variant"
|
|
239
|
+
},
|
|
240
|
+
"getter": false,
|
|
241
|
+
"setter": false,
|
|
242
|
+
"reflect": false,
|
|
243
|
+
"attribute": "status-indicator",
|
|
244
|
+
"defaultValue": "'online'"
|
|
215
245
|
}
|
|
216
246
|
};
|
|
217
247
|
}
|
|
248
|
+
static get states() {
|
|
249
|
+
return {
|
|
250
|
+
"avatarError": {}
|
|
251
|
+
};
|
|
252
|
+
}
|
|
218
253
|
static get events() {
|
|
219
254
|
return [{
|
|
220
255
|
"method": "closeClick",
|
|
@@ -269,7 +304,7 @@ export class AiChatHeader {
|
|
|
269
304
|
"composed": true,
|
|
270
305
|
"docs": {
|
|
271
306
|
"tags": [],
|
|
272
|
-
"text": "
|
|
307
|
+
"text": "More options button"
|
|
273
308
|
},
|
|
274
309
|
"complexType": {
|
|
275
310
|
"original": "void",
|