@a.izzuddin/ai-chat 0.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/README.md +293 -0
- package/README.npm.md +293 -0
- package/custom-elements.json +729 -0
- package/dist/index.d.mts +71 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.js +422 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +422 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +82 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
import { css, LitElement, html } from 'lit';
|
|
2
|
+
import { state, customElement } from 'lit/decorators.js';
|
|
3
|
+
import { repeat } from 'lit/directives/repeat.js';
|
|
4
|
+
import { classMap } from 'lit/directives/class-map.js';
|
|
5
|
+
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
8
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
9
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
10
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
11
|
+
if (decorator = decorators[i])
|
|
12
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
13
|
+
if (kind && result) __defProp(target, key, result);
|
|
14
|
+
return result;
|
|
15
|
+
};
|
|
16
|
+
var AIChat = class extends LitElement {
|
|
17
|
+
constructor() {
|
|
18
|
+
super();
|
|
19
|
+
this.apiUrl = "";
|
|
20
|
+
this.sessionId = "default-session";
|
|
21
|
+
this.chatTitle = "My AI Agent";
|
|
22
|
+
this.theme = "light";
|
|
23
|
+
this.initialMessages = [];
|
|
24
|
+
this.messages = [];
|
|
25
|
+
this.input = "";
|
|
26
|
+
this.isLoading = false;
|
|
27
|
+
}
|
|
28
|
+
connectedCallback() {
|
|
29
|
+
super.connectedCallback();
|
|
30
|
+
if (this.initialMessages && this.initialMessages.length > 0) {
|
|
31
|
+
this.messages = [...this.initialMessages];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
updated(changedProperties) {
|
|
35
|
+
super.updated(changedProperties);
|
|
36
|
+
if (changedProperties.has("messages")) {
|
|
37
|
+
this.scrollToBottom();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
scrollToBottom() {
|
|
41
|
+
requestAnimationFrame(() => {
|
|
42
|
+
this.messagesEndRef?.scrollIntoView({ behavior: "smooth" });
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
handleInput(e) {
|
|
46
|
+
this.input = e.target.value;
|
|
47
|
+
}
|
|
48
|
+
async handleSubmit(e) {
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
if (!this.input.trim() || this.isLoading || !this.apiUrl) return;
|
|
51
|
+
const userMessage = {
|
|
52
|
+
id: Date.now().toString(),
|
|
53
|
+
role: "user",
|
|
54
|
+
content: this.input.trim()
|
|
55
|
+
};
|
|
56
|
+
this.messages = [...this.messages, userMessage];
|
|
57
|
+
const questionText = this.input.trim();
|
|
58
|
+
this.input = "";
|
|
59
|
+
this.isLoading = true;
|
|
60
|
+
this.dispatchEvent(new CustomEvent("message-sent", {
|
|
61
|
+
detail: userMessage,
|
|
62
|
+
bubbles: true,
|
|
63
|
+
composed: true
|
|
64
|
+
}));
|
|
65
|
+
try {
|
|
66
|
+
const response = await fetch(`${this.apiUrl}/ask`, {
|
|
67
|
+
method: "POST",
|
|
68
|
+
headers: { "Content-Type": "application/json" },
|
|
69
|
+
body: JSON.stringify({
|
|
70
|
+
session_id: this.sessionId,
|
|
71
|
+
question: questionText
|
|
72
|
+
})
|
|
73
|
+
});
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
const errorText = await response.text();
|
|
76
|
+
throw new Error(`Backend error: ${response.status} ${errorText}`);
|
|
77
|
+
}
|
|
78
|
+
const data = await response.json();
|
|
79
|
+
const assistantMessage = {
|
|
80
|
+
id: (Date.now() + 1).toString(),
|
|
81
|
+
role: "assistant",
|
|
82
|
+
content: data.response || "No response from agent"
|
|
83
|
+
};
|
|
84
|
+
this.messages = [...this.messages, assistantMessage];
|
|
85
|
+
this.dispatchEvent(new CustomEvent("response-received", {
|
|
86
|
+
detail: assistantMessage,
|
|
87
|
+
bubbles: true,
|
|
88
|
+
composed: true
|
|
89
|
+
}));
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.error("Backend connection failed:", err);
|
|
92
|
+
const errorMessage = {
|
|
93
|
+
id: (Date.now() + 1).toString(),
|
|
94
|
+
role: "assistant",
|
|
95
|
+
content: `Error: ${err.message}
|
|
96
|
+
|
|
97
|
+
Please check your API endpoint configuration.`
|
|
98
|
+
};
|
|
99
|
+
this.messages = [...this.messages, errorMessage];
|
|
100
|
+
this.dispatchEvent(new CustomEvent("error", {
|
|
101
|
+
detail: err,
|
|
102
|
+
bubbles: true,
|
|
103
|
+
composed: true
|
|
104
|
+
}));
|
|
105
|
+
} finally {
|
|
106
|
+
this.isLoading = false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
render() {
|
|
110
|
+
return html`
|
|
111
|
+
<!-- Header -->
|
|
112
|
+
<div class="header">
|
|
113
|
+
<div class="header-content">
|
|
114
|
+
<h1 class="title">${this.chatTitle}</h1>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<!-- Messages Area -->
|
|
119
|
+
<div class="messages-area">
|
|
120
|
+
<div class="messages-container">
|
|
121
|
+
${this.messages.length === 0 ? html`
|
|
122
|
+
<div class="empty-state">
|
|
123
|
+
<p>How can I help you today?</p>
|
|
124
|
+
</div>
|
|
125
|
+
` : ""}
|
|
126
|
+
|
|
127
|
+
${repeat(this.messages, (msg) => msg.id, (msg) => html`
|
|
128
|
+
<div class=${classMap({
|
|
129
|
+
message: true,
|
|
130
|
+
user: msg.role === "user",
|
|
131
|
+
assistant: msg.role === "assistant"
|
|
132
|
+
})}>
|
|
133
|
+
<div class="avatar">
|
|
134
|
+
${msg.role === "user" ? "U" : "AI"}
|
|
135
|
+
</div>
|
|
136
|
+
<div class="message-content">
|
|
137
|
+
<p class="message-text">${msg.content}</p>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
`)}
|
|
141
|
+
|
|
142
|
+
${this.isLoading ? html`
|
|
143
|
+
<div class="loading">
|
|
144
|
+
<div class="avatar">AI</div>
|
|
145
|
+
<div class="message-content">
|
|
146
|
+
<div class="spinner"></div>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
` : ""}
|
|
150
|
+
|
|
151
|
+
<div ${(el) => this.messagesEndRef = el}></div>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<!-- Input Area -->
|
|
156
|
+
<div class="input-area">
|
|
157
|
+
<form class="input-form" @submit=${this.handleSubmit}>
|
|
158
|
+
<input
|
|
159
|
+
type="text"
|
|
160
|
+
class="input-field"
|
|
161
|
+
placeholder="Type your message..."
|
|
162
|
+
.value=${this.input}
|
|
163
|
+
@input=${this.handleInput}
|
|
164
|
+
?disabled=${this.isLoading}
|
|
165
|
+
/>
|
|
166
|
+
<button
|
|
167
|
+
type="submit"
|
|
168
|
+
class="send-button"
|
|
169
|
+
?disabled=${this.isLoading || !this.input.trim()}
|
|
170
|
+
aria-label="Send message"
|
|
171
|
+
>
|
|
172
|
+
${this.isLoading ? html`
|
|
173
|
+
<div class="spinner" style="border-color: #fff; border-top-color: transparent;"></div>
|
|
174
|
+
` : html`
|
|
175
|
+
<svg class="send-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
176
|
+
<line x1="22" y1="2" x2="11" y2="13"></line>
|
|
177
|
+
<polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
|
|
178
|
+
</svg>
|
|
179
|
+
`}
|
|
180
|
+
</button>
|
|
181
|
+
</form>
|
|
182
|
+
</div>
|
|
183
|
+
`;
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
AIChat.styles = css`
|
|
187
|
+
:host {
|
|
188
|
+
display: flex;
|
|
189
|
+
flex-direction: column;
|
|
190
|
+
height: 100vh;
|
|
191
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
192
|
+
background: #fafafa;
|
|
193
|
+
color: #09090b;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
:host([theme="dark"]) {
|
|
197
|
+
background: #000;
|
|
198
|
+
color: #fafafa;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.header {
|
|
202
|
+
border-bottom: 1px solid #e4e4e7;
|
|
203
|
+
background: #fff;
|
|
204
|
+
padding: 1rem;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
:host([theme="dark"]) .header {
|
|
208
|
+
border-bottom-color: #27272a;
|
|
209
|
+
background: #18181b;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.header-content {
|
|
213
|
+
max-width: 56rem;
|
|
214
|
+
margin: 0 auto;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.title {
|
|
218
|
+
font-size: 1.25rem;
|
|
219
|
+
font-weight: 600;
|
|
220
|
+
margin: 0;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.messages-area {
|
|
224
|
+
flex: 1;
|
|
225
|
+
overflow-y: auto;
|
|
226
|
+
padding: 1.5rem 1rem;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.messages-container {
|
|
230
|
+
max-width: 56rem;
|
|
231
|
+
margin: 0 auto;
|
|
232
|
+
display: flex;
|
|
233
|
+
flex-direction: column;
|
|
234
|
+
gap: 1.5rem;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.empty-state {
|
|
238
|
+
text-align: center;
|
|
239
|
+
color: #71717a;
|
|
240
|
+
margin-top: 5rem;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
:host([theme="dark"]) .empty-state {
|
|
244
|
+
color: #a1a1aa;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.empty-state p {
|
|
248
|
+
font-size: 1.5rem;
|
|
249
|
+
font-weight: 500;
|
|
250
|
+
margin: 0;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.message {
|
|
254
|
+
display: flex;
|
|
255
|
+
gap: 1rem;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.message.user {
|
|
259
|
+
flex-direction: row-reverse;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.avatar {
|
|
263
|
+
width: 2.5rem;
|
|
264
|
+
height: 2.5rem;
|
|
265
|
+
border-radius: 9999px;
|
|
266
|
+
background: #e4e4e7;
|
|
267
|
+
display: flex;
|
|
268
|
+
align-items: center;
|
|
269
|
+
justify-content: center;
|
|
270
|
+
flex-shrink: 0;
|
|
271
|
+
font-weight: 500;
|
|
272
|
+
font-size: 0.875rem;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
:host([theme="dark"]) .avatar {
|
|
276
|
+
background: #3f3f46;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.message-content {
|
|
280
|
+
max-width: 36rem;
|
|
281
|
+
padding: 0.75rem 1rem;
|
|
282
|
+
border-radius: 1rem;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.message.user .message-content {
|
|
286
|
+
background: #2563eb;
|
|
287
|
+
color: #fff;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.message.assistant .message-content {
|
|
291
|
+
background: #fff;
|
|
292
|
+
border: 1px solid #e4e4e7;
|
|
293
|
+
color: #09090b;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
:host([theme="dark"]) .message.assistant .message-content {
|
|
297
|
+
background: #27272a;
|
|
298
|
+
border-color: #3f3f46;
|
|
299
|
+
color: #fafafa;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.message-text {
|
|
303
|
+
white-space: pre-wrap;
|
|
304
|
+
margin: 0;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.loading {
|
|
308
|
+
display: flex;
|
|
309
|
+
gap: 1rem;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.spinner {
|
|
313
|
+
display: inline-block;
|
|
314
|
+
width: 1.25rem;
|
|
315
|
+
height: 1.25rem;
|
|
316
|
+
border: 2px solid #e4e4e7;
|
|
317
|
+
border-top-color: #71717a;
|
|
318
|
+
border-radius: 50%;
|
|
319
|
+
animation: spin 1s linear infinite;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
@keyframes spin {
|
|
323
|
+
to { transform: rotate(360deg); }
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.input-area {
|
|
327
|
+
border-top: 1px solid #e4e4e7;
|
|
328
|
+
background: #fff;
|
|
329
|
+
padding: 1rem;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
:host([theme="dark"]) .input-area {
|
|
333
|
+
border-top-color: #27272a;
|
|
334
|
+
background: #000;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.input-form {
|
|
338
|
+
max-width: 56rem;
|
|
339
|
+
margin: 0 auto;
|
|
340
|
+
display: flex;
|
|
341
|
+
gap: 0.75rem;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.input-field {
|
|
345
|
+
flex: 1;
|
|
346
|
+
height: 3rem;
|
|
347
|
+
padding: 0 1rem;
|
|
348
|
+
border: 1px solid #e4e4e7;
|
|
349
|
+
border-radius: 0.5rem;
|
|
350
|
+
font-size: 1rem;
|
|
351
|
+
font-family: inherit;
|
|
352
|
+
background: #fff;
|
|
353
|
+
color: #09090b;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
:host([theme="dark"]) .input-field {
|
|
357
|
+
border-color: #3f3f46;
|
|
358
|
+
background: #18181b;
|
|
359
|
+
color: #fafafa;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.input-field:focus {
|
|
363
|
+
outline: 2px solid #2563eb;
|
|
364
|
+
outline-offset: 2px;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.input-field:disabled {
|
|
368
|
+
opacity: 0.5;
|
|
369
|
+
cursor: not-allowed;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.send-button {
|
|
373
|
+
width: 3rem;
|
|
374
|
+
height: 3rem;
|
|
375
|
+
border-radius: 9999px;
|
|
376
|
+
border: none;
|
|
377
|
+
background: #2563eb;
|
|
378
|
+
color: #fff;
|
|
379
|
+
cursor: pointer;
|
|
380
|
+
display: flex;
|
|
381
|
+
align-items: center;
|
|
382
|
+
justify-content: center;
|
|
383
|
+
transition: background 0.2s;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.send-button:hover:not(:disabled) {
|
|
387
|
+
background: #1d4ed8;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.send-button:disabled {
|
|
391
|
+
opacity: 0.5;
|
|
392
|
+
cursor: not-allowed;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.send-icon {
|
|
396
|
+
width: 1.25rem;
|
|
397
|
+
height: 1.25rem;
|
|
398
|
+
}
|
|
399
|
+
`;
|
|
400
|
+
AIChat.properties = {
|
|
401
|
+
apiUrl: { type: String, attribute: "api-url" },
|
|
402
|
+
sessionId: { type: String, attribute: "session-id" },
|
|
403
|
+
chatTitle: { type: String, attribute: "title" },
|
|
404
|
+
theme: { type: String },
|
|
405
|
+
initialMessages: { type: Array }
|
|
406
|
+
};
|
|
407
|
+
__decorateClass([
|
|
408
|
+
state()
|
|
409
|
+
], AIChat.prototype, "messages", 2);
|
|
410
|
+
__decorateClass([
|
|
411
|
+
state()
|
|
412
|
+
], AIChat.prototype, "input", 2);
|
|
413
|
+
__decorateClass([
|
|
414
|
+
state()
|
|
415
|
+
], AIChat.prototype, "isLoading", 2);
|
|
416
|
+
AIChat = __decorateClass([
|
|
417
|
+
customElement("ai-chat")
|
|
418
|
+
], AIChat);
|
|
419
|
+
|
|
420
|
+
export { AIChat };
|
|
421
|
+
//# sourceMappingURL=index.mjs.map
|
|
422
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ai-chat.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import { LitElement, html, css, PropertyValues } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { repeat } from 'lit/directives/repeat.js';\nimport { classMap } from 'lit/directives/class-map.js';\n\nexport interface Message {\n id: string;\n role: 'user' | 'assistant';\n content: string;\n}\n\n/**\n * AI Chat Web Component\n *\n * @fires message-sent - Fired when user sends a message\n * @fires response-received - Fired when AI responds\n * @fires error - Fired when an error occurs\n *\n * @example\n * ```html\n * <ai-chat\n * api-url=\"https://api.example.com\"\n * session-id=\"user-123\"\n * title=\"My AI Assistant\">\n * </ai-chat>\n * ```\n */\n@customElement('ai-chat')\nexport class AIChat extends LitElement {\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n height: 100vh;\n font-family: system-ui, -apple-system, sans-serif;\n background: #fafafa;\n color: #09090b;\n }\n\n :host([theme=\"dark\"]) {\n background: #000;\n color: #fafafa;\n }\n\n .header {\n border-bottom: 1px solid #e4e4e7;\n background: #fff;\n padding: 1rem;\n }\n\n :host([theme=\"dark\"]) .header {\n border-bottom-color: #27272a;\n background: #18181b;\n }\n\n .header-content {\n max-width: 56rem;\n margin: 0 auto;\n }\n\n .title {\n font-size: 1.25rem;\n font-weight: 600;\n margin: 0;\n }\n\n .messages-area {\n flex: 1;\n overflow-y: auto;\n padding: 1.5rem 1rem;\n }\n\n .messages-container {\n max-width: 56rem;\n margin: 0 auto;\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n }\n\n .empty-state {\n text-align: center;\n color: #71717a;\n margin-top: 5rem;\n }\n\n :host([theme=\"dark\"]) .empty-state {\n color: #a1a1aa;\n }\n\n .empty-state p {\n font-size: 1.5rem;\n font-weight: 500;\n margin: 0;\n }\n\n .message {\n display: flex;\n gap: 1rem;\n }\n\n .message.user {\n flex-direction: row-reverse;\n }\n\n .avatar {\n width: 2.5rem;\n height: 2.5rem;\n border-radius: 9999px;\n background: #e4e4e7;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n font-weight: 500;\n font-size: 0.875rem;\n }\n\n :host([theme=\"dark\"]) .avatar {\n background: #3f3f46;\n }\n\n .message-content {\n max-width: 36rem;\n padding: 0.75rem 1rem;\n border-radius: 1rem;\n }\n\n .message.user .message-content {\n background: #2563eb;\n color: #fff;\n }\n\n .message.assistant .message-content {\n background: #fff;\n border: 1px solid #e4e4e7;\n color: #09090b;\n }\n\n :host([theme=\"dark\"]) .message.assistant .message-content {\n background: #27272a;\n border-color: #3f3f46;\n color: #fafafa;\n }\n\n .message-text {\n white-space: pre-wrap;\n margin: 0;\n }\n\n .loading {\n display: flex;\n gap: 1rem;\n }\n\n .spinner {\n display: inline-block;\n width: 1.25rem;\n height: 1.25rem;\n border: 2px solid #e4e4e7;\n border-top-color: #71717a;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n\n .input-area {\n border-top: 1px solid #e4e4e7;\n background: #fff;\n padding: 1rem;\n }\n\n :host([theme=\"dark\"]) .input-area {\n border-top-color: #27272a;\n background: #000;\n }\n\n .input-form {\n max-width: 56rem;\n margin: 0 auto;\n display: flex;\n gap: 0.75rem;\n }\n\n .input-field {\n flex: 1;\n height: 3rem;\n padding: 0 1rem;\n border: 1px solid #e4e4e7;\n border-radius: 0.5rem;\n font-size: 1rem;\n font-family: inherit;\n background: #fff;\n color: #09090b;\n }\n\n :host([theme=\"dark\"]) .input-field {\n border-color: #3f3f46;\n background: #18181b;\n color: #fafafa;\n }\n\n .input-field:focus {\n outline: 2px solid #2563eb;\n outline-offset: 2px;\n }\n\n .input-field:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .send-button {\n width: 3rem;\n height: 3rem;\n border-radius: 9999px;\n border: none;\n background: #2563eb;\n color: #fff;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s;\n }\n\n .send-button:hover:not(:disabled) {\n background: #1d4ed8;\n }\n\n .send-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .send-icon {\n width: 1.25rem;\n height: 1.25rem;\n }\n `;\n\n declare apiUrl: string;\n declare sessionId: string;\n declare chatTitle: string;\n declare theme: 'light' | 'dark';\n declare initialMessages: Message[];\n\n @state()\n private declare messages: Message[];\n\n @state()\n private declare input: string;\n\n @state()\n private declare isLoading: boolean;\n\n private messagesEndRef?: HTMLDivElement;\n\n static override properties = {\n apiUrl: { type: String, attribute: 'api-url' },\n sessionId: { type: String, attribute: 'session-id' },\n chatTitle: { type: String, attribute: 'title' },\n theme: { type: String },\n initialMessages: { type: Array },\n };\n\n constructor() {\n super();\n this.apiUrl = '';\n this.sessionId = 'default-session';\n this.chatTitle = 'My AI Agent';\n this.theme = 'light';\n this.initialMessages = [];\n this.messages = [];\n this.input = '';\n this.isLoading = false;\n }\n\n connectedCallback() {\n super.connectedCallback();\n if (this.initialMessages && this.initialMessages.length > 0) {\n this.messages = [...this.initialMessages];\n }\n }\n\n updated(changedProperties: PropertyValues) {\n super.updated(changedProperties);\n if (changedProperties.has('messages')) {\n this.scrollToBottom();\n }\n }\n\n private scrollToBottom() {\n requestAnimationFrame(() => {\n this.messagesEndRef?.scrollIntoView({ behavior: 'smooth' });\n });\n }\n\n private handleInput(e: Event) {\n this.input = (e.target as HTMLInputElement).value;\n }\n\n private async handleSubmit(e: Event) {\n e.preventDefault();\n\n if (!this.input.trim() || this.isLoading || !this.apiUrl) return;\n\n const userMessage: Message = {\n id: Date.now().toString(),\n role: 'user',\n content: this.input.trim(),\n };\n\n this.messages = [...this.messages, userMessage];\n const questionText = this.input.trim();\n this.input = '';\n this.isLoading = true;\n\n // Dispatch message-sent event\n this.dispatchEvent(new CustomEvent('message-sent', {\n detail: userMessage,\n bubbles: true,\n composed: true,\n }));\n\n try {\n const response = await fetch(`${this.apiUrl}/ask`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n session_id: this.sessionId,\n question: questionText,\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Backend error: ${response.status} ${errorText}`);\n }\n\n const data = await response.json();\n\n const assistantMessage: Message = {\n id: (Date.now() + 1).toString(),\n role: 'assistant',\n content: data.response || 'No response from agent',\n };\n\n this.messages = [...this.messages, assistantMessage];\n\n // Dispatch response-received event\n this.dispatchEvent(new CustomEvent('response-received', {\n detail: assistantMessage,\n bubbles: true,\n composed: true,\n }));\n } catch (err: any) {\n console.error('Backend connection failed:', err);\n\n const errorMessage: Message = {\n id: (Date.now() + 1).toString(),\n role: 'assistant',\n content: `Error: ${err.message}\\n\\nPlease check your API endpoint configuration.`,\n };\n\n this.messages = [...this.messages, errorMessage];\n\n // Dispatch error event\n this.dispatchEvent(new CustomEvent('error', {\n detail: err,\n bubbles: true,\n composed: true,\n }));\n } finally {\n this.isLoading = false;\n }\n }\n\n render() {\n return html`\n <!-- Header -->\n <div class=\"header\">\n <div class=\"header-content\">\n <h1 class=\"title\">${this.chatTitle}</h1>\n </div>\n </div>\n\n <!-- Messages Area -->\n <div class=\"messages-area\">\n <div class=\"messages-container\">\n ${this.messages.length === 0 ? html`\n <div class=\"empty-state\">\n <p>How can I help you today?</p>\n </div>\n ` : ''}\n\n ${repeat(this.messages, (msg) => msg.id, (msg) => html`\n <div class=${classMap({\n message: true,\n user: msg.role === 'user',\n assistant: msg.role === 'assistant'\n })}>\n <div class=\"avatar\">\n ${msg.role === 'user' ? 'U' : 'AI'}\n </div>\n <div class=\"message-content\">\n <p class=\"message-text\">${msg.content}</p>\n </div>\n </div>\n `)}\n\n ${this.isLoading ? html`\n <div class=\"loading\">\n <div class=\"avatar\">AI</div>\n <div class=\"message-content\">\n <div class=\"spinner\"></div>\n </div>\n </div>\n ` : ''}\n\n <div ${(el: Element) => this.messagesEndRef = el as HTMLDivElement}></div>\n </div>\n </div>\n\n <!-- Input Area -->\n <div class=\"input-area\">\n <form class=\"input-form\" @submit=${this.handleSubmit}>\n <input\n type=\"text\"\n class=\"input-field\"\n placeholder=\"Type your message...\"\n .value=${this.input}\n @input=${this.handleInput}\n ?disabled=${this.isLoading}\n />\n <button\n type=\"submit\"\n class=\"send-button\"\n ?disabled=${this.isLoading || !this.input.trim()}\n aria-label=\"Send message\"\n >\n ${this.isLoading ? html`\n <div class=\"spinner\" style=\"border-color: #fff; border-top-color: transparent;\"></div>\n ` : html`\n <svg class=\"send-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"></line>\n <polygon points=\"22 2 15 22 11 13 2 9 22 2\"></polygon>\n </svg>\n `}\n </button>\n </form>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ai-chat': AIChat;\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@a.izzuddin/ai-chat",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "A framework-agnostic AI chat web component. Works with React, Vue, Svelte, Angular, and vanilla JavaScript.",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"custom-elements.json"
|
|
11
|
+
],
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.mjs",
|
|
16
|
+
"require": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"customElements": "custom-elements.json",
|
|
20
|
+
"keywords": [
|
|
21
|
+
"ai",
|
|
22
|
+
"chat",
|
|
23
|
+
"chatbot",
|
|
24
|
+
"web-components",
|
|
25
|
+
"custom-elements",
|
|
26
|
+
"lit",
|
|
27
|
+
"typescript",
|
|
28
|
+
"framework-agnostic",
|
|
29
|
+
"react",
|
|
30
|
+
"vue",
|
|
31
|
+
"svelte",
|
|
32
|
+
"angular"
|
|
33
|
+
],
|
|
34
|
+
"author": "a.izzuddin",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "git+https://github.com/aiddin/ai-chat.git"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"dev": "next dev",
|
|
42
|
+
"dev:demo": "next dev",
|
|
43
|
+
"build": "tsup && npm run analyze",
|
|
44
|
+
"analyze": "custom-elements-manifest analyze --litelement",
|
|
45
|
+
"build:demo": "next build",
|
|
46
|
+
"start": "next start",
|
|
47
|
+
"lint": "eslint",
|
|
48
|
+
"test": "playwright test",
|
|
49
|
+
"test:ui": "playwright test --ui",
|
|
50
|
+
"test:debug": "playwright test --debug",
|
|
51
|
+
"prepublishOnly": "npm run build",
|
|
52
|
+
"tauri": "tauri",
|
|
53
|
+
"tauri:dev": "tauri dev",
|
|
54
|
+
"tauri:build": "tauri build"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"lit": "^3.3.1"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@custom-elements-manifest/analyzer": "^0.11.0",
|
|
61
|
+
"@playwright/test": "^1.57.0",
|
|
62
|
+
"@shadcn/ui": "^0.0.4",
|
|
63
|
+
"@tailwindcss/postcss": "^4",
|
|
64
|
+
"@tauri-apps/cli": "^2.9.4",
|
|
65
|
+
"@types/node": "^20",
|
|
66
|
+
"@types/react": "^19.2.7",
|
|
67
|
+
"@types/react-dom": "^19.2.3",
|
|
68
|
+
"autoprefixer": "^10.4.22",
|
|
69
|
+
"eslint": "^9",
|
|
70
|
+
"eslint-config-next": "16.0.5",
|
|
71
|
+
"markdown-it": "^14.1.0",
|
|
72
|
+
"next": "16.0.5",
|
|
73
|
+
"playwright": "^1.57.0",
|
|
74
|
+
"postcss": "^8.5.6",
|
|
75
|
+
"react": "19.2.0",
|
|
76
|
+
"react-dom": "19.2.0",
|
|
77
|
+
"tailwindcss": "^4.1.17",
|
|
78
|
+
"tsup": "^8.5.1",
|
|
79
|
+
"tw-animate-css": "^1.4.0",
|
|
80
|
+
"typescript": "^5"
|
|
81
|
+
}
|
|
82
|
+
}
|