@a.izzuddin/ai-chat 0.1.2 → 0.2.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.
@@ -528,6 +528,17 @@
528
528
  "default": "'light'",
529
529
  "attribute": "theme"
530
530
  },
531
+ {
532
+ "kind": "field",
533
+ "name": "mode",
534
+ "privacy": "public",
535
+ "type": {
536
+ "text": "'fullscreen' | 'widget'"
537
+ },
538
+ "default": "'fullscreen'",
539
+ "attribute": "mode",
540
+ "reflects": true
541
+ },
531
542
  {
532
543
  "kind": "field",
533
544
  "name": "initialMessages",
@@ -565,6 +576,15 @@
565
576
  "privacy": "private",
566
577
  "default": "false"
567
578
  },
579
+ {
580
+ "kind": "field",
581
+ "name": "isOpen",
582
+ "type": {
583
+ "text": "boolean"
584
+ },
585
+ "privacy": "private",
586
+ "default": "false"
587
+ },
568
588
  {
569
589
  "kind": "field",
570
590
  "name": "messagesEndRef",
@@ -573,6 +593,11 @@
573
593
  },
574
594
  "privacy": "private"
575
595
  },
596
+ {
597
+ "kind": "method",
598
+ "name": "toggleWidget",
599
+ "privacy": "private"
600
+ },
576
601
  {
577
602
  "kind": "method",
578
603
  "name": "scrollToBottom",
@@ -603,6 +628,11 @@
603
628
  }
604
629
  }
605
630
  ]
631
+ },
632
+ {
633
+ "kind": "method",
634
+ "name": "renderChatUI",
635
+ "privacy": "private"
606
636
  }
607
637
  ],
608
638
  "events": [
@@ -661,6 +691,14 @@
661
691
  "default": "'light'",
662
692
  "fieldName": "theme"
663
693
  },
694
+ {
695
+ "name": "mode",
696
+ "type": {
697
+ "text": "'fullscreen' | 'widget'"
698
+ },
699
+ "default": "'fullscreen'",
700
+ "fieldName": "mode"
701
+ },
664
702
  {
665
703
  "name": "initialMessages",
666
704
  "type": {
package/dist/index.d.mts CHANGED
@@ -29,10 +29,12 @@ declare class AIChat extends LitElement {
29
29
  sessionId: string;
30
30
  chatTitle: string;
31
31
  theme: 'light' | 'dark';
32
+ mode: 'fullscreen' | 'widget';
32
33
  initialMessages: Message[];
33
34
  private messages;
34
35
  private input;
35
36
  private isLoading;
37
+ private isOpen;
36
38
  private messagesEndRef?;
37
39
  static properties: {
38
40
  apiUrl: {
@@ -50,16 +52,22 @@ declare class AIChat extends LitElement {
50
52
  theme: {
51
53
  type: StringConstructor;
52
54
  };
55
+ mode: {
56
+ type: StringConstructor;
57
+ reflect: boolean;
58
+ };
53
59
  initialMessages: {
54
60
  type: ArrayConstructor;
55
61
  };
56
62
  };
57
63
  constructor();
64
+ private toggleWidget;
58
65
  connectedCallback(): void;
59
66
  updated(changedProperties: PropertyValues): void;
60
67
  private scrollToBottom;
61
68
  private handleInput;
62
69
  private handleSubmit;
70
+ private renderChatUI;
63
71
  render(): lit_html.TemplateResult<1>;
64
72
  }
65
73
  declare global {
package/dist/index.d.ts CHANGED
@@ -29,10 +29,12 @@ declare class AIChat extends LitElement {
29
29
  sessionId: string;
30
30
  chatTitle: string;
31
31
  theme: 'light' | 'dark';
32
+ mode: 'fullscreen' | 'widget';
32
33
  initialMessages: Message[];
33
34
  private messages;
34
35
  private input;
35
36
  private isLoading;
37
+ private isOpen;
36
38
  private messagesEndRef?;
37
39
  static properties: {
38
40
  apiUrl: {
@@ -50,16 +52,22 @@ declare class AIChat extends LitElement {
50
52
  theme: {
51
53
  type: StringConstructor;
52
54
  };
55
+ mode: {
56
+ type: StringConstructor;
57
+ reflect: boolean;
58
+ };
53
59
  initialMessages: {
54
60
  type: ArrayConstructor;
55
61
  };
56
62
  };
57
63
  constructor();
64
+ private toggleWidget;
58
65
  connectedCallback(): void;
59
66
  updated(changedProperties: PropertyValues): void;
60
67
  private scrollToBottom;
61
68
  private handleInput;
62
69
  private handleSubmit;
70
+ private renderChatUI;
63
71
  render(): lit_html.TemplateResult<1>;
64
72
  }
65
73
  declare global {
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ var __decorateClass = (decorators, target, key, kind) => {
15
15
  if (kind && result) __defProp(target, key, result);
16
16
  return result;
17
17
  };
18
+ var VERSION = "0.2.1";
18
19
  exports.AIChat = class AIChat extends lit.LitElement {
19
20
  constructor() {
20
21
  super();
@@ -22,10 +23,15 @@ exports.AIChat = class AIChat extends lit.LitElement {
22
23
  this.sessionId = "default-session";
23
24
  this.chatTitle = "My AI Agent";
24
25
  this.theme = "light";
26
+ this.mode = "fullscreen";
25
27
  this.initialMessages = [];
26
28
  this.messages = [];
27
29
  this.input = "";
28
30
  this.isLoading = false;
31
+ this.isOpen = false;
32
+ }
33
+ toggleWidget() {
34
+ this.isOpen = !this.isOpen;
29
35
  }
30
36
  connectedCallback() {
31
37
  super.connectedCallback();
@@ -108,7 +114,7 @@ Please check your API endpoint configuration.`
108
114
  this.isLoading = false;
109
115
  }
110
116
  }
111
- render() {
117
+ renderChatUI() {
112
118
  return lit.html`
113
119
  <!-- Header -->
114
120
  <div class="header">
@@ -182,24 +188,137 @@ Please check your API endpoint configuration.`
182
188
  </button>
183
189
  </form>
184
190
  </div>
191
+
192
+ <!-- Version -->
193
+ <div class="version-tag">v${VERSION}</div>
185
194
  `;
186
195
  }
196
+ render() {
197
+ if (this.mode === "widget") {
198
+ return lit.html`
199
+ <div class="widget-container">
200
+ <!-- Chat Window -->
201
+ <div class=${classMap_js.classMap({ "widget-window": true, "open": this.isOpen })}>
202
+ ${this.renderChatUI()}
203
+ </div>
204
+
205
+ <!-- Toggle Button -->
206
+ <button
207
+ class="widget-button"
208
+ @click=${this.toggleWidget}
209
+ aria-label=${this.isOpen ? "Close chat" : "Open chat"}
210
+ >
211
+ ${this.isOpen ? lit.html`
212
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
213
+ <line x1="18" y1="6" x2="6" y2="18"></line>
214
+ <line x1="6" y1="6" x2="18" y2="18"></line>
215
+ </svg>
216
+ ` : lit.html`
217
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
218
+ <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>
219
+ </svg>
220
+ `}
221
+ </button>
222
+ </div>
223
+ `;
224
+ }
225
+ return this.renderChatUI();
226
+ }
187
227
  };
188
228
  exports.AIChat.styles = lit.css`
189
229
  :host {
230
+ font-family: system-ui, -apple-system, sans-serif;
231
+ color: #09090b;
232
+ }
233
+
234
+ /* Fullscreen mode (default) */
235
+ :host([mode="fullscreen"]) {
190
236
  display: flex;
191
237
  flex-direction: column;
192
238
  height: 100vh;
193
- font-family: system-ui, -apple-system, sans-serif;
194
239
  background: #fafafa;
195
- color: #09090b;
196
240
  }
197
241
 
198
- :host([theme="dark"]) {
242
+ :host([mode="fullscreen"][theme="dark"]) {
199
243
  background: #000;
200
244
  color: #fafafa;
201
245
  }
202
246
 
247
+ /* Widget mode */
248
+ :host([mode="widget"]) {
249
+ position: fixed;
250
+ bottom: 20px;
251
+ right: 20px;
252
+ z-index: 9999;
253
+ }
254
+
255
+ .widget-container {
256
+ position: relative;
257
+ }
258
+
259
+ .widget-button {
260
+ width: 60px;
261
+ height: 60px;
262
+ border-radius: 50%;
263
+ background: #2563eb;
264
+ border: none;
265
+ cursor: pointer;
266
+ display: flex;
267
+ align-items: center;
268
+ justify-content: center;
269
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
270
+ transition: transform 0.2s, box-shadow 0.2s;
271
+ }
272
+
273
+ .widget-button:hover {
274
+ transform: scale(1.05);
275
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
276
+ }
277
+
278
+ .widget-button svg {
279
+ width: 28px;
280
+ height: 28px;
281
+ color: white;
282
+ }
283
+
284
+ .widget-window {
285
+ position: absolute;
286
+ bottom: 80px;
287
+ right: 0;
288
+ width: 380px;
289
+ height: 600px;
290
+ background: #fff;
291
+ border-radius: 12px;
292
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
293
+ display: flex;
294
+ flex-direction: column;
295
+ overflow: hidden;
296
+ opacity: 0;
297
+ transform: scale(0.9) translateY(20px);
298
+ pointer-events: none;
299
+ transition: opacity 0.2s, transform 0.2s;
300
+ }
301
+
302
+ .widget-window.open {
303
+ opacity: 1;
304
+ transform: scale(1) translateY(0);
305
+ pointer-events: all;
306
+ }
307
+
308
+ :host([theme="dark"]) .widget-window {
309
+ background: #18181b;
310
+ color: #fafafa;
311
+ }
312
+
313
+ @media (max-width: 480px) {
314
+ .widget-window {
315
+ width: calc(100vw - 40px);
316
+ height: calc(100vh - 100px);
317
+ bottom: 80px;
318
+ right: 0;
319
+ }
320
+ }
321
+
203
322
  .header {
204
323
  border-bottom: 1px solid #e4e4e7;
205
324
  background: #fff;
@@ -398,12 +517,26 @@ exports.AIChat.styles = lit.css`
398
517
  width: 1.25rem;
399
518
  height: 1.25rem;
400
519
  }
520
+
521
+ .version-tag {
522
+ text-align: center;
523
+ padding: 0.5rem;
524
+ font-size: 0.75rem;
525
+ color: #71717a;
526
+ border-top: 1px solid #e4e4e7;
527
+ }
528
+
529
+ :host([theme="dark"]) .version-tag {
530
+ color: #a1a1aa;
531
+ border-top-color: #27272a;
532
+ }
401
533
  `;
402
534
  exports.AIChat.properties = {
403
535
  apiUrl: { type: String, attribute: "api-url" },
404
536
  sessionId: { type: String, attribute: "session-id" },
405
537
  chatTitle: { type: String, attribute: "title" },
406
538
  theme: { type: String },
539
+ mode: { type: String, reflect: true },
407
540
  initialMessages: { type: Array }
408
541
  };
409
542
  __decorateClass([
@@ -415,6 +548,9 @@ __decorateClass([
415
548
  __decorateClass([
416
549
  decorators_js.state()
417
550
  ], exports.AIChat.prototype, "isLoading", 2);
551
+ __decorateClass([
552
+ decorators_js.state()
553
+ ], exports.AIChat.prototype, "isOpen", 2);
418
554
  exports.AIChat = __decorateClass([
419
555
  decorators_js.customElement("ai-chat")
420
556
  ], exports.AIChat);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/ai-chat.ts"],"names":["AIChat","LitElement","html","repeat","classMap","css","state","customElement"],"mappings":"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","file":"index.js","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"]}
1
+ {"version":3,"sources":["../src/components/ai-chat.ts"],"names":["AIChat","LitElement","html","repeat","classMap","css","state","customElement"],"mappings":"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","file":"index.js","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\nconst VERSION = '0.2.1';\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 font-family: system-ui, -apple-system, sans-serif;\n color: #09090b;\n }\n\n /* Fullscreen mode (default) */\n :host([mode=\"fullscreen\"]) {\n display: flex;\n flex-direction: column;\n height: 100vh;\n background: #fafafa;\n }\n\n :host([mode=\"fullscreen\"][theme=\"dark\"]) {\n background: #000;\n color: #fafafa;\n }\n\n /* Widget mode */\n :host([mode=\"widget\"]) {\n position: fixed;\n bottom: 20px;\n right: 20px;\n z-index: 9999;\n }\n\n .widget-container {\n position: relative;\n }\n\n .widget-button {\n width: 60px;\n height: 60px;\n border-radius: 50%;\n background: #2563eb;\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n transition: transform 0.2s, box-shadow 0.2s;\n }\n\n .widget-button:hover {\n transform: scale(1.05);\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);\n }\n\n .widget-button svg {\n width: 28px;\n height: 28px;\n color: white;\n }\n\n .widget-window {\n position: absolute;\n bottom: 80px;\n right: 0;\n width: 380px;\n height: 600px;\n background: #fff;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n opacity: 0;\n transform: scale(0.9) translateY(20px);\n pointer-events: none;\n transition: opacity 0.2s, transform 0.2s;\n }\n\n .widget-window.open {\n opacity: 1;\n transform: scale(1) translateY(0);\n pointer-events: all;\n }\n\n :host([theme=\"dark\"]) .widget-window {\n background: #18181b;\n color: #fafafa;\n }\n\n @media (max-width: 480px) {\n .widget-window {\n width: calc(100vw - 40px);\n height: calc(100vh - 100px);\n bottom: 80px;\n right: 0;\n }\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 .version-tag {\n text-align: center;\n padding: 0.5rem;\n font-size: 0.75rem;\n color: #71717a;\n border-top: 1px solid #e4e4e7;\n }\n\n :host([theme=\"dark\"]) .version-tag {\n color: #a1a1aa;\n border-top-color: #27272a;\n }\n `;\n\n declare apiUrl: string;\n declare sessionId: string;\n declare chatTitle: string;\n declare theme: 'light' | 'dark';\n declare mode: 'fullscreen' | 'widget';\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 @state()\n private declare isOpen: 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 mode: { type: String, reflect: true },\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.mode = 'fullscreen';\n this.initialMessages = [];\n this.messages = [];\n this.input = '';\n this.isLoading = false;\n this.isOpen = false;\n }\n\n private toggleWidget() {\n this.isOpen = !this.isOpen;\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 private renderChatUI() {\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 <!-- Version -->\n <div class=\"version-tag\">v${VERSION}</div>\n `;\n }\n\n render() {\n if (this.mode === 'widget') {\n return html`\n <div class=\"widget-container\">\n <!-- Chat Window -->\n <div class=${classMap({ 'widget-window': true, 'open': this.isOpen })}>\n ${this.renderChatUI()}\n </div>\n\n <!-- Toggle Button -->\n <button\n class=\"widget-button\"\n @click=${this.toggleWidget}\n aria-label=${this.isOpen ? 'Close chat' : 'Open chat'}\n >\n ${this.isOpen ? html`\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n ` : html`\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <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>\n </svg>\n `}\n </button>\n </div>\n `;\n }\n\n // Fullscreen mode\n return this.renderChatUI();\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ai-chat': AIChat;\n }\n}\n"]}
package/dist/index.mjs CHANGED
@@ -13,6 +13,7 @@ var __decorateClass = (decorators, target, key, kind) => {
13
13
  if (kind && result) __defProp(target, key, result);
14
14
  return result;
15
15
  };
16
+ var VERSION = "0.2.1";
16
17
  var AIChat = class extends LitElement {
17
18
  constructor() {
18
19
  super();
@@ -20,10 +21,15 @@ var AIChat = class extends LitElement {
20
21
  this.sessionId = "default-session";
21
22
  this.chatTitle = "My AI Agent";
22
23
  this.theme = "light";
24
+ this.mode = "fullscreen";
23
25
  this.initialMessages = [];
24
26
  this.messages = [];
25
27
  this.input = "";
26
28
  this.isLoading = false;
29
+ this.isOpen = false;
30
+ }
31
+ toggleWidget() {
32
+ this.isOpen = !this.isOpen;
27
33
  }
28
34
  connectedCallback() {
29
35
  super.connectedCallback();
@@ -106,7 +112,7 @@ Please check your API endpoint configuration.`
106
112
  this.isLoading = false;
107
113
  }
108
114
  }
109
- render() {
115
+ renderChatUI() {
110
116
  return html`
111
117
  <!-- Header -->
112
118
  <div class="header">
@@ -180,24 +186,137 @@ Please check your API endpoint configuration.`
180
186
  </button>
181
187
  </form>
182
188
  </div>
189
+
190
+ <!-- Version -->
191
+ <div class="version-tag">v${VERSION}</div>
183
192
  `;
184
193
  }
194
+ render() {
195
+ if (this.mode === "widget") {
196
+ return html`
197
+ <div class="widget-container">
198
+ <!-- Chat Window -->
199
+ <div class=${classMap({ "widget-window": true, "open": this.isOpen })}>
200
+ ${this.renderChatUI()}
201
+ </div>
202
+
203
+ <!-- Toggle Button -->
204
+ <button
205
+ class="widget-button"
206
+ @click=${this.toggleWidget}
207
+ aria-label=${this.isOpen ? "Close chat" : "Open chat"}
208
+ >
209
+ ${this.isOpen ? html`
210
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
211
+ <line x1="18" y1="6" x2="6" y2="18"></line>
212
+ <line x1="6" y1="6" x2="18" y2="18"></line>
213
+ </svg>
214
+ ` : html`
215
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
216
+ <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>
217
+ </svg>
218
+ `}
219
+ </button>
220
+ </div>
221
+ `;
222
+ }
223
+ return this.renderChatUI();
224
+ }
185
225
  };
186
226
  AIChat.styles = css`
187
227
  :host {
228
+ font-family: system-ui, -apple-system, sans-serif;
229
+ color: #09090b;
230
+ }
231
+
232
+ /* Fullscreen mode (default) */
233
+ :host([mode="fullscreen"]) {
188
234
  display: flex;
189
235
  flex-direction: column;
190
236
  height: 100vh;
191
- font-family: system-ui, -apple-system, sans-serif;
192
237
  background: #fafafa;
193
- color: #09090b;
194
238
  }
195
239
 
196
- :host([theme="dark"]) {
240
+ :host([mode="fullscreen"][theme="dark"]) {
197
241
  background: #000;
198
242
  color: #fafafa;
199
243
  }
200
244
 
245
+ /* Widget mode */
246
+ :host([mode="widget"]) {
247
+ position: fixed;
248
+ bottom: 20px;
249
+ right: 20px;
250
+ z-index: 9999;
251
+ }
252
+
253
+ .widget-container {
254
+ position: relative;
255
+ }
256
+
257
+ .widget-button {
258
+ width: 60px;
259
+ height: 60px;
260
+ border-radius: 50%;
261
+ background: #2563eb;
262
+ border: none;
263
+ cursor: pointer;
264
+ display: flex;
265
+ align-items: center;
266
+ justify-content: center;
267
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
268
+ transition: transform 0.2s, box-shadow 0.2s;
269
+ }
270
+
271
+ .widget-button:hover {
272
+ transform: scale(1.05);
273
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
274
+ }
275
+
276
+ .widget-button svg {
277
+ width: 28px;
278
+ height: 28px;
279
+ color: white;
280
+ }
281
+
282
+ .widget-window {
283
+ position: absolute;
284
+ bottom: 80px;
285
+ right: 0;
286
+ width: 380px;
287
+ height: 600px;
288
+ background: #fff;
289
+ border-radius: 12px;
290
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
291
+ display: flex;
292
+ flex-direction: column;
293
+ overflow: hidden;
294
+ opacity: 0;
295
+ transform: scale(0.9) translateY(20px);
296
+ pointer-events: none;
297
+ transition: opacity 0.2s, transform 0.2s;
298
+ }
299
+
300
+ .widget-window.open {
301
+ opacity: 1;
302
+ transform: scale(1) translateY(0);
303
+ pointer-events: all;
304
+ }
305
+
306
+ :host([theme="dark"]) .widget-window {
307
+ background: #18181b;
308
+ color: #fafafa;
309
+ }
310
+
311
+ @media (max-width: 480px) {
312
+ .widget-window {
313
+ width: calc(100vw - 40px);
314
+ height: calc(100vh - 100px);
315
+ bottom: 80px;
316
+ right: 0;
317
+ }
318
+ }
319
+
201
320
  .header {
202
321
  border-bottom: 1px solid #e4e4e7;
203
322
  background: #fff;
@@ -396,12 +515,26 @@ AIChat.styles = css`
396
515
  width: 1.25rem;
397
516
  height: 1.25rem;
398
517
  }
518
+
519
+ .version-tag {
520
+ text-align: center;
521
+ padding: 0.5rem;
522
+ font-size: 0.75rem;
523
+ color: #71717a;
524
+ border-top: 1px solid #e4e4e7;
525
+ }
526
+
527
+ :host([theme="dark"]) .version-tag {
528
+ color: #a1a1aa;
529
+ border-top-color: #27272a;
530
+ }
399
531
  `;
400
532
  AIChat.properties = {
401
533
  apiUrl: { type: String, attribute: "api-url" },
402
534
  sessionId: { type: String, attribute: "session-id" },
403
535
  chatTitle: { type: String, attribute: "title" },
404
536
  theme: { type: String },
537
+ mode: { type: String, reflect: true },
405
538
  initialMessages: { type: Array }
406
539
  };
407
540
  __decorateClass([
@@ -413,6 +546,9 @@ __decorateClass([
413
546
  __decorateClass([
414
547
  state()
415
548
  ], AIChat.prototype, "isLoading", 2);
549
+ __decorateClass([
550
+ state()
551
+ ], AIChat.prototype, "isOpen", 2);
416
552
  AIChat = __decorateClass([
417
553
  customElement("ai-chat")
418
554
  ], AIChat);
@@ -1 +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"]}
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\nconst VERSION = '0.2.1';\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 font-family: system-ui, -apple-system, sans-serif;\n color: #09090b;\n }\n\n /* Fullscreen mode (default) */\n :host([mode=\"fullscreen\"]) {\n display: flex;\n flex-direction: column;\n height: 100vh;\n background: #fafafa;\n }\n\n :host([mode=\"fullscreen\"][theme=\"dark\"]) {\n background: #000;\n color: #fafafa;\n }\n\n /* Widget mode */\n :host([mode=\"widget\"]) {\n position: fixed;\n bottom: 20px;\n right: 20px;\n z-index: 9999;\n }\n\n .widget-container {\n position: relative;\n }\n\n .widget-button {\n width: 60px;\n height: 60px;\n border-radius: 50%;\n background: #2563eb;\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n transition: transform 0.2s, box-shadow 0.2s;\n }\n\n .widget-button:hover {\n transform: scale(1.05);\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);\n }\n\n .widget-button svg {\n width: 28px;\n height: 28px;\n color: white;\n }\n\n .widget-window {\n position: absolute;\n bottom: 80px;\n right: 0;\n width: 380px;\n height: 600px;\n background: #fff;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n opacity: 0;\n transform: scale(0.9) translateY(20px);\n pointer-events: none;\n transition: opacity 0.2s, transform 0.2s;\n }\n\n .widget-window.open {\n opacity: 1;\n transform: scale(1) translateY(0);\n pointer-events: all;\n }\n\n :host([theme=\"dark\"]) .widget-window {\n background: #18181b;\n color: #fafafa;\n }\n\n @media (max-width: 480px) {\n .widget-window {\n width: calc(100vw - 40px);\n height: calc(100vh - 100px);\n bottom: 80px;\n right: 0;\n }\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 .version-tag {\n text-align: center;\n padding: 0.5rem;\n font-size: 0.75rem;\n color: #71717a;\n border-top: 1px solid #e4e4e7;\n }\n\n :host([theme=\"dark\"]) .version-tag {\n color: #a1a1aa;\n border-top-color: #27272a;\n }\n `;\n\n declare apiUrl: string;\n declare sessionId: string;\n declare chatTitle: string;\n declare theme: 'light' | 'dark';\n declare mode: 'fullscreen' | 'widget';\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 @state()\n private declare isOpen: 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 mode: { type: String, reflect: true },\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.mode = 'fullscreen';\n this.initialMessages = [];\n this.messages = [];\n this.input = '';\n this.isLoading = false;\n this.isOpen = false;\n }\n\n private toggleWidget() {\n this.isOpen = !this.isOpen;\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 private renderChatUI() {\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 <!-- Version -->\n <div class=\"version-tag\">v${VERSION}</div>\n `;\n }\n\n render() {\n if (this.mode === 'widget') {\n return html`\n <div class=\"widget-container\">\n <!-- Chat Window -->\n <div class=${classMap({ 'widget-window': true, 'open': this.isOpen })}>\n ${this.renderChatUI()}\n </div>\n\n <!-- Toggle Button -->\n <button\n class=\"widget-button\"\n @click=${this.toggleWidget}\n aria-label=${this.isOpen ? 'Close chat' : 'Open chat'}\n >\n ${this.isOpen ? html`\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n ` : html`\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <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>\n </svg>\n `}\n </button>\n </div>\n `;\n }\n\n // Fullscreen mode\n return this.renderChatUI();\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ai-chat': AIChat;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a.izzuddin/ai-chat",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "description": "A framework-agnostic AI chat web component. Works with React, Vue, Svelte, Angular, and vanilla JavaScript.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",