@a.izzuddin/ai-chat 0.2.8 → 0.2.10
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/README.md +28 -13
- package/custom-elements.json +69 -16
- package/dist/index.d.mts +15 -2
- package/dist/index.d.ts +15 -2
- package/dist/index.js +153 -37
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +153 -37
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -14,7 +14,7 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
14
14
|
if (kind && result) __defProp(target, key, result);
|
|
15
15
|
return result;
|
|
16
16
|
};
|
|
17
|
-
var VERSION = "0.2.
|
|
17
|
+
var VERSION = "0.2.10";
|
|
18
18
|
var AIChat = class extends LitElement {
|
|
19
19
|
constructor() {
|
|
20
20
|
super();
|
|
@@ -25,6 +25,7 @@ var AIChat = class extends LitElement {
|
|
|
25
25
|
this.mode = "fullscreen";
|
|
26
26
|
this.initialMessages = [];
|
|
27
27
|
this.botAvatarUrl = "";
|
|
28
|
+
this.widgetIconUrl = "";
|
|
28
29
|
this.backgroundImageUrl = "";
|
|
29
30
|
this.widgetWidth = "380px";
|
|
30
31
|
this.widgetHeight = "600px";
|
|
@@ -42,6 +43,25 @@ var AIChat = class extends LitElement {
|
|
|
42
43
|
toggleWidget() {
|
|
43
44
|
this.isOpen = !this.isOpen;
|
|
44
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Clear all chat messages and reset to welcome message
|
|
48
|
+
* @public
|
|
49
|
+
*/
|
|
50
|
+
clearChat() {
|
|
51
|
+
this.clearMessagesFromStorage();
|
|
52
|
+
if (this.welcomeMessage) {
|
|
53
|
+
const welcomeText = this.welcomeSubtitle ? `${this.welcomeMessage}
|
|
54
|
+
|
|
55
|
+
${this.welcomeSubtitle}` : this.welcomeMessage;
|
|
56
|
+
this.messages = [{
|
|
57
|
+
id: "welcome-" + Date.now(),
|
|
58
|
+
role: "assistant",
|
|
59
|
+
content: welcomeText
|
|
60
|
+
}];
|
|
61
|
+
} else {
|
|
62
|
+
this.messages = [];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
45
65
|
lightenColor(hex, percent = 15) {
|
|
46
66
|
hex = hex.replace("#", "");
|
|
47
67
|
const r = parseInt(hex.substring(0, 2), 16);
|
|
@@ -52,23 +72,66 @@ var AIChat = class extends LitElement {
|
|
|
52
72
|
const newB = Math.min(255, Math.round(b + (255 - b) * (percent / 100)));
|
|
53
73
|
return `#${newR.toString(16).padStart(2, "0")}${newG.toString(16).padStart(2, "0")}${newB.toString(16).padStart(2, "0")}`;
|
|
54
74
|
}
|
|
75
|
+
getStorageKey() {
|
|
76
|
+
return `ai-chat-messages-${this.sessionId}`;
|
|
77
|
+
}
|
|
78
|
+
saveMessagesToStorage() {
|
|
79
|
+
try {
|
|
80
|
+
const storageKey = this.getStorageKey();
|
|
81
|
+
localStorage.setItem(storageKey, JSON.stringify(this.messages));
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.warn("Failed to save messages to localStorage:", error);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
loadMessagesFromStorage() {
|
|
87
|
+
try {
|
|
88
|
+
const storageKey = this.getStorageKey();
|
|
89
|
+
const saved = localStorage.getItem(storageKey);
|
|
90
|
+
if (saved) {
|
|
91
|
+
return JSON.parse(saved);
|
|
92
|
+
}
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.warn("Failed to load messages from localStorage:", error);
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
clearMessagesFromStorage() {
|
|
99
|
+
try {
|
|
100
|
+
const storageKey = this.getStorageKey();
|
|
101
|
+
localStorage.removeItem(storageKey);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.warn("Failed to clear messages from localStorage:", error);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
55
106
|
formatMessageContent(content) {
|
|
56
107
|
const escapeHtml = (text) => {
|
|
57
108
|
const div = document.createElement("div");
|
|
58
109
|
div.textContent = text;
|
|
59
110
|
return div.innerHTML;
|
|
60
111
|
};
|
|
61
|
-
let processedContent = content.replace(/(
|
|
112
|
+
let processedContent = content.replace(/([^\n])\s*(\d+\.\s+)/g, "$1\n$2");
|
|
113
|
+
processedContent = processedContent.replace(/(\d+\.\s+[^0-9]+?)(?=\s+\d+\.\s+|\s*$)/g, "$1\n");
|
|
62
114
|
processedContent = processedContent.replace(/(-\s+[^-]+?)(?=\s+-\s+|\s*$)/g, "$1\n");
|
|
63
115
|
const lines = processedContent.split("\n");
|
|
64
116
|
let formattedContent = "";
|
|
65
117
|
let inList = false;
|
|
66
118
|
let listType = null;
|
|
119
|
+
let orderedListCounter = 1;
|
|
120
|
+
const getNextListType = (startIndex) => {
|
|
121
|
+
for (let j = startIndex + 1; j < lines.length; j++) {
|
|
122
|
+
const nextLine = lines[j].trim();
|
|
123
|
+
if (nextLine === "") continue;
|
|
124
|
+
if (nextLine.match(/^[-*•]\s+/)) return "ul";
|
|
125
|
+
if (nextLine.match(/^\d+\.\s+/)) return "ol";
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
};
|
|
67
130
|
for (let i = 0; i < lines.length; i++) {
|
|
68
131
|
const line = lines[i];
|
|
69
132
|
const trimmedLine = line.trim();
|
|
70
133
|
const unorderedMatch = trimmedLine.match(/^[-*•]\s+(.+)$/);
|
|
71
|
-
const orderedMatch = trimmedLine.match(
|
|
134
|
+
const orderedMatch = trimmedLine.match(/^(\d+)\.\s+(.+)$/);
|
|
72
135
|
if (unorderedMatch) {
|
|
73
136
|
if (!inList || listType !== "ul") {
|
|
74
137
|
if (inList) formattedContent += listType === "ol" ? "</ol>" : "</ul>";
|
|
@@ -78,22 +141,40 @@ var AIChat = class extends LitElement {
|
|
|
78
141
|
}
|
|
79
142
|
formattedContent += `<li>${escapeHtml(unorderedMatch[1])}</li>`;
|
|
80
143
|
} else if (orderedMatch) {
|
|
144
|
+
const itemNumber = parseInt(orderedMatch[1], 10);
|
|
145
|
+
const itemText = orderedMatch[2];
|
|
81
146
|
if (!inList || listType !== "ol") {
|
|
82
147
|
if (inList) formattedContent += listType === "ol" ? "</ol>" : "</ul>";
|
|
83
|
-
|
|
148
|
+
if (itemNumber === 1) {
|
|
149
|
+
orderedListCounter = 1;
|
|
150
|
+
formattedContent += "<ol>";
|
|
151
|
+
} else {
|
|
152
|
+
formattedContent += `<ol start="${orderedListCounter}">`;
|
|
153
|
+
}
|
|
84
154
|
inList = true;
|
|
85
155
|
listType = "ol";
|
|
86
156
|
}
|
|
87
|
-
formattedContent += `<li>${escapeHtml(
|
|
157
|
+
formattedContent += `<li value="${itemNumber}">${escapeHtml(itemText)}</li>`;
|
|
158
|
+
orderedListCounter = itemNumber + 1;
|
|
88
159
|
} else {
|
|
89
|
-
if (inList) {
|
|
90
|
-
formattedContent += listType === "ol" ? "</ol>" : "</ul>";
|
|
91
|
-
inList = false;
|
|
92
|
-
listType = null;
|
|
93
|
-
}
|
|
94
160
|
if (trimmedLine === "") {
|
|
95
|
-
|
|
161
|
+
const nextListType = getNextListType(i);
|
|
162
|
+
if (inList && nextListType === listType) {
|
|
163
|
+
formattedContent += '<li style="list-style: none; height: 0.5em;"></li>';
|
|
164
|
+
} else {
|
|
165
|
+
if (inList) {
|
|
166
|
+
formattedContent += listType === "ol" ? "</ol>" : "</ul>";
|
|
167
|
+
inList = false;
|
|
168
|
+
listType = null;
|
|
169
|
+
}
|
|
170
|
+
formattedContent += "<br>";
|
|
171
|
+
}
|
|
96
172
|
} else {
|
|
173
|
+
if (inList) {
|
|
174
|
+
formattedContent += listType === "ol" ? "</ol>" : "</ul>";
|
|
175
|
+
inList = false;
|
|
176
|
+
listType = null;
|
|
177
|
+
}
|
|
97
178
|
formattedContent += escapeHtml(line) + "\n";
|
|
98
179
|
}
|
|
99
180
|
}
|
|
@@ -105,20 +186,37 @@ var AIChat = class extends LitElement {
|
|
|
105
186
|
}
|
|
106
187
|
connectedCallback() {
|
|
107
188
|
super.connectedCallback();
|
|
189
|
+
const savedMessages = this.loadMessagesFromStorage();
|
|
108
190
|
if (this.initialMessages && this.initialMessages.length > 0) {
|
|
109
191
|
this.messages = [...this.initialMessages];
|
|
192
|
+
} else if (savedMessages && savedMessages.length > 0) {
|
|
193
|
+
this.messages = savedMessages;
|
|
194
|
+
} else if (this.welcomeMessage) {
|
|
195
|
+
const welcomeText = this.welcomeSubtitle ? `${this.welcomeMessage}
|
|
196
|
+
|
|
197
|
+
${this.welcomeSubtitle}` : this.welcomeMessage;
|
|
198
|
+
this.messages = [{
|
|
199
|
+
id: "welcome-" + Date.now(),
|
|
200
|
+
role: "assistant",
|
|
201
|
+
content: welcomeText
|
|
202
|
+
}];
|
|
110
203
|
}
|
|
111
204
|
}
|
|
112
205
|
updated(changedProperties) {
|
|
113
206
|
super.updated(changedProperties);
|
|
114
207
|
if (changedProperties.has("messages")) {
|
|
115
208
|
this.scrollToBottom();
|
|
209
|
+
this.saveMessagesToStorage();
|
|
116
210
|
}
|
|
117
211
|
}
|
|
118
212
|
scrollToBottom() {
|
|
119
|
-
|
|
120
|
-
this.
|
|
121
|
-
|
|
213
|
+
setTimeout(() => {
|
|
214
|
+
const userMessages = this.shadowRoot?.querySelectorAll(".message.user");
|
|
215
|
+
if (userMessages && userMessages.length > 0) {
|
|
216
|
+
const lastUserMessage = userMessages[userMessages.length - 1];
|
|
217
|
+
lastUserMessage.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
218
|
+
}
|
|
219
|
+
}, 100);
|
|
122
220
|
}
|
|
123
221
|
handleInput(e) {
|
|
124
222
|
this.input = e.target.value;
|
|
@@ -171,7 +269,7 @@ var AIChat = class extends LitElement {
|
|
|
171
269
|
if (trimmedResponse.startsWith("{") || trimmedResponse.startsWith("[")) {
|
|
172
270
|
console.log("\u{1F504} Detected stringified JSON, parsing...");
|
|
173
271
|
try {
|
|
174
|
-
|
|
272
|
+
const innerData = JSON.parse(data.response);
|
|
175
273
|
console.log("\u2705 Parsed inner data with JSON.parse");
|
|
176
274
|
if (innerData && innerData.response && typeof innerData.response === "string") {
|
|
177
275
|
responseText = innerData.response;
|
|
@@ -306,26 +404,14 @@ Please check your API endpoint configuration.`
|
|
|
306
404
|
<!-- Messages Area -->
|
|
307
405
|
<div class="messages-area" style="--user-message-bg: ${this.userMessageBg}; --bot-message-bg: ${this.botMessageBg}; --primary-color: ${this.primaryColor}; --primary-color-light: ${primaryColorLight}; --primary-color-hover: ${this.primaryColorHover}; ${this.backgroundImageUrl ? `--background-image-url: url('${this.backgroundImageUrl}');` : ""}">
|
|
308
406
|
<div class="messages-container">
|
|
309
|
-
${this.messages.length === 0 ? html`
|
|
310
|
-
<div class="empty-state">
|
|
311
|
-
<div class="empty-state-avatar">
|
|
312
|
-
${this.botAvatarUrl ? html`<img src="${this.botAvatarUrl}" alt="Bot" class="empty-state-avatar-image" />` : html`<svg viewBox="0 0 24 24" fill="none" stroke="#9ca3af" stroke-width="2">
|
|
313
|
-
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
|
314
|
-
</svg>`}
|
|
315
|
-
</div>
|
|
316
|
-
<div class="empty-state-content">
|
|
317
|
-
<p class="empty-state-message">${this.welcomeMessage}</p>
|
|
318
|
-
${this.welcomeSubtitle ? html`<p class="empty-state-subtitle">${this.welcomeSubtitle}</p>` : ""}
|
|
319
|
-
</div>
|
|
320
|
-
</div>
|
|
321
|
-
` : ""}
|
|
322
|
-
|
|
323
407
|
${repeat(this.messages, (msg) => msg.id, (msg) => html`
|
|
324
|
-
<div
|
|
408
|
+
<div
|
|
409
|
+
class=${classMap({
|
|
325
410
|
message: true,
|
|
326
411
|
user: msg.role === "user",
|
|
327
412
|
assistant: msg.role === "assistant"
|
|
328
|
-
})}
|
|
413
|
+
})}
|
|
414
|
+
>
|
|
329
415
|
<div class="avatar">
|
|
330
416
|
${msg.role === "user" ? "U" : this.botAvatarUrl ? html`<img src="${this.botAvatarUrl}" alt="AI" class="avatar-image" />` : "AI"}
|
|
331
417
|
</div>
|
|
@@ -369,8 +455,6 @@ Please check your API endpoint configuration.`
|
|
|
369
455
|
</div>
|
|
370
456
|
</div>
|
|
371
457
|
` : ""}
|
|
372
|
-
|
|
373
|
-
<div ${(el) => this.messagesEndRef = el}></div>
|
|
374
458
|
</div>
|
|
375
459
|
</div>
|
|
376
460
|
|
|
@@ -380,7 +464,7 @@ Please check your API endpoint configuration.`
|
|
|
380
464
|
<input
|
|
381
465
|
type="text"
|
|
382
466
|
class="input-field"
|
|
383
|
-
placeholder="
|
|
467
|
+
placeholder="Taip mesej anda..."
|
|
384
468
|
.value=${this.input}
|
|
385
469
|
@input=${this.handleInput}
|
|
386
470
|
?disabled=${this.isLoading}
|
|
@@ -422,7 +506,10 @@ Please check your API endpoint configuration.`
|
|
|
422
506
|
|
|
423
507
|
<!-- Toggle Button -->
|
|
424
508
|
<button
|
|
425
|
-
class
|
|
509
|
+
class=${classMap({
|
|
510
|
+
"widget-button": true,
|
|
511
|
+
"widget-button-no-bg": !this.isOpen && !!this.widgetIconUrl
|
|
512
|
+
})}
|
|
426
513
|
style="--primary-color: ${this.primaryColor}; --primary-color-light: ${primaryColorLight};"
|
|
427
514
|
@click=${this.toggleWidget}
|
|
428
515
|
aria-label=${this.isOpen ? "Close chat" : "Open chat"}
|
|
@@ -432,6 +519,8 @@ Please check your API endpoint configuration.`
|
|
|
432
519
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
433
520
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
434
521
|
</svg>
|
|
522
|
+
` : this.widgetIconUrl ? html`
|
|
523
|
+
<img src="${this.widgetIconUrl}" alt="Chat" class="widget-button-icon" />
|
|
435
524
|
` : html`
|
|
436
525
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
437
526
|
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
|
@@ -494,12 +583,29 @@ AIChat.styles = css`
|
|
|
494
583
|
box-shadow: 0 6px 20px rgba(65, 105, 225, 0.4);
|
|
495
584
|
}
|
|
496
585
|
|
|
586
|
+
.widget-button-no-bg {
|
|
587
|
+
background: transparent;
|
|
588
|
+
box-shadow: none;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.widget-button-no-bg:hover {
|
|
592
|
+
background: transparent;
|
|
593
|
+
box-shadow: none;
|
|
594
|
+
transform: scale(1.1);
|
|
595
|
+
}
|
|
596
|
+
|
|
497
597
|
.widget-button svg {
|
|
498
598
|
width: 28px;
|
|
499
599
|
height: 28px;
|
|
500
600
|
color: white;
|
|
501
601
|
}
|
|
502
602
|
|
|
603
|
+
.widget-button-icon {
|
|
604
|
+
width: auto;
|
|
605
|
+
height: auto;
|
|
606
|
+
object-fit: cover;
|
|
607
|
+
}
|
|
608
|
+
|
|
503
609
|
.widget-window {
|
|
504
610
|
position: absolute;
|
|
505
611
|
bottom: 80px;
|
|
@@ -961,11 +1067,13 @@ AIChat.styles = css`
|
|
|
961
1067
|
margin: 0.5rem 0;
|
|
962
1068
|
padding-left: 1.5rem;
|
|
963
1069
|
white-space: normal;
|
|
1070
|
+
list-style-position: outside;
|
|
964
1071
|
}
|
|
965
1072
|
|
|
966
1073
|
.message-text li {
|
|
967
1074
|
margin: 0.25rem 0;
|
|
968
1075
|
white-space: normal;
|
|
1076
|
+
display: list-item;
|
|
969
1077
|
}
|
|
970
1078
|
|
|
971
1079
|
.message-text ul {
|
|
@@ -974,6 +1082,12 @@ AIChat.styles = css`
|
|
|
974
1082
|
|
|
975
1083
|
.message-text ol {
|
|
976
1084
|
list-style-type: decimal;
|
|
1085
|
+
counter-reset: list-counter;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
.message-text ol li {
|
|
1089
|
+
display: list-item;
|
|
1090
|
+
list-style-type: decimal;
|
|
977
1091
|
}
|
|
978
1092
|
|
|
979
1093
|
.faq-section {
|
|
@@ -1032,7 +1146,8 @@ AIChat.styles = css`
|
|
|
1032
1146
|
border-color: #3f3f46;
|
|
1033
1147
|
}
|
|
1034
1148
|
|
|
1035
|
-
|
|
1149
|
+
/* FAQ static item styles - commented out for now */
|
|
1150
|
+
/* .faq-item-static {
|
|
1036
1151
|
font-size: 0.875rem;
|
|
1037
1152
|
color: #6B7280;
|
|
1038
1153
|
padding: 0;
|
|
@@ -1043,7 +1158,7 @@ AIChat.styles = css`
|
|
|
1043
1158
|
|
|
1044
1159
|
:host([theme="dark"]) .faq-item-static {
|
|
1045
1160
|
color: #9CA3AF;
|
|
1046
|
-
}
|
|
1161
|
+
} */
|
|
1047
1162
|
|
|
1048
1163
|
.loading {
|
|
1049
1164
|
display: flex;
|
|
@@ -1172,6 +1287,7 @@ AIChat.properties = {
|
|
|
1172
1287
|
mode: { type: String, reflect: true },
|
|
1173
1288
|
initialMessages: { type: Array },
|
|
1174
1289
|
botAvatarUrl: { type: String, attribute: "bot-avatar-url" },
|
|
1290
|
+
widgetIconUrl: { type: String, attribute: "widget-icon-url" },
|
|
1175
1291
|
backgroundImageUrl: { type: String, attribute: "background-image-url" },
|
|
1176
1292
|
widgetWidth: { type: String, attribute: "widget-width" },
|
|
1177
1293
|
widgetHeight: { type: String, attribute: "widget-height" },
|