@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/README.md
CHANGED
|
@@ -13,7 +13,7 @@ A modern, customizable chat widget built with Lit web components. Features a cle
|
|
|
13
13
|
- 🎯 **TypeScript Support** - Full type safety
|
|
14
14
|
- 📝 **List Formatting** - Automatic rendering of bulleted and numbered lists
|
|
15
15
|
- 💡 **Suggested Questions** - Clickable follow-up questions for better UX
|
|
16
|
-
- 🔗 **Related FAQs** - Display related FAQ references
|
|
16
|
+
<!-- - 🔗 **Related FAQs** - Display related FAQ references (commented out) -->
|
|
17
17
|
- 👋 **Customizable Welcome Message** - Set custom greeting with optional subtitle
|
|
18
18
|
|
|
19
19
|
## Quick Start
|
|
@@ -117,21 +117,28 @@ import '@a.izzuddin/ai-chat';
|
|
|
117
117
|
session-id="user-123"
|
|
118
118
|
title="AI Assistant"
|
|
119
119
|
bot-avatar-url="/path/to/avatar.png"
|
|
120
|
+
widget-icon-url="/path/to/widget-icon.png"
|
|
120
121
|
background-image-url="/path/to/background.png">
|
|
121
122
|
</ai-chat>
|
|
122
123
|
```
|
|
123
124
|
|
|
125
|
+
**Note:** The `widget-icon-url` sets a custom icon for the floating widget button (only applies in widget mode).
|
|
126
|
+
|
|
124
127
|
#### Custom Welcome Message
|
|
128
|
+
The welcome message appears as the first assistant message in the chat instead of a large empty state.
|
|
129
|
+
|
|
125
130
|
```html
|
|
126
131
|
<ai-chat
|
|
127
132
|
api-url="https://api.example.com"
|
|
128
133
|
session-id="user-123"
|
|
129
134
|
title="AI Assistant"
|
|
130
|
-
welcome-message="
|
|
131
|
-
welcome-subtitle="
|
|
135
|
+
welcome-message="Hai, bagaimana saya boleh bantu?"
|
|
136
|
+
welcome-subtitle="Sila tanya tentang permohonan atau apa-apa berkaitan MySTI">
|
|
132
137
|
</ai-chat>
|
|
133
138
|
```
|
|
134
139
|
|
|
140
|
+
The welcome message and subtitle will be combined and displayed as a regular chat message from the assistant.
|
|
141
|
+
|
|
135
142
|
### Dark Mode
|
|
136
143
|
```html
|
|
137
144
|
<ai-chat
|
|
@@ -158,9 +165,10 @@ import '@a.izzuddin/ai-chat';
|
|
|
158
165
|
| `user-message-bg` | string | '#D6E4FF' | User message background |
|
|
159
166
|
| `bot-message-bg` | string | '#F5F5F5' | Bot message background |
|
|
160
167
|
| `bot-avatar-url` | string | '' | Custom bot avatar image |
|
|
168
|
+
| `widget-icon-url` | string | '' | Custom widget button icon (widget mode only) |
|
|
161
169
|
| `background-image-url` | string | '' | Chat background image |
|
|
162
|
-
| `welcome-message` | string | 'How can I help you today?' | Initial
|
|
163
|
-
| `welcome-subtitle` | string | '' | Optional subtitle
|
|
170
|
+
| `welcome-message` | string | 'How can I help you today?' | Initial message from assistant (appears as first chat message) |
|
|
171
|
+
| `welcome-subtitle` | string | '' | Optional subtitle added to welcome message |
|
|
164
172
|
|
|
165
173
|
## API Integration
|
|
166
174
|
|
|
@@ -178,12 +186,6 @@ Expected response format:
|
|
|
178
186
|
```json
|
|
179
187
|
{
|
|
180
188
|
"response": "string",
|
|
181
|
-
"faq_used": [
|
|
182
|
-
{
|
|
183
|
-
"no.": "1",
|
|
184
|
-
"question": "What is MySTI?"
|
|
185
|
-
}
|
|
186
|
-
],
|
|
187
189
|
"suggested_follow_ups": [
|
|
188
190
|
"What are the main objectives of the program?",
|
|
189
191
|
"How can companies apply?",
|
|
@@ -192,13 +194,26 @@ Expected response format:
|
|
|
192
194
|
}
|
|
193
195
|
```
|
|
194
196
|
|
|
197
|
+
<!-- FAQ functionality - commented out for now
|
|
198
|
+
{
|
|
199
|
+
"response": "string",
|
|
200
|
+
"faq_used": [
|
|
201
|
+
{
|
|
202
|
+
"no.": "1",
|
|
203
|
+
"question": "What is MySTI?"
|
|
204
|
+
}
|
|
205
|
+
],
|
|
206
|
+
"suggested_follow_ups": [...]
|
|
207
|
+
}
|
|
208
|
+
-->
|
|
209
|
+
|
|
195
210
|
**Supported field variations:**
|
|
196
|
-
- `faq_used` or `faqs_used` for related FAQs
|
|
211
|
+
<!-- - `faq_used` or `faqs_used` for related FAQs (commented out) -->
|
|
197
212
|
- `suggested_follow_ups` or `suggested_questions` for clickable follow-up questions
|
|
198
213
|
|
|
199
214
|
### Response Behavior
|
|
200
215
|
|
|
201
|
-
- **Related FAQs** - Displayed as non-clickable text references
|
|
216
|
+
<!-- - **Related FAQs** - Displayed as non-clickable text references (commented out) -->
|
|
202
217
|
- **Suggested Questions** - Displayed as clickable buttons that send the question when clicked
|
|
203
218
|
- **List Formatting** - Messages support automatic list rendering:
|
|
204
219
|
- Unordered lists: Lines starting with `-`, `*`, or `•`
|
package/custom-elements.json
CHANGED
|
@@ -221,14 +221,6 @@
|
|
|
221
221
|
"name": "Message",
|
|
222
222
|
"module": "./components/ai-chat"
|
|
223
223
|
}
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
"kind": "js",
|
|
227
|
-
"name": "FAQ",
|
|
228
|
-
"declaration": {
|
|
229
|
-
"name": "FAQ",
|
|
230
|
-
"module": "./components/ai-chat"
|
|
231
|
-
}
|
|
232
224
|
}
|
|
233
225
|
]
|
|
234
226
|
},
|
|
@@ -614,6 +606,16 @@
|
|
|
614
606
|
"default": "''",
|
|
615
607
|
"attribute": "bot-avatar-url"
|
|
616
608
|
},
|
|
609
|
+
{
|
|
610
|
+
"kind": "field",
|
|
611
|
+
"name": "widgetIconUrl",
|
|
612
|
+
"privacy": "public",
|
|
613
|
+
"type": {
|
|
614
|
+
"text": "string"
|
|
615
|
+
},
|
|
616
|
+
"default": "''",
|
|
617
|
+
"attribute": "widget-icon-url"
|
|
618
|
+
},
|
|
617
619
|
{
|
|
618
620
|
"kind": "field",
|
|
619
621
|
"name": "backgroundImageUrl",
|
|
@@ -741,17 +743,20 @@
|
|
|
741
743
|
"default": "false"
|
|
742
744
|
},
|
|
743
745
|
{
|
|
744
|
-
"kind": "
|
|
745
|
-
"name": "
|
|
746
|
-
"type": {
|
|
747
|
-
"text": "HTMLDivElement | undefined"
|
|
748
|
-
},
|
|
746
|
+
"kind": "method",
|
|
747
|
+
"name": "toggleWidget",
|
|
749
748
|
"privacy": "private"
|
|
750
749
|
},
|
|
751
750
|
{
|
|
752
751
|
"kind": "method",
|
|
753
|
-
"name": "
|
|
754
|
-
"privacy": "
|
|
752
|
+
"name": "clearChat",
|
|
753
|
+
"privacy": "public",
|
|
754
|
+
"return": {
|
|
755
|
+
"type": {
|
|
756
|
+
"text": "void"
|
|
757
|
+
}
|
|
758
|
+
},
|
|
759
|
+
"description": "Clear all chat messages and reset to welcome message"
|
|
755
760
|
},
|
|
756
761
|
{
|
|
757
762
|
"kind": "method",
|
|
@@ -778,6 +783,46 @@
|
|
|
778
783
|
}
|
|
779
784
|
]
|
|
780
785
|
},
|
|
786
|
+
{
|
|
787
|
+
"kind": "method",
|
|
788
|
+
"name": "getStorageKey",
|
|
789
|
+
"privacy": "private",
|
|
790
|
+
"return": {
|
|
791
|
+
"type": {
|
|
792
|
+
"text": "string"
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
},
|
|
796
|
+
{
|
|
797
|
+
"kind": "method",
|
|
798
|
+
"name": "saveMessagesToStorage",
|
|
799
|
+
"privacy": "private",
|
|
800
|
+
"return": {
|
|
801
|
+
"type": {
|
|
802
|
+
"text": "void"
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
},
|
|
806
|
+
{
|
|
807
|
+
"kind": "method",
|
|
808
|
+
"name": "loadMessagesFromStorage",
|
|
809
|
+
"privacy": "private",
|
|
810
|
+
"return": {
|
|
811
|
+
"type": {
|
|
812
|
+
"text": "Message[] | null"
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
},
|
|
816
|
+
{
|
|
817
|
+
"kind": "method",
|
|
818
|
+
"name": "clearMessagesFromStorage",
|
|
819
|
+
"privacy": "private",
|
|
820
|
+
"return": {
|
|
821
|
+
"type": {
|
|
822
|
+
"text": "void"
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
},
|
|
781
826
|
{
|
|
782
827
|
"kind": "method",
|
|
783
828
|
"name": "formatMessageContent",
|
|
@@ -926,6 +971,14 @@
|
|
|
926
971
|
"default": "''",
|
|
927
972
|
"fieldName": "botAvatarUrl"
|
|
928
973
|
},
|
|
974
|
+
{
|
|
975
|
+
"name": "widget-icon-url",
|
|
976
|
+
"type": {
|
|
977
|
+
"text": "string"
|
|
978
|
+
},
|
|
979
|
+
"default": "''",
|
|
980
|
+
"fieldName": "widgetIconUrl"
|
|
981
|
+
},
|
|
929
982
|
{
|
|
930
983
|
"name": "background-image-url",
|
|
931
984
|
"type": {
|
|
@@ -1035,7 +1088,7 @@
|
|
|
1035
1088
|
"name": "AIChat",
|
|
1036
1089
|
"parameters": [
|
|
1037
1090
|
{
|
|
1038
|
-
"name": "{\n apiUrl,\n sessionId = \"default-session\",\n title = \"My AI Agent\",\n initialMessages = [],\n className,\n onMessageSent,\n onResponseReceived,\n onError,\n}",
|
|
1091
|
+
"name": "{\r\n apiUrl,\r\n sessionId = \"default-session\",\r\n title = \"My AI Agent\",\r\n initialMessages = [],\r\n className,\r\n onMessageSent,\r\n onResponseReceived,\r\n onError,\r\n}",
|
|
1039
1092
|
"type": {
|
|
1040
1093
|
"text": "AIChatProps"
|
|
1041
1094
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -60,6 +60,7 @@ declare class AIChat extends LitElement {
|
|
|
60
60
|
mode: 'fullscreen' | 'widget';
|
|
61
61
|
initialMessages: Message[];
|
|
62
62
|
botAvatarUrl: string;
|
|
63
|
+
widgetIconUrl: string;
|
|
63
64
|
backgroundImageUrl: string;
|
|
64
65
|
widgetWidth: string;
|
|
65
66
|
widgetHeight: string;
|
|
@@ -73,7 +74,6 @@ declare class AIChat extends LitElement {
|
|
|
73
74
|
private input;
|
|
74
75
|
private isLoading;
|
|
75
76
|
private isOpen;
|
|
76
|
-
private messagesEndRef?;
|
|
77
77
|
static properties: {
|
|
78
78
|
apiUrl: {
|
|
79
79
|
type: StringConstructor;
|
|
@@ -101,6 +101,10 @@ declare class AIChat extends LitElement {
|
|
|
101
101
|
type: StringConstructor;
|
|
102
102
|
attribute: string;
|
|
103
103
|
};
|
|
104
|
+
widgetIconUrl: {
|
|
105
|
+
type: StringConstructor;
|
|
106
|
+
attribute: string;
|
|
107
|
+
};
|
|
104
108
|
backgroundImageUrl: {
|
|
105
109
|
type: StringConstructor;
|
|
106
110
|
attribute: string;
|
|
@@ -140,7 +144,16 @@ declare class AIChat extends LitElement {
|
|
|
140
144
|
};
|
|
141
145
|
constructor();
|
|
142
146
|
private toggleWidget;
|
|
147
|
+
/**
|
|
148
|
+
* Clear all chat messages and reset to welcome message
|
|
149
|
+
* @public
|
|
150
|
+
*/
|
|
151
|
+
clearChat(): void;
|
|
143
152
|
private lightenColor;
|
|
153
|
+
private getStorageKey;
|
|
154
|
+
private saveMessagesToStorage;
|
|
155
|
+
private loadMessagesFromStorage;
|
|
156
|
+
private clearMessagesFromStorage;
|
|
144
157
|
private formatMessageContent;
|
|
145
158
|
connectedCallback(): void;
|
|
146
159
|
updated(changedProperties: PropertyValues): void;
|
|
@@ -157,4 +170,4 @@ declare global {
|
|
|
157
170
|
}
|
|
158
171
|
}
|
|
159
172
|
|
|
160
|
-
export { AIChat, type
|
|
173
|
+
export { AIChat, type Message };
|
package/dist/index.d.ts
CHANGED
|
@@ -60,6 +60,7 @@ declare class AIChat extends LitElement {
|
|
|
60
60
|
mode: 'fullscreen' | 'widget';
|
|
61
61
|
initialMessages: Message[];
|
|
62
62
|
botAvatarUrl: string;
|
|
63
|
+
widgetIconUrl: string;
|
|
63
64
|
backgroundImageUrl: string;
|
|
64
65
|
widgetWidth: string;
|
|
65
66
|
widgetHeight: string;
|
|
@@ -73,7 +74,6 @@ declare class AIChat extends LitElement {
|
|
|
73
74
|
private input;
|
|
74
75
|
private isLoading;
|
|
75
76
|
private isOpen;
|
|
76
|
-
private messagesEndRef?;
|
|
77
77
|
static properties: {
|
|
78
78
|
apiUrl: {
|
|
79
79
|
type: StringConstructor;
|
|
@@ -101,6 +101,10 @@ declare class AIChat extends LitElement {
|
|
|
101
101
|
type: StringConstructor;
|
|
102
102
|
attribute: string;
|
|
103
103
|
};
|
|
104
|
+
widgetIconUrl: {
|
|
105
|
+
type: StringConstructor;
|
|
106
|
+
attribute: string;
|
|
107
|
+
};
|
|
104
108
|
backgroundImageUrl: {
|
|
105
109
|
type: StringConstructor;
|
|
106
110
|
attribute: string;
|
|
@@ -140,7 +144,16 @@ declare class AIChat extends LitElement {
|
|
|
140
144
|
};
|
|
141
145
|
constructor();
|
|
142
146
|
private toggleWidget;
|
|
147
|
+
/**
|
|
148
|
+
* Clear all chat messages and reset to welcome message
|
|
149
|
+
* @public
|
|
150
|
+
*/
|
|
151
|
+
clearChat(): void;
|
|
143
152
|
private lightenColor;
|
|
153
|
+
private getStorageKey;
|
|
154
|
+
private saveMessagesToStorage;
|
|
155
|
+
private loadMessagesFromStorage;
|
|
156
|
+
private clearMessagesFromStorage;
|
|
144
157
|
private formatMessageContent;
|
|
145
158
|
connectedCallback(): void;
|
|
146
159
|
updated(changedProperties: PropertyValues): void;
|
|
@@ -157,4 +170,4 @@ declare global {
|
|
|
157
170
|
}
|
|
158
171
|
}
|
|
159
172
|
|
|
160
|
-
export { AIChat, type
|
|
173
|
+
export { AIChat, type Message };
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
16
16
|
if (kind && result) __defProp(target, key, result);
|
|
17
17
|
return result;
|
|
18
18
|
};
|
|
19
|
-
var VERSION = "0.2.
|
|
19
|
+
var VERSION = "0.2.10";
|
|
20
20
|
exports.AIChat = class AIChat extends lit.LitElement {
|
|
21
21
|
constructor() {
|
|
22
22
|
super();
|
|
@@ -27,6 +27,7 @@ exports.AIChat = class AIChat extends lit.LitElement {
|
|
|
27
27
|
this.mode = "fullscreen";
|
|
28
28
|
this.initialMessages = [];
|
|
29
29
|
this.botAvatarUrl = "";
|
|
30
|
+
this.widgetIconUrl = "";
|
|
30
31
|
this.backgroundImageUrl = "";
|
|
31
32
|
this.widgetWidth = "380px";
|
|
32
33
|
this.widgetHeight = "600px";
|
|
@@ -44,6 +45,25 @@ exports.AIChat = class AIChat extends lit.LitElement {
|
|
|
44
45
|
toggleWidget() {
|
|
45
46
|
this.isOpen = !this.isOpen;
|
|
46
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Clear all chat messages and reset to welcome message
|
|
50
|
+
* @public
|
|
51
|
+
*/
|
|
52
|
+
clearChat() {
|
|
53
|
+
this.clearMessagesFromStorage();
|
|
54
|
+
if (this.welcomeMessage) {
|
|
55
|
+
const welcomeText = this.welcomeSubtitle ? `${this.welcomeMessage}
|
|
56
|
+
|
|
57
|
+
${this.welcomeSubtitle}` : this.welcomeMessage;
|
|
58
|
+
this.messages = [{
|
|
59
|
+
id: "welcome-" + Date.now(),
|
|
60
|
+
role: "assistant",
|
|
61
|
+
content: welcomeText
|
|
62
|
+
}];
|
|
63
|
+
} else {
|
|
64
|
+
this.messages = [];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
47
67
|
lightenColor(hex, percent = 15) {
|
|
48
68
|
hex = hex.replace("#", "");
|
|
49
69
|
const r = parseInt(hex.substring(0, 2), 16);
|
|
@@ -54,23 +74,66 @@ exports.AIChat = class AIChat extends lit.LitElement {
|
|
|
54
74
|
const newB = Math.min(255, Math.round(b + (255 - b) * (percent / 100)));
|
|
55
75
|
return `#${newR.toString(16).padStart(2, "0")}${newG.toString(16).padStart(2, "0")}${newB.toString(16).padStart(2, "0")}`;
|
|
56
76
|
}
|
|
77
|
+
getStorageKey() {
|
|
78
|
+
return `ai-chat-messages-${this.sessionId}`;
|
|
79
|
+
}
|
|
80
|
+
saveMessagesToStorage() {
|
|
81
|
+
try {
|
|
82
|
+
const storageKey = this.getStorageKey();
|
|
83
|
+
localStorage.setItem(storageKey, JSON.stringify(this.messages));
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.warn("Failed to save messages to localStorage:", error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
loadMessagesFromStorage() {
|
|
89
|
+
try {
|
|
90
|
+
const storageKey = this.getStorageKey();
|
|
91
|
+
const saved = localStorage.getItem(storageKey);
|
|
92
|
+
if (saved) {
|
|
93
|
+
return JSON.parse(saved);
|
|
94
|
+
}
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.warn("Failed to load messages from localStorage:", error);
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
clearMessagesFromStorage() {
|
|
101
|
+
try {
|
|
102
|
+
const storageKey = this.getStorageKey();
|
|
103
|
+
localStorage.removeItem(storageKey);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.warn("Failed to clear messages from localStorage:", error);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
57
108
|
formatMessageContent(content) {
|
|
58
109
|
const escapeHtml = (text) => {
|
|
59
110
|
const div = document.createElement("div");
|
|
60
111
|
div.textContent = text;
|
|
61
112
|
return div.innerHTML;
|
|
62
113
|
};
|
|
63
|
-
let processedContent = content.replace(/(
|
|
114
|
+
let processedContent = content.replace(/([^\n])\s*(\d+\.\s+)/g, "$1\n$2");
|
|
115
|
+
processedContent = processedContent.replace(/(\d+\.\s+[^0-9]+?)(?=\s+\d+\.\s+|\s*$)/g, "$1\n");
|
|
64
116
|
processedContent = processedContent.replace(/(-\s+[^-]+?)(?=\s+-\s+|\s*$)/g, "$1\n");
|
|
65
117
|
const lines = processedContent.split("\n");
|
|
66
118
|
let formattedContent = "";
|
|
67
119
|
let inList = false;
|
|
68
120
|
let listType = null;
|
|
121
|
+
let orderedListCounter = 1;
|
|
122
|
+
const getNextListType = (startIndex) => {
|
|
123
|
+
for (let j = startIndex + 1; j < lines.length; j++) {
|
|
124
|
+
const nextLine = lines[j].trim();
|
|
125
|
+
if (nextLine === "") continue;
|
|
126
|
+
if (nextLine.match(/^[-*•]\s+/)) return "ul";
|
|
127
|
+
if (nextLine.match(/^\d+\.\s+/)) return "ol";
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
};
|
|
69
132
|
for (let i = 0; i < lines.length; i++) {
|
|
70
133
|
const line = lines[i];
|
|
71
134
|
const trimmedLine = line.trim();
|
|
72
135
|
const unorderedMatch = trimmedLine.match(/^[-*•]\s+(.+)$/);
|
|
73
|
-
const orderedMatch = trimmedLine.match(
|
|
136
|
+
const orderedMatch = trimmedLine.match(/^(\d+)\.\s+(.+)$/);
|
|
74
137
|
if (unorderedMatch) {
|
|
75
138
|
if (!inList || listType !== "ul") {
|
|
76
139
|
if (inList) formattedContent += listType === "ol" ? "</ol>" : "</ul>";
|
|
@@ -80,22 +143,40 @@ exports.AIChat = class AIChat extends lit.LitElement {
|
|
|
80
143
|
}
|
|
81
144
|
formattedContent += `<li>${escapeHtml(unorderedMatch[1])}</li>`;
|
|
82
145
|
} else if (orderedMatch) {
|
|
146
|
+
const itemNumber = parseInt(orderedMatch[1], 10);
|
|
147
|
+
const itemText = orderedMatch[2];
|
|
83
148
|
if (!inList || listType !== "ol") {
|
|
84
149
|
if (inList) formattedContent += listType === "ol" ? "</ol>" : "</ul>";
|
|
85
|
-
|
|
150
|
+
if (itemNumber === 1) {
|
|
151
|
+
orderedListCounter = 1;
|
|
152
|
+
formattedContent += "<ol>";
|
|
153
|
+
} else {
|
|
154
|
+
formattedContent += `<ol start="${orderedListCounter}">`;
|
|
155
|
+
}
|
|
86
156
|
inList = true;
|
|
87
157
|
listType = "ol";
|
|
88
158
|
}
|
|
89
|
-
formattedContent += `<li>${escapeHtml(
|
|
159
|
+
formattedContent += `<li value="${itemNumber}">${escapeHtml(itemText)}</li>`;
|
|
160
|
+
orderedListCounter = itemNumber + 1;
|
|
90
161
|
} else {
|
|
91
|
-
if (inList) {
|
|
92
|
-
formattedContent += listType === "ol" ? "</ol>" : "</ul>";
|
|
93
|
-
inList = false;
|
|
94
|
-
listType = null;
|
|
95
|
-
}
|
|
96
162
|
if (trimmedLine === "") {
|
|
97
|
-
|
|
163
|
+
const nextListType = getNextListType(i);
|
|
164
|
+
if (inList && nextListType === listType) {
|
|
165
|
+
formattedContent += '<li style="list-style: none; height: 0.5em;"></li>';
|
|
166
|
+
} else {
|
|
167
|
+
if (inList) {
|
|
168
|
+
formattedContent += listType === "ol" ? "</ol>" : "</ul>";
|
|
169
|
+
inList = false;
|
|
170
|
+
listType = null;
|
|
171
|
+
}
|
|
172
|
+
formattedContent += "<br>";
|
|
173
|
+
}
|
|
98
174
|
} else {
|
|
175
|
+
if (inList) {
|
|
176
|
+
formattedContent += listType === "ol" ? "</ol>" : "</ul>";
|
|
177
|
+
inList = false;
|
|
178
|
+
listType = null;
|
|
179
|
+
}
|
|
99
180
|
formattedContent += escapeHtml(line) + "\n";
|
|
100
181
|
}
|
|
101
182
|
}
|
|
@@ -107,20 +188,37 @@ exports.AIChat = class AIChat extends lit.LitElement {
|
|
|
107
188
|
}
|
|
108
189
|
connectedCallback() {
|
|
109
190
|
super.connectedCallback();
|
|
191
|
+
const savedMessages = this.loadMessagesFromStorage();
|
|
110
192
|
if (this.initialMessages && this.initialMessages.length > 0) {
|
|
111
193
|
this.messages = [...this.initialMessages];
|
|
194
|
+
} else if (savedMessages && savedMessages.length > 0) {
|
|
195
|
+
this.messages = savedMessages;
|
|
196
|
+
} else if (this.welcomeMessage) {
|
|
197
|
+
const welcomeText = this.welcomeSubtitle ? `${this.welcomeMessage}
|
|
198
|
+
|
|
199
|
+
${this.welcomeSubtitle}` : this.welcomeMessage;
|
|
200
|
+
this.messages = [{
|
|
201
|
+
id: "welcome-" + Date.now(),
|
|
202
|
+
role: "assistant",
|
|
203
|
+
content: welcomeText
|
|
204
|
+
}];
|
|
112
205
|
}
|
|
113
206
|
}
|
|
114
207
|
updated(changedProperties) {
|
|
115
208
|
super.updated(changedProperties);
|
|
116
209
|
if (changedProperties.has("messages")) {
|
|
117
210
|
this.scrollToBottom();
|
|
211
|
+
this.saveMessagesToStorage();
|
|
118
212
|
}
|
|
119
213
|
}
|
|
120
214
|
scrollToBottom() {
|
|
121
|
-
|
|
122
|
-
this.
|
|
123
|
-
|
|
215
|
+
setTimeout(() => {
|
|
216
|
+
const userMessages = this.shadowRoot?.querySelectorAll(".message.user");
|
|
217
|
+
if (userMessages && userMessages.length > 0) {
|
|
218
|
+
const lastUserMessage = userMessages[userMessages.length - 1];
|
|
219
|
+
lastUserMessage.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
220
|
+
}
|
|
221
|
+
}, 100);
|
|
124
222
|
}
|
|
125
223
|
handleInput(e) {
|
|
126
224
|
this.input = e.target.value;
|
|
@@ -173,7 +271,7 @@ exports.AIChat = class AIChat extends lit.LitElement {
|
|
|
173
271
|
if (trimmedResponse.startsWith("{") || trimmedResponse.startsWith("[")) {
|
|
174
272
|
console.log("\u{1F504} Detected stringified JSON, parsing...");
|
|
175
273
|
try {
|
|
176
|
-
|
|
274
|
+
const innerData = JSON.parse(data.response);
|
|
177
275
|
console.log("\u2705 Parsed inner data with JSON.parse");
|
|
178
276
|
if (innerData && innerData.response && typeof innerData.response === "string") {
|
|
179
277
|
responseText = innerData.response;
|
|
@@ -308,26 +406,14 @@ Please check your API endpoint configuration.`
|
|
|
308
406
|
<!-- Messages Area -->
|
|
309
407
|
<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}');` : ""}">
|
|
310
408
|
<div class="messages-container">
|
|
311
|
-
${this.messages.length === 0 ? lit.html`
|
|
312
|
-
<div class="empty-state">
|
|
313
|
-
<div class="empty-state-avatar">
|
|
314
|
-
${this.botAvatarUrl ? lit.html`<img src="${this.botAvatarUrl}" alt="Bot" class="empty-state-avatar-image" />` : lit.html`<svg viewBox="0 0 24 24" fill="none" stroke="#9ca3af" stroke-width="2">
|
|
315
|
-
<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>
|
|
316
|
-
</svg>`}
|
|
317
|
-
</div>
|
|
318
|
-
<div class="empty-state-content">
|
|
319
|
-
<p class="empty-state-message">${this.welcomeMessage}</p>
|
|
320
|
-
${this.welcomeSubtitle ? lit.html`<p class="empty-state-subtitle">${this.welcomeSubtitle}</p>` : ""}
|
|
321
|
-
</div>
|
|
322
|
-
</div>
|
|
323
|
-
` : ""}
|
|
324
|
-
|
|
325
409
|
${repeat_js.repeat(this.messages, (msg) => msg.id, (msg) => lit.html`
|
|
326
|
-
<div
|
|
410
|
+
<div
|
|
411
|
+
class=${classMap_js.classMap({
|
|
327
412
|
message: true,
|
|
328
413
|
user: msg.role === "user",
|
|
329
414
|
assistant: msg.role === "assistant"
|
|
330
|
-
})}
|
|
415
|
+
})}
|
|
416
|
+
>
|
|
331
417
|
<div class="avatar">
|
|
332
418
|
${msg.role === "user" ? "U" : this.botAvatarUrl ? lit.html`<img src="${this.botAvatarUrl}" alt="AI" class="avatar-image" />` : "AI"}
|
|
333
419
|
</div>
|
|
@@ -371,8 +457,6 @@ Please check your API endpoint configuration.`
|
|
|
371
457
|
</div>
|
|
372
458
|
</div>
|
|
373
459
|
` : ""}
|
|
374
|
-
|
|
375
|
-
<div ${(el) => this.messagesEndRef = el}></div>
|
|
376
460
|
</div>
|
|
377
461
|
</div>
|
|
378
462
|
|
|
@@ -382,7 +466,7 @@ Please check your API endpoint configuration.`
|
|
|
382
466
|
<input
|
|
383
467
|
type="text"
|
|
384
468
|
class="input-field"
|
|
385
|
-
placeholder="
|
|
469
|
+
placeholder="Taip mesej anda..."
|
|
386
470
|
.value=${this.input}
|
|
387
471
|
@input=${this.handleInput}
|
|
388
472
|
?disabled=${this.isLoading}
|
|
@@ -424,7 +508,10 @@ Please check your API endpoint configuration.`
|
|
|
424
508
|
|
|
425
509
|
<!-- Toggle Button -->
|
|
426
510
|
<button
|
|
427
|
-
class
|
|
511
|
+
class=${classMap_js.classMap({
|
|
512
|
+
"widget-button": true,
|
|
513
|
+
"widget-button-no-bg": !this.isOpen && !!this.widgetIconUrl
|
|
514
|
+
})}
|
|
428
515
|
style="--primary-color: ${this.primaryColor}; --primary-color-light: ${primaryColorLight};"
|
|
429
516
|
@click=${this.toggleWidget}
|
|
430
517
|
aria-label=${this.isOpen ? "Close chat" : "Open chat"}
|
|
@@ -434,6 +521,8 @@ Please check your API endpoint configuration.`
|
|
|
434
521
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
435
522
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
436
523
|
</svg>
|
|
524
|
+
` : this.widgetIconUrl ? lit.html`
|
|
525
|
+
<img src="${this.widgetIconUrl}" alt="Chat" class="widget-button-icon" />
|
|
437
526
|
` : lit.html`
|
|
438
527
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
439
528
|
<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>
|
|
@@ -496,12 +585,29 @@ exports.AIChat.styles = lit.css`
|
|
|
496
585
|
box-shadow: 0 6px 20px rgba(65, 105, 225, 0.4);
|
|
497
586
|
}
|
|
498
587
|
|
|
588
|
+
.widget-button-no-bg {
|
|
589
|
+
background: transparent;
|
|
590
|
+
box-shadow: none;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
.widget-button-no-bg:hover {
|
|
594
|
+
background: transparent;
|
|
595
|
+
box-shadow: none;
|
|
596
|
+
transform: scale(1.1);
|
|
597
|
+
}
|
|
598
|
+
|
|
499
599
|
.widget-button svg {
|
|
500
600
|
width: 28px;
|
|
501
601
|
height: 28px;
|
|
502
602
|
color: white;
|
|
503
603
|
}
|
|
504
604
|
|
|
605
|
+
.widget-button-icon {
|
|
606
|
+
width: auto;
|
|
607
|
+
height: auto;
|
|
608
|
+
object-fit: cover;
|
|
609
|
+
}
|
|
610
|
+
|
|
505
611
|
.widget-window {
|
|
506
612
|
position: absolute;
|
|
507
613
|
bottom: 80px;
|
|
@@ -963,11 +1069,13 @@ exports.AIChat.styles = lit.css`
|
|
|
963
1069
|
margin: 0.5rem 0;
|
|
964
1070
|
padding-left: 1.5rem;
|
|
965
1071
|
white-space: normal;
|
|
1072
|
+
list-style-position: outside;
|
|
966
1073
|
}
|
|
967
1074
|
|
|
968
1075
|
.message-text li {
|
|
969
1076
|
margin: 0.25rem 0;
|
|
970
1077
|
white-space: normal;
|
|
1078
|
+
display: list-item;
|
|
971
1079
|
}
|
|
972
1080
|
|
|
973
1081
|
.message-text ul {
|
|
@@ -976,6 +1084,12 @@ exports.AIChat.styles = lit.css`
|
|
|
976
1084
|
|
|
977
1085
|
.message-text ol {
|
|
978
1086
|
list-style-type: decimal;
|
|
1087
|
+
counter-reset: list-counter;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
.message-text ol li {
|
|
1091
|
+
display: list-item;
|
|
1092
|
+
list-style-type: decimal;
|
|
979
1093
|
}
|
|
980
1094
|
|
|
981
1095
|
.faq-section {
|
|
@@ -1034,7 +1148,8 @@ exports.AIChat.styles = lit.css`
|
|
|
1034
1148
|
border-color: #3f3f46;
|
|
1035
1149
|
}
|
|
1036
1150
|
|
|
1037
|
-
|
|
1151
|
+
/* FAQ static item styles - commented out for now */
|
|
1152
|
+
/* .faq-item-static {
|
|
1038
1153
|
font-size: 0.875rem;
|
|
1039
1154
|
color: #6B7280;
|
|
1040
1155
|
padding: 0;
|
|
@@ -1045,7 +1160,7 @@ exports.AIChat.styles = lit.css`
|
|
|
1045
1160
|
|
|
1046
1161
|
:host([theme="dark"]) .faq-item-static {
|
|
1047
1162
|
color: #9CA3AF;
|
|
1048
|
-
}
|
|
1163
|
+
} */
|
|
1049
1164
|
|
|
1050
1165
|
.loading {
|
|
1051
1166
|
display: flex;
|
|
@@ -1174,6 +1289,7 @@ exports.AIChat.properties = {
|
|
|
1174
1289
|
mode: { type: String, reflect: true },
|
|
1175
1290
|
initialMessages: { type: Array },
|
|
1176
1291
|
botAvatarUrl: { type: String, attribute: "bot-avatar-url" },
|
|
1292
|
+
widgetIconUrl: { type: String, attribute: "widget-icon-url" },
|
|
1177
1293
|
backgroundImageUrl: { type: String, attribute: "background-image-url" },
|
|
1178
1294
|
widgetWidth: { type: String, attribute: "widget-width" },
|
|
1179
1295
|
widgetHeight: { type: String, attribute: "widget-height" },
|