@cloudflare/ai-search-snippet 0.0.17

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.
@@ -0,0 +1,3413 @@
1
+ var A = Object.defineProperty;
2
+ var R = (o, i, e) => i in o ? A(o, i, { enumerable: !0, configurable: !0, writable: !0, value: e }) : o[i] = e;
3
+ var r = (o, i, e) => R(o, typeof i != "symbol" ? i + "" : i, e);
4
+ function T(o, i) {
5
+ let e;
6
+ return function(...s) {
7
+ const a = () => {
8
+ clearTimeout(e), o(...s);
9
+ };
10
+ clearTimeout(e), e = setTimeout(a, i);
11
+ };
12
+ }
13
+ function l(o) {
14
+ const i = document.createElement("div");
15
+ return i.textContent = o, i.innerHTML;
16
+ }
17
+ function C(o) {
18
+ return new DOMParser().parseFromString(o, "text/html").documentElement.textContent || "";
19
+ }
20
+ function H(o) {
21
+ const i = new Date(o), t = (/* @__PURE__ */ new Date()).getTime() - i.getTime();
22
+ if (t < 6e4)
23
+ return "Just now";
24
+ if (t < 36e5) {
25
+ const s = Math.floor(t / 6e4);
26
+ return `${s} ${s === 1 ? "minute" : "minutes"} ago`;
27
+ }
28
+ if (t < 864e5) {
29
+ const s = Math.floor(t / 36e5);
30
+ return `${s} ${s === 1 ? "hour" : "hours"} ago`;
31
+ }
32
+ return i.toLocaleString(void 0, {
33
+ month: "short",
34
+ day: "numeric",
35
+ hour: "2-digit",
36
+ minute: "2-digit"
37
+ });
38
+ }
39
+ function S(o = "id") {
40
+ return `${o}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
41
+ }
42
+ function u(o, i) {
43
+ return o !== null ? o : i;
44
+ }
45
+ function f(o, i) {
46
+ return o === null ? i : o === "true" || o === "";
47
+ }
48
+ function b(o, i) {
49
+ if (o === null) return i;
50
+ const e = Number.parseInt(o, 10);
51
+ return Number.isNaN(e) ? i : e;
52
+ }
53
+ function m(o, i) {
54
+ return new CustomEvent(o, {
55
+ detail: i,
56
+ bubbles: !0,
57
+ composed: !0,
58
+ cancelable: !0
59
+ });
60
+ }
61
+ function w(o) {
62
+ if (!o)
63
+ throw new Error("API URL is required");
64
+ return new P(o);
65
+ }
66
+ class N {
67
+ constructor(i) {
68
+ r(this, "activeRequests", /* @__PURE__ */ new Map());
69
+ r(this, "baseUrl");
70
+ this.baseUrl = i.replace(/\/$/, "");
71
+ }
72
+ search(i, e) {
73
+ throw new Error("Not implemented");
74
+ }
75
+ searchStream(i, e) {
76
+ throw new Error("Not implemented");
77
+ }
78
+ chat(i, e) {
79
+ throw new Error("Not implemented");
80
+ }
81
+ cancelRequest(i) {
82
+ throw new Error("Not implemented");
83
+ }
84
+ cancelAllRequests() {
85
+ throw new Error("Not implemented");
86
+ }
87
+ }
88
+ class P extends N {
89
+ request(i = {}, e, t) {
90
+ return fetch(`${this.baseUrl}/${e}`, {
91
+ method: "POST",
92
+ body: JSON.stringify({
93
+ messages: [{ role: "user", content: i.query }],
94
+ stream: i.streaming,
95
+ max_results: i.maxResults
96
+ }),
97
+ headers: {
98
+ "Content-Type": "application/json"
99
+ },
100
+ signal: t
101
+ });
102
+ }
103
+ /**
104
+ * Performs a search query with optional streaming
105
+ */
106
+ async search(i, e = {}) {
107
+ const t = this.generateRequestId(), s = new AbortController(), a = e.signal || s.signal;
108
+ this.registerRequest(t, s);
109
+ try {
110
+ const n = await this.request(
111
+ {
112
+ query: i,
113
+ streaming: !1,
114
+ maxResults: 30
115
+ },
116
+ "search",
117
+ a
118
+ );
119
+ if (!n.ok)
120
+ throw new Error(`HTTP error! status: ${n.status}`);
121
+ if (!n.body)
122
+ throw new Error("Response body is empty");
123
+ const c = await n.json();
124
+ if (c.success && c.result) {
125
+ const h = /* @__PURE__ */ new Map();
126
+ for (const d of c.result.chunks) {
127
+ const g = d.item.key, p = d.scoring_details.vector_score;
128
+ (!h.has(g) || (h.get(g)?.score ?? 0) < p) && h.set(g, { chunk: d, score: p });
129
+ }
130
+ return Array.from(h.values()).sort((d, g) => g.score - d.score).slice(0, 10).map(
131
+ ({ chunk: d }) => ({
132
+ type: "result",
133
+ id: d.id,
134
+ title: C(d.item.metadata.title),
135
+ description: C(d.item.metadata.description),
136
+ url: d.item.key,
137
+ image: d.item.metadata.image || void 0,
138
+ metadata: d.item.metadata
139
+ })
140
+ );
141
+ }
142
+ throw c.success === !1 ? new Error(c.error) : new Error("Unknown error");
143
+ } finally {
144
+ this.unregisterRequest(t);
145
+ }
146
+ }
147
+ async *searchStream(i, e) {
148
+ const t = this.generateRequestId(), s = new AbortController(), a = e?.signal || s.signal;
149
+ this.registerRequest(t, s);
150
+ const n = await this.request(
151
+ {
152
+ query: i,
153
+ streaming: !0
154
+ },
155
+ "ai-search",
156
+ a
157
+ );
158
+ if (!n.ok)
159
+ throw new Error(`HTTP error! status: ${n.status}`);
160
+ if (!n.body)
161
+ throw new Error("Response body is empty");
162
+ let c = "";
163
+ const h = n.body.getReader(), d = new TextDecoder();
164
+ for (; ; ) {
165
+ const { done: p, value: k } = await h.read();
166
+ if (p)
167
+ break;
168
+ const B = d.decode(k, { stream: !0 });
169
+ c += B;
170
+ }
171
+ yield {
172
+ type: "result",
173
+ id: "",
174
+ title: "",
175
+ description: c.replaceAll("data: ", "").trim().split(`
176
+
177
+ `).map((p) => JSON.parse(p)).map((p) => p.response).join(""),
178
+ url: "",
179
+ metadata: {}
180
+ };
181
+ }
182
+ async *chat(i, e) {
183
+ const t = new AbortController(), s = e?.signal || t.signal, a = await this.request(
184
+ {
185
+ query: i,
186
+ streaming: !1
187
+ },
188
+ "chat/completions",
189
+ s
190
+ );
191
+ if (!a.ok)
192
+ throw new Error(`HTTP error! status: ${a.status}`);
193
+ if (!a.body)
194
+ throw new Error("Response body is empty");
195
+ yield {
196
+ type: "text",
197
+ message: (await a.json()).choices.map((c) => c.message.content).join("")
198
+ };
199
+ }
200
+ /**
201
+ * Cancels an active request by ID
202
+ */
203
+ cancelRequest(i) {
204
+ const e = this.activeRequests.get(i);
205
+ e && (e.controller.abort(), this.unregisterRequest(i));
206
+ }
207
+ /**
208
+ * Cancels all active requests
209
+ */
210
+ cancelAllRequests() {
211
+ for (const [i] of this.activeRequests)
212
+ this.cancelRequest(i);
213
+ }
214
+ /**
215
+ * Register an active request
216
+ */
217
+ registerRequest(i, e) {
218
+ this.activeRequests.set(i, {
219
+ id: i,
220
+ controller: e,
221
+ timestamp: Date.now()
222
+ });
223
+ }
224
+ /**
225
+ * Unregister a completed request
226
+ */
227
+ unregisterRequest(i) {
228
+ this.activeRequests.delete(i);
229
+ }
230
+ /**
231
+ * Generate unique request ID
232
+ */
233
+ generateRequestId() {
234
+ return `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
235
+ }
236
+ }
237
+ const $ = `
238
+ /* Chat container */
239
+ .chat-container {
240
+ display: flex;
241
+ flex-direction: column;
242
+ height: 100%;
243
+ overflow-y: auto;
244
+ }
245
+
246
+ /* Messages area */
247
+ .chat-messages {
248
+ flex: 1;
249
+ overflow-y: auto;
250
+ overflow-x: hidden;
251
+ padding: var(--search-snippet-spacing-md);
252
+ display: flex;
253
+ flex-direction: column;
254
+ gap: var(--search-snippet-spacing-md);
255
+ }
256
+
257
+ .chat-messages::-webkit-scrollbar {
258
+ width: 8px;
259
+ }
260
+
261
+ .chat-messages::-webkit-scrollbar-track {
262
+ background: var(--search-snippet-surface);
263
+ }
264
+
265
+ .chat-messages::-webkit-scrollbar-thumb {
266
+ background: var(--search-snippet-border-color);
267
+ border-radius: var(--search-snippet-border-radius);
268
+ }
269
+
270
+ .chat-messages::-webkit-scrollbar-thumb:hover {
271
+ background: var(--search-snippet-text-secondary);
272
+ }
273
+
274
+ /* Message */
275
+ .chat-message {
276
+ display: flex;
277
+ gap: var(--search-snippet-spacing-sm);
278
+ max-width: 85%;
279
+ animation: slideIn var(--search-snippet-animation-duration) ease-out;
280
+ }
281
+
282
+ @keyframes slideIn {
283
+ from {
284
+ opacity: 0;
285
+ transform: translateY(10px);
286
+ }
287
+ to {
288
+ opacity: 1;
289
+ transform: translateY(0);
290
+ }
291
+ }
292
+
293
+ .chat-message-user {
294
+ align-self: flex-end;
295
+ flex-direction: row-reverse;
296
+ }
297
+
298
+ .chat-message-assistant {
299
+ align-self: flex-start;
300
+ }
301
+
302
+ .chat-message-system {
303
+ align-self: center;
304
+ max-width: 100%;
305
+ }
306
+
307
+ /* Message avatar */
308
+ .chat-message-avatar {
309
+ width: 32px;
310
+ height: 32px;
311
+ border-radius: 50%;
312
+ display: flex;
313
+ align-items: center;
314
+ justify-content: center;
315
+ flex-shrink: 0;
316
+ font-size: var(--search-snippet-font-size-sm);
317
+ font-weight: var(--search-snippet-font-weight-bold);
318
+ background: var(--search-snippet-primary-color);
319
+ color: white;
320
+ }
321
+
322
+ .chat-message-assistant .chat-message-avatar {
323
+ background: var(--search-snippet-surface);
324
+ color: var(--search-snippet-text-color);
325
+ border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
326
+ }
327
+
328
+ /* Message content */
329
+ .chat-message-content {
330
+ flex: 1;
331
+ display: flex;
332
+ flex-direction: column;
333
+ gap: var(--search-snippet-spacing-xs);
334
+ max-width: 100%;
335
+ }
336
+
337
+ .chat-message-content ol, .chat-message-content ul {
338
+ margin-inline-start: 16px;
339
+ }
340
+
341
+ .chat-message-content ol li, .chat-message-content ul li {
342
+ padding-inline-start: 0;
343
+ }
344
+
345
+ .chat-message-bubble {
346
+ padding: var(--search-snippet-spacing-sm) var(--search-snippet-spacing-md);
347
+ border-radius: var(--search-snippet-border-radius);
348
+ word-wrap: break-word;
349
+ overflow-wrap: break-word;
350
+ }
351
+
352
+ .chat-message-user .chat-message-bubble {
353
+ background: var(--search-snippet-user-message-bg);
354
+ color: var(--search-snippet-user-message-text);
355
+ border-top-right-radius: var(--search-snippet-spacing-xs);
356
+ }
357
+
358
+ .chat-message-assistant .chat-message-bubble {
359
+ background: var(--search-snippet-assistant-message-bg);
360
+ color: var(--search-snippet-assistant-message-text);
361
+ border-top-left-radius: var(--search-snippet-spacing-xs);
362
+ border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
363
+ }
364
+
365
+ .chat-message-system .chat-message-bubble {
366
+ background: var(--search-snippet-system-message-bg);
367
+ color: var(--search-snippet-system-message-text);
368
+ text-align: center;
369
+ font-size: var(--search-snippet-font-size-sm);
370
+ padding: var(--search-snippet-spacing-xs) var(--search-snippet-spacing-md);
371
+ }
372
+
373
+ .chat-message-text {
374
+ font-size: var(--search-snippet-font-size-base);
375
+ line-height: 1.5;
376
+ white-space: wrap;
377
+ }
378
+ .chat-message-text li{
379
+ padding-inline-start: var(--search-snippet-spacing-md);
380
+ }
381
+
382
+ .chat-message-metadata {
383
+ display: flex;
384
+ align-items: center;
385
+ gap: var(--search-snippet-spacing-sm);
386
+ font-size: var(--search-snippet-font-size-sm);
387
+ color: var(--search-snippet-text-secondary);
388
+ }
389
+
390
+ .chat-message-user .chat-message-metadata {
391
+ justify-content: flex-end;
392
+ }
393
+
394
+ .chat-message-time {
395
+ opacity: 0.7;
396
+ }
397
+
398
+ /* Streaming indicator */
399
+ .chat-streaming {
400
+ display: flex;
401
+ align-items: center;
402
+ gap: var(--search-snippet-spacing-xs);
403
+ }
404
+
405
+ .chat-streaming-dot {
406
+ width: 8px;
407
+ height: 8px;
408
+ border-radius: 50%;
409
+ background: currentColor;
410
+ animation: pulse 1.4s ease-in-out infinite;
411
+ }
412
+
413
+ .chat-streaming-dot:nth-child(2) {
414
+ animation-delay: 0.2s;
415
+ }
416
+
417
+ .chat-streaming-dot:nth-child(3) {
418
+ animation-delay: 0.4s;
419
+ }
420
+
421
+ @keyframes pulse {
422
+ 0%, 80%, 100% {
423
+ opacity: 0.3;
424
+ transform: scale(0.8);
425
+ }
426
+ 40% {
427
+ opacity: 1;
428
+ transform: scale(1);
429
+ }
430
+ }
431
+
432
+ /* Input area */
433
+ .chat-input-area {
434
+ padding: var(--search-snippet-spacing-md);
435
+ border-top: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
436
+ background: var(--search-snippet-surface);
437
+ }
438
+
439
+ .chat-input-wrapper {
440
+ display: flex;
441
+ gap: var(--search-snippet-spacing-sm);
442
+ align-items: flex-end;
443
+ }
444
+
445
+ .chat-input {
446
+ flex: 1;
447
+ min-height: var(--search-snippet-input-height);
448
+ max-height: 120px;
449
+ padding: var(--search-snippet-spacing-sm) var(--search-snippet-spacing-md);
450
+ font-family: var(--search-snippet-font-family);
451
+ font-size: var(--search-snippet-font-size-base);
452
+ line-height: var(--search-snippet-line-height);
453
+ color: var(--search-snippet-text-color);
454
+ background: var(--search-snippet-background);
455
+ border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
456
+ border-radius: var(--search-snippet-border-radius);
457
+ outline: none;
458
+ resize: vertical;
459
+ transition: var(--search-snippet-transition);
460
+ }
461
+
462
+ .chat-input:focus {
463
+ border-color: var(--search-snippet-primary-color);
464
+ box-shadow: 0 0 0 3px var(--search-snippet-focus-ring);
465
+ }
466
+
467
+ .chat-input::placeholder {
468
+ color: var(--search-snippet-text-secondary);
469
+ }
470
+
471
+ .chat-input:disabled {
472
+ background: var(--search-snippet-surface);
473
+ cursor: not-allowed;
474
+ opacity: 0.6;
475
+ }
476
+
477
+ .chat-send-button {
478
+ flex-shrink: 0;
479
+ height: var(--search-snippet-input-height);
480
+ padding: 0 var(--search-snippet-spacing-lg);
481
+ }
482
+
483
+ .chat-send-button:disabled {
484
+ opacity: 0.6;
485
+ cursor: not-allowed;
486
+ }
487
+
488
+ /* Empty chat state */
489
+ .chat-empty {
490
+ display: flex;
491
+ flex-direction: column;
492
+ align-items: center;
493
+ justify-content: center;
494
+ padding: var(--search-snippet-spacing-xxl);
495
+ gap: var(--search-snippet-spacing-md);
496
+ color: var(--search-snippet-text-secondary);
497
+ text-align: center;
498
+ height: 100%;
499
+ }
500
+
501
+ .chat-empty-icon {
502
+ width: 64px;
503
+ height: 64px;
504
+ opacity: 0.5;
505
+ }
506
+
507
+ .chat-empty-title {
508
+ font-size: var(--search-snippet-font-size-lg);
509
+ font-weight: var(--search-snippet-font-weight-medium);
510
+ color: var(--search-snippet-text-color);
511
+ }
512
+
513
+ .chat-empty-description {
514
+ font-size: var(--search-snippet-font-size-sm);
515
+ }
516
+
517
+ /* Code blocks in messages */
518
+ .chat-message-bubble pre {
519
+ background: var(--search-snippet-surface);
520
+ padding: var(--search-snippet-spacing-sm);
521
+ border-radius: var(--search-snippet-border-radius);
522
+ overflow-x: auto;
523
+ font-family: var(--search-snippet-font-family-mono);
524
+ font-size: var(--search-snippet-font-size-sm);
525
+ margin: var(--search-snippet-spacing-xs) 0;
526
+ }
527
+
528
+ .chat-message-bubble code {
529
+ font-family: var(--search-snippet-font-family-mono);
530
+ font-size: 0.9em;
531
+ background: var(--search-snippet-surface);
532
+ padding: 2px 4px;
533
+ border-radius: var(--search-snippet-border-radius);
534
+ }
535
+
536
+ .chat-message-bubble pre code {
537
+ background: none;
538
+ padding: 0;
539
+ }
540
+
541
+ /* Links in messages */
542
+ .chat-message-bubble a {
543
+ color: var(--search-snippet-primary-color);
544
+ text-decoration: underline;
545
+ }
546
+
547
+ .chat-message-bubble a:hover {
548
+ text-decoration: none;
549
+ }
550
+ `, y = `
551
+ :host {
552
+ /* Colors - Light Mode */
553
+ --search-snippet-primary-color: #2563eb;
554
+ --search-snippet-primary-hover: #0f51dfff;
555
+ --search-snippet-background: #ffffff;
556
+ --search-snippet-surface: #f8f9fa;
557
+ --search-snippet-text-color: #212529;
558
+ --search-snippet-text-secondary: #6c757d;
559
+ --search-snippet-border-color: #dee2e6;
560
+ --search-snippet-hover-background: #f1f3f5;
561
+ --search-snippet-focus-ring: #0066cc40;
562
+ --search-snippet-error-color: #dc3545;
563
+ --search-snippet-error-background: #f8d7da;
564
+ --search-snippet-success-color: #28a745;
565
+ --search-snippet-success-background: #d4edda;
566
+ --search-snippet-warning-color: #ffc107;
567
+ --search-snippet-warning-background: #fff3cd;
568
+
569
+ /* Message Colors */
570
+ --search-snippet-user-message-bg: #0066cc;
571
+ --search-snippet-user-message-text: #ffffff;
572
+ --search-snippet-assistant-message-bg: #f1f3f5;
573
+ --search-snippet-assistant-message-text: #212529;
574
+ --search-snippet-system-message-bg: #fff3cd;
575
+ --search-snippet-system-message-text: #856404;
576
+
577
+ /* Typography */
578
+ --search-snippet-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto',
579
+ 'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji',
580
+ 'Segoe UI Emoji', 'Segoe UI Symbol';
581
+ --search-snippet-font-family-mono: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
582
+ --search-snippet-font-size-base: 14px;
583
+ --search-snippet-font-size-sm: 12px;
584
+ --search-snippet-font-size-lg: 16px;
585
+ --search-snippet-font-size-xl: 18px;
586
+ --search-snippet-line-height: 1.5;
587
+ --search-snippet-font-weight-normal: 400;
588
+ --search-snippet-font-weight-medium: 500;
589
+ --search-snippet-font-weight-bold: 600;
590
+
591
+ /* Spacing */
592
+ --search-snippet-spacing-xs: 4px;
593
+ --search-snippet-spacing-sm: 8px;
594
+ --search-snippet-spacing-md: 12px;
595
+ --search-snippet-spacing-lg: 16px;
596
+ --search-snippet-spacing-xl: 24px;
597
+ --search-snippet-spacing-xxl: 32px;
598
+
599
+ /* Sizing */
600
+ --search-snippet-width: 100%;
601
+ --search-snippet-max-width: 100%;
602
+ --search-snippet-min-width: 320px;
603
+ --search-snippet-max-height: 600px;
604
+ --search-snippet-input-height: 44px;
605
+ --search-snippet-button-height: 36px;
606
+ --search-snippet-icon-size: 20px;
607
+
608
+ /* Border */
609
+ --search-snippet-border-width: 1px;
610
+ --search-snippet-border-radius: 18px;
611
+
612
+ /* Shadows */
613
+ --search-snippet-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
614
+ --search-snippet-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
615
+ --search-snippet-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.15);
616
+ --search-snippet-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.2);
617
+ --search-snippet-shadow-inner: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
618
+
619
+ /* Animation */
620
+ --search-snippet-transition-fast: 150ms ease;
621
+ --search-snippet-transition: 200ms ease;
622
+ --search-snippet-transition-slow: 300ms ease;
623
+ --search-snippet-animation-duration: 0.2s;
624
+
625
+ /* Z-index */
626
+ --search-snippet-z-dropdown: 1000;
627
+ --search-snippet-z-modal: 1050;
628
+ --search-snippet-z-popover: 1060;
629
+ --search-snippet-z-tooltip: 1070;
630
+
631
+ /* Layout */
632
+ display: block;
633
+ width: var(--search-snippet-width);
634
+ max-width: var(--search-snippet-max-width);
635
+ min-width: var(--search-snippet-min-width);
636
+ font-family: var(--search-snippet-font-family);
637
+ font-size: var(--search-snippet-font-size-base);
638
+ line-height: var(--search-snippet-line-height);
639
+ color: var(--search-snippet-text-color);
640
+
641
+
642
+ /* Search */
643
+ --search-snippet-icon-size: 20px;
644
+ --search-snippet-icon-margin-left: 6px;
645
+ --search-snippet-result-item-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
646
+
647
+ /* Chat Bubble */
648
+ --chat-bubble-button-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
649
+ --chat-bubble-window-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
650
+ --chat-bubble-button-size: 60px;
651
+ --chat-bubble-button-radius: 50%;
652
+ --chat-bubble-button-icon-size: 28px;
653
+ --chat-bubble-button-icon-color: white;
654
+ --chat-bubble-button-bottom: 20px;
655
+ --chat-bubble-button-right: 20px;
656
+ --chat-bubble-button-z-index: 9999;
657
+ --chat-bubble-position: fixed;
658
+
659
+
660
+ }
661
+
662
+ :host(:not([theme="dark"])) {
663
+ /* Colors - Light Mode */
664
+ --search-snippet-primary-color: #2563eb;
665
+ --search-snippet-primary-hover: #0f51dfff;
666
+ --search-snippet-background: #ffffff;
667
+ --search-snippet-surface: #f8f9fa;
668
+ --search-snippet-text-color: #212529;
669
+ --search-snippet-text-secondary: #6c757d;
670
+ --search-snippet-border-color: #dee2e6;
671
+ --search-snippet-hover-background: #f1f3f5;
672
+ --search-snippet-focus-ring: #0066cc40;
673
+ --search-snippet-error-color: #dc3545;
674
+ --search-snippet-error-background: #f8d7da;
675
+ --search-snippet-success-color: #28a745;
676
+ --search-snippet-success-background: #d4edda;
677
+ --search-snippet-warning-color: #ffc107;
678
+ --search-snippet-warning-background: #fff3cd;
679
+
680
+ /* Message Colors */
681
+ --search-snippet-user-message-bg: #0066cc;
682
+ --search-snippet-user-message-text: #ffffff;
683
+ --search-snippet-assistant-message-bg: #f1f3f5;
684
+ --search-snippet-assistant-message-text: #212529;
685
+ --search-snippet-system-message-bg: #fff3cd;
686
+ --search-snippet-system-message-text: #856404;
687
+ }
688
+
689
+ /* Dark Mode */
690
+ @media (prefers-color-scheme: dark) {
691
+ :host(:not([theme="light"])) {
692
+ --search-snippet-primary-color: #2563eb;
693
+ --search-snippet-primary-hover: #0f51dfff;
694
+ --search-snippet-background: #1a1b1e;
695
+ --search-snippet-surface: #25262b;
696
+ --search-snippet-text-color: #c1c2c5;
697
+ --search-snippet-text-secondary: #909296;
698
+ --search-snippet-border-color: #373a40;
699
+ --search-snippet-hover-background: #2c2e33;
700
+ --search-snippet-focus-ring: #4dabf740;
701
+ --search-snippet-error-color: #ff6b6b;
702
+ --search-snippet-error-background: #3d1f1f;
703
+ --search-snippet-success-color: #51cf66;
704
+ --search-snippet-success-background: #1f3d24;
705
+ --search-snippet-warning-color: #ffd43b;
706
+ --search-snippet-warning-background: #3d3419;
707
+
708
+ --search-snippet-user-message-bg: #4dabf7;
709
+ --search-snippet-user-message-text: #1a1b1e;
710
+ --search-snippet-assistant-message-bg: #2c2e33;
711
+ --search-snippet-assistant-message-text: #c1c2c5;
712
+ --search-snippet-system-message-bg: #3d3419;
713
+ --search-snippet-system-message-text: #ffd43b;
714
+ color-scheme: dark;
715
+ }
716
+ }
717
+
718
+ /* Auto theme support */
719
+ :host([theme="light"]) {
720
+ color-scheme: light;
721
+ }
722
+
723
+
724
+ /* Base reset */
725
+ * {
726
+ box-sizing: border-box;
727
+ margin: 0;
728
+ padding: 0;
729
+ }
730
+
731
+ /* Container */
732
+ .container {
733
+ background: var(--search-snippet-background);
734
+ border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
735
+ border-radius: var(--search-snippet-border-radius);
736
+ box-shadow: var(--search-snippet-shadow);
737
+ overflow: hidden;
738
+ display: flex;
739
+ flex-direction: column;
740
+ }
741
+
742
+ /* Header */
743
+ .header {
744
+ padding: var(--search-snippet-spacing-md);
745
+ border-bottom: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
746
+ background: var(--search-snippet-surface);
747
+ display: flex;
748
+ align-items: center;
749
+ justify-content: space-between;
750
+ gap: var(--search-snippet-spacing-md);
751
+ }
752
+
753
+ .header-title {
754
+ font-size: var(--search-snippet-font-size-lg);
755
+ font-weight: var(--search-snippet-font-weight-bold);
756
+ color: var(--search-snippet-text-color);
757
+ }
758
+
759
+ /* Input */
760
+ .input-wrapper {
761
+ position: relative;
762
+ display: flex;
763
+ align-items: center;
764
+ gap: var(--search-snippet-spacing-sm);
765
+ }
766
+
767
+ .input {
768
+ width: 100%;
769
+ height: var(--search-snippet-input-height);
770
+ padding: var(--search-snippet-spacing-sm) var(--search-snippet-spacing-md);
771
+ font-family: var(--search-snippet-font-family);
772
+ font-size: var(--search-snippet-font-size-base);
773
+ line-height: var(--search-snippet-line-height);
774
+ color: var(--search-snippet-text-color);
775
+ background: var(--search-snippet-background);
776
+ border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
777
+ border-radius: var(--search-snippet-border-radius);
778
+ outline: none;
779
+ transition: var(--search-snippet-transition);
780
+ }
781
+
782
+ .input:focus {
783
+ border-color: var(--search-snippet-primary-color);
784
+ box-shadow: 0 0 0 3px var(--search-snippet-focus-ring);
785
+ }
786
+
787
+ .input::placeholder {
788
+ color: var(--search-snippet-text-secondary);
789
+ }
790
+
791
+ .input:disabled {
792
+ background: var(--search-snippet-surface);
793
+ cursor: not-allowed;
794
+ opacity: 0.6;
795
+ }
796
+
797
+ /* Button */
798
+ .button {
799
+ height: var(--search-snippet-button-height);
800
+ padding: 0 var(--search-snippet-spacing-lg);
801
+ font-family: var(--search-snippet-font-family);
802
+ font-size: var(--search-snippet-font-size-base);
803
+ font-weight: var(--search-snippet-font-weight-medium);
804
+ color: #ffffff;
805
+ background: var(--search-snippet-primary-color);
806
+ border: none;
807
+ border-radius: var(--search-snippet-border-radius);
808
+ cursor: pointer;
809
+ outline: none;
810
+ transition: var(--search-snippet-transition);
811
+ display: inline-flex;
812
+ align-items: center;
813
+ justify-content: center;
814
+ gap: var(--search-snippet-spacing-sm);
815
+ white-space: nowrap;
816
+ }
817
+
818
+ .button:hover:not(:disabled) {
819
+ background: var(--search-snippet-primary-hover);
820
+ }
821
+
822
+ .button:focus-visible {
823
+ box-shadow: 0 0 0 3px var(--search-snippet-focus-ring);
824
+ }
825
+
826
+ .button:disabled {
827
+ opacity: 0.6;
828
+ cursor: not-allowed;
829
+ }
830
+
831
+ .button-secondary {
832
+ background: var(--search-snippet-surface);
833
+ color: var(--search-snippet-text-color);
834
+ border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
835
+ }
836
+
837
+ .button-secondary:hover:not(:disabled) {
838
+ background: var(--search-snippet-hover-background);
839
+ }
840
+
841
+ /* Content area */
842
+ .content {
843
+ flex: 1;
844
+ overflow-y: auto;
845
+ overflow-x: hidden;
846
+ padding: var(--search-snippet-spacing-md);
847
+ }
848
+
849
+ /* Scrollbar styling */
850
+ .content::-webkit-scrollbar {
851
+ width: 8px;
852
+ }
853
+
854
+ .content::-webkit-scrollbar-track {
855
+ background: var(--search-snippet-surface);
856
+ }
857
+
858
+ .content::-webkit-scrollbar-thumb {
859
+ background: var(--search-snippet-border-color);
860
+ border-radius: var(--search-snippet-border-radius);
861
+ }
862
+
863
+ .content::-webkit-scrollbar-thumb:hover {
864
+ background: var(--search-snippet-text-secondary);
865
+ }
866
+
867
+ /* Loading spinner */
868
+ .loading {
869
+ display: inline-block;
870
+ width: var(--search-snippet-icon-size);
871
+ height: var(--search-snippet-icon-size);
872
+ border: 2px solid currentColor;
873
+ border-top-color: transparent;
874
+ border-radius: 50%;
875
+ animation: spin 0.6s linear infinite;
876
+ }
877
+
878
+ @keyframes spin {
879
+ to { transform: rotate(360deg); }
880
+ }
881
+
882
+ /* Error message */
883
+ .error {
884
+ padding: var(--search-snippet-spacing-md);
885
+ color: var(--search-snippet-error-color);
886
+ background: var(--search-snippet-error-background);
887
+ border-radius: var(--search-snippet-border-radius);
888
+ font-size: var(--search-snippet-font-size-sm);
889
+ }
890
+
891
+ /* Empty state */
892
+ .empty {
893
+ padding: var(--search-snippet-spacing-xl);
894
+ text-align: center;
895
+ color: var(--search-snippet-text-secondary);
896
+ }
897
+
898
+ /* Accessibility */
899
+ .sr-only {
900
+ position: absolute;
901
+ width: 1px;
902
+ height: 1px;
903
+ padding: 0;
904
+ margin: -1px;
905
+ overflow: hidden;
906
+ clip: rect(0, 0, 0, 0);
907
+ white-space: nowrap;
908
+ border-width: 0;
909
+ }
910
+
911
+ /* Focus visible polyfill */
912
+ .focus-visible:focus {
913
+ outline: 2px solid var(--search-snippet-primary-color);
914
+ outline-offset: 2px;
915
+ }
916
+
917
+ /* Powered by branding - block style (for sidebars) */
918
+ .powered-by {
919
+ display: flex;
920
+ align-items: center;
921
+ justify-content: center;
922
+ gap: var(--search-snippet-spacing-xs);
923
+ padding: var(--search-snippet-spacing-sm) var(--search-snippet-spacing-md);
924
+ font-size: var(--search-snippet-font-size-sm);
925
+ color: var(--search-snippet-text-secondary);
926
+ background: var(--search-snippet-surface);
927
+ border-top: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
928
+ margin-top: auto;
929
+ flex-shrink: 0;
930
+ }
931
+
932
+ .powered-by svg {
933
+ width: 16px;
934
+ height: 16px;
935
+ flex-shrink: 0;
936
+ }
937
+
938
+ .powered-by a,
939
+ .powered-by-inline a {
940
+ color: var(--search-snippet-text-secondary);
941
+ text-decoration: none;
942
+ transition: color var(--search-snippet-transition-fast);
943
+ display: inline-flex;
944
+ align-items: center;
945
+ gap: 4px;
946
+ }
947
+
948
+ .powered-by a:hover,
949
+ .powered-by-inline a:hover {
950
+ color: var(--search-snippet-primary-color);
951
+ }
952
+
953
+ /* Powered by branding - inline style (for headers/subtle placement) */
954
+ .powered-by-inline {
955
+ font-size: var(--search-snippet-font-size-sm);
956
+ color: var(--search-snippet-text-secondary);
957
+ padding: var(--search-snippet-spacing-xs) 0;
958
+ text-align: center;
959
+ }
960
+ `, j = `<svg width="32" height="10" viewBox="0 0 412 186" xmlns="http://www.w3.org/2000/svg" aria-label="Cloudflare" role="img">
961
+ <path fill="#f38020" d="m280.8395,183.31456c11,-26 -4,-38 -19,-38l-148,-2c-4,0 -4,-6 1,-7l150,-2c17,-1 37,-15 43,-33c0,0 10,-21 9,-24a97,97 0 0 0 -187,-11c-38,-25 -78,9 -69,46c-48,3 -65,46 -60,72c0,1 1,2 3,2l274,0c1,0 3,-1 3,-3z"/>
962
+ <path fill="#faae40" d="m330.8395,81.31456c-4,0 -6,-1 -7,1l-5,21c-5,16 3,30 20,31l32,2c4,0 4,6 -1,7l-33,1c-36,4 -46,39 -46,39c0,2 0,3 2,3l113,0l3,-2a81,81 0 0 0 -78,-103"/>
963
+ </svg>`, K = "https://search.ai.cloudflare.com", x = `Powered by <a href="${K}" target="_blank" rel="noopener noreferrer">Cloudflare AI Search ${j}</a>`;
964
+ function V(o) {
965
+ let i = o;
966
+ i = _(i), i = i.replace(/```([\s\S]*?)```/g, (n, c) => `<pre><code>${c.trim()}</code></pre>`);
967
+ const e = i.split(`
968
+ `), t = [];
969
+ let s = !1, a = "";
970
+ for (let n = 0; n < e.length; n++) {
971
+ const c = e[n], h = c.match(/^(#{1,6})\s+(.+)$/);
972
+ if (h) {
973
+ const p = h[1].length, k = h[2];
974
+ t.push(`<h${p}>${v(k)}</h${p}>`);
975
+ continue;
976
+ }
977
+ if (c.match(/^---+$/)) {
978
+ t.push("<hr />");
979
+ continue;
980
+ }
981
+ if (c.match(/^>\s+/)) {
982
+ const p = c.replace(/^>\s+/, "");
983
+ t.push(`<blockquote>${v(p)}</blockquote>`);
984
+ continue;
985
+ }
986
+ const d = c.match(/^[-*]\s+(.+)$/);
987
+ if (d) {
988
+ (!s || a !== "ul") && (s && t.push(`</${a}>`), t.push("<ul>"), s = !0, a = "ul"), t.push(`<li>${v(d[1])}</li>`);
989
+ continue;
990
+ }
991
+ const g = c.match(/^\d+\.\s+(.+)$/);
992
+ if (g) {
993
+ (!s || a !== "ol") && (s && t.push(`</${a}>`), t.push("<ol>"), s = !0, a = "ol"), t.push(`<li>${v(g[1])}</li>`);
994
+ continue;
995
+ }
996
+ if (s && (t.push(`</${a}>`), s = !1, a = ""), c.trim() === "") {
997
+ t.push("<br />");
998
+ continue;
999
+ }
1000
+ t.push(`<p>${v(c)}</p>`);
1001
+ }
1002
+ return s && t.push(`</${a}>`), t.join(`
1003
+ `);
1004
+ }
1005
+ function v(o) {
1006
+ let i = o;
1007
+ return i = i.replace(/`([^`]+)`/g, "<code>$1</code>"), i = i.replace(/\*\*\*(.+?)\*\*\*/g, "<strong><em>$1</em></strong>"), i = i.replace(/___(.+?)___/g, "<strong><em>$1</em></strong>"), i = i.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>"), i = i.replace(/__(.+?)__/g, "<strong>$1</strong>"), i = i.replace(/\*(.+?)\*/g, "<em>$1</em>"), i = i.replace(/_(.+?)_/g, "<em>$1</em>"), i = i.replace(
1008
+ /\[([^\]]+)\]\(([^)]+)\)/g,
1009
+ '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'
1010
+ ), i;
1011
+ }
1012
+ function _(o) {
1013
+ const i = {
1014
+ "&": "&amp;",
1015
+ "<": "&lt;",
1016
+ ">": "&gt;",
1017
+ '"': "&quot;",
1018
+ "'": "&#39;"
1019
+ };
1020
+ return o.replace(/[&<>"']/g, (e) => i[e] || e);
1021
+ }
1022
+ class q {
1023
+ constructor(i, e, t) {
1024
+ r(this, "container");
1025
+ r(this, "client");
1026
+ r(this, "props");
1027
+ r(this, "inputElement", null);
1028
+ r(this, "messagesContainer", null);
1029
+ r(this, "sendButton", null);
1030
+ r(this, "messages", []);
1031
+ r(this, "isStreaming", !1);
1032
+ r(this, "currentStreamingMessageId", null);
1033
+ // Event handler references for cleanup
1034
+ r(this, "handleInputResize", null);
1035
+ r(this, "handleInputKeydown", null);
1036
+ r(this, "handleSendClick", null);
1037
+ this.container = i, this.client = e, this.props = t, this.render(), this.attachEventListeners();
1038
+ }
1039
+ /**
1040
+ * Render the chat interface
1041
+ */
1042
+ render() {
1043
+ this.container.innerHTML = `
1044
+ <div class="chat-container">
1045
+ <div class="chat-messages">
1046
+ <div class="chat-empty">
1047
+ <svg class="chat-empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1048
+ <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>
1049
+ </svg>
1050
+ <div class="chat-empty-title">Start a Conversation</div>
1051
+ <div class="chat-empty-description">
1052
+ Send a message to begin chatting
1053
+ </div>
1054
+ </div>
1055
+ </div>
1056
+ <div class="chat-input-area">
1057
+ <div class="chat-input-wrapper">
1058
+ <textarea
1059
+ class="chat-input"
1060
+ placeholder="${l(this.props.placeholder || "Type a message...")}"
1061
+ aria-label="Chat message input"
1062
+ style="height: 40px;"
1063
+ rows="1"
1064
+ ></textarea>
1065
+ <button class="button chat-send-button" aria-label="Send message">
1066
+ <span>Send</span>
1067
+ </button>
1068
+ </div>
1069
+ </div>
1070
+ </div>
1071
+ `, this.messagesContainer = this.container.querySelector(".chat-messages"), this.inputElement = this.container.querySelector(".chat-input"), this.sendButton = this.container.querySelector(".chat-send-button");
1072
+ }
1073
+ /**
1074
+ * Attach event listeners
1075
+ */
1076
+ attachEventListeners() {
1077
+ !this.inputElement || !this.sendButton || (this.handleInputResize = (i) => {
1078
+ const e = i.target;
1079
+ e.style.height = "auto", e.style.height = `${e.scrollHeight}px`;
1080
+ }, this.inputElement.addEventListener("input", this.handleInputResize), this.handleInputKeydown = (i) => {
1081
+ i.key === "Enter" && !i.shiftKey && (i.preventDefault(), this.handleSendMessage());
1082
+ }, this.inputElement.addEventListener("keydown", this.handleInputKeydown), this.handleSendClick = () => {
1083
+ this.handleSendMessage();
1084
+ }, this.sendButton.addEventListener("click", this.handleSendClick));
1085
+ }
1086
+ /**
1087
+ * Handle send message
1088
+ */
1089
+ async handleSendMessage() {
1090
+ if (!this.inputElement || this.isStreaming) return;
1091
+ const i = this.inputElement.value.trim();
1092
+ i.length !== 0 && (this.inputElement.value = "", this.inputElement.style.height = "auto", await this.sendMessage(i));
1093
+ }
1094
+ /**
1095
+ * Send a message
1096
+ */
1097
+ async sendMessage(i) {
1098
+ const e = {
1099
+ id: S("msg"),
1100
+ role: "user",
1101
+ content: i,
1102
+ timestamp: Date.now()
1103
+ };
1104
+ this.addMessage(e), this.renderMessages(!0), this.setStreamingState(!0);
1105
+ const t = S("msg"), s = {
1106
+ id: t,
1107
+ role: "assistant",
1108
+ content: "",
1109
+ timestamp: Date.now()
1110
+ };
1111
+ this.addMessage(s), this.currentStreamingMessageId = t, this.renderMessages(!0);
1112
+ try {
1113
+ const a = this.client.chat(i);
1114
+ let n = "";
1115
+ for await (const h of a)
1116
+ if (h.type === "text" && h.message)
1117
+ n += h.message, this.updateStreamingMessage(t, n);
1118
+ else if (h.type === "error") {
1119
+ this.showErrorInMessage(t, h.message || "Unknown error");
1120
+ break;
1121
+ }
1122
+ const c = this.messages.findIndex((h) => h.id === t);
1123
+ c !== -1 && (this.messages[c].content = n), this.container.dispatchEvent(m("message", { message: s }));
1124
+ } catch (a) {
1125
+ this.showErrorInMessage(t, a.message), this.container.dispatchEvent(
1126
+ m("error", {
1127
+ error: {
1128
+ message: a.message,
1129
+ code: "CHAT_ERROR"
1130
+ }
1131
+ })
1132
+ );
1133
+ } finally {
1134
+ this.setStreamingState(!1), this.renderMessages(), this.currentStreamingMessageId = null;
1135
+ }
1136
+ }
1137
+ /**
1138
+ * Add a message to the chat
1139
+ */
1140
+ addMessage(i) {
1141
+ this.messages.push(i), this.renderMessages();
1142
+ }
1143
+ /**
1144
+ * Update streaming message content
1145
+ */
1146
+ updateStreamingMessage(i, e) {
1147
+ const t = this.messages.findIndex((s) => s.id === i);
1148
+ t !== -1 && (this.messages[t].content = e, this.renderMessages(!0));
1149
+ }
1150
+ /**
1151
+ * Show error in message
1152
+ */
1153
+ showErrorInMessage(i, e) {
1154
+ const t = this.messages.findIndex((s) => s.id === i);
1155
+ t !== -1 && (this.messages[t].content = `Error: ${e}`, this.renderMessages());
1156
+ }
1157
+ /**
1158
+ * Render all messages
1159
+ */
1160
+ renderMessages(i = !1) {
1161
+ if (!this.messagesContainer) return;
1162
+ if (this.messages.length === 0) {
1163
+ this.messagesContainer.innerHTML = `
1164
+ <div class="chat-empty">
1165
+ <svg class="chat-empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1166
+ <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>
1167
+ </svg>
1168
+ <div class="chat-empty-title">Start a Conversation</div>
1169
+ <div class="chat-empty-description">
1170
+ Send a message to begin chatting
1171
+ </div>
1172
+ </div>
1173
+ `;
1174
+ return;
1175
+ }
1176
+ const e = this.messages.map(
1177
+ (t) => this.renderMessage(t, i && t.id === this.currentStreamingMessageId)
1178
+ ).join("");
1179
+ this.messagesContainer.innerHTML = e, this.scrollToBottom();
1180
+ }
1181
+ /**
1182
+ * Render a single message
1183
+ */
1184
+ renderMessage(i, e = !1) {
1185
+ const t = `chat-message-${i.role}`, s = i.role === "user" ? "U" : "AI";
1186
+ return `
1187
+ <div class="chat-message ${t}">
1188
+ <div class="chat-message-avatar">${s}</div>
1189
+ <div class="chat-message-content">
1190
+ <div class="chat-message-bubble">
1191
+ ${i.content ? `<div class="chat-message-text">${V(i.content)}</div>` : ""}
1192
+ ${e ? '<div class="chat-streaming"><span class="chat-streaming-dot"></span><span class="chat-streaming-dot"></span><span class="chat-streaming-dot"></span></div>' : ""}
1193
+ </div>
1194
+ <div class="chat-message-metadata">
1195
+ <span class="chat-message-time">${H(i.timestamp)}</span>
1196
+ </div>
1197
+ </div>
1198
+ </div>
1199
+ `;
1200
+ }
1201
+ /**
1202
+ * Scroll to bottom of messages
1203
+ */
1204
+ scrollToBottom() {
1205
+ this.messagesContainer && requestAnimationFrame(() => {
1206
+ this.messagesContainer && (this.messagesContainer.scrollTop = this.messagesContainer.scrollHeight);
1207
+ });
1208
+ }
1209
+ /**
1210
+ * Set streaming state
1211
+ */
1212
+ setStreamingState(i) {
1213
+ this.isStreaming = i, this.inputElement && (this.inputElement.disabled = i), this.sendButton && (this.sendButton.disabled = i, this.sendButton.innerHTML = i ? '<div class="loading"></div>' : "<span>Send</span>");
1214
+ }
1215
+ /**
1216
+ * Get all messages
1217
+ */
1218
+ getMessages() {
1219
+ return [...this.messages];
1220
+ }
1221
+ /**
1222
+ * Clear all messages
1223
+ */
1224
+ clearMessages() {
1225
+ this.messages = [], this.renderMessages();
1226
+ }
1227
+ /**
1228
+ * Set messages (for restoring history)
1229
+ */
1230
+ setMessages(i) {
1231
+ this.messages = [...i], this.renderMessages();
1232
+ }
1233
+ /**
1234
+ * Destroy and cleanup
1235
+ */
1236
+ destroy() {
1237
+ this.isStreaming && this.client.cancelAllRequests(), this.inputElement && (this.handleInputResize && this.inputElement.removeEventListener("input", this.handleInputResize), this.handleInputKeydown && this.inputElement.removeEventListener("keydown", this.handleInputKeydown)), this.sendButton && this.handleSendClick && this.sendButton.removeEventListener("click", this.handleSendClick), this.handleInputResize = null, this.handleInputKeydown = null, this.handleSendClick = null;
1238
+ }
1239
+ }
1240
+ const E = "chat-bubble-snippet";
1241
+ class O extends HTMLElement {
1242
+ constructor() {
1243
+ super();
1244
+ r(this, "shadow");
1245
+ r(this, "client", null);
1246
+ r(this, "chatView", null);
1247
+ r(this, "container", null);
1248
+ r(this, "isExpanded", !1);
1249
+ r(this, "isMinimized", !1);
1250
+ // Event handler references for cleanup
1251
+ r(this, "handleBubbleClick", null);
1252
+ r(this, "handleCloseClick", null);
1253
+ r(this, "handleMinimizeClick", null);
1254
+ r(this, "handleClearClick", null);
1255
+ this.shadow = this.attachShadow({ mode: "open" });
1256
+ }
1257
+ static get observedAttributes() {
1258
+ return ["api-url", "placeholder", "theme", "hide-branding"];
1259
+ }
1260
+ connectedCallback() {
1261
+ this.render(), this.initializeClient(), this.dispatchEvent(m("ready", void 0));
1262
+ }
1263
+ disconnectedCallback() {
1264
+ this.cleanup();
1265
+ }
1266
+ attributeChangedCallback(e, t, s) {
1267
+ t !== s && (e === "api-url" ? this.initializeClient() : e === "theme" && this.updateTheme(s));
1268
+ }
1269
+ getProps() {
1270
+ return {
1271
+ apiUrl: u(this.getAttribute("api-url"), "http://localhost:3000"),
1272
+ placeholder: u(this.getAttribute("placeholder"), "Type a message..."),
1273
+ theme: u(this.getAttribute("theme"), "auto"),
1274
+ hideBranding: f(this.getAttribute("hide-branding"), !1)
1275
+ };
1276
+ }
1277
+ initializeClient() {
1278
+ const e = this.getProps();
1279
+ if (!e.apiUrl) {
1280
+ console.error("ChatBubbleSnippet: api-url attribute is required");
1281
+ return;
1282
+ }
1283
+ try {
1284
+ this.client = w(e.apiUrl);
1285
+ } catch (t) {
1286
+ console.error("ChatBubbleSnippet:", t);
1287
+ }
1288
+ }
1289
+ render() {
1290
+ const e = document.createElement("style");
1291
+ e.textContent = `${y}
1292
+ ${$}
1293
+ ${this.getBubbleStyles()}`, this.container = document.createElement("div"), this.container.className = "chat-bubble-widget", this.container.innerHTML = this.getBaseHTML(), this.shadow.innerHTML = "", this.shadow.appendChild(e), this.shadow.appendChild(this.container), this.attachEventListeners();
1294
+ }
1295
+ getBubbleStyles() {
1296
+ return `
1297
+ .chat-bubble-widget {
1298
+ position: var(--chat-bubble-position);
1299
+ bottom: var(--chat-bubble-button-bottom);
1300
+ right: var(--chat-bubble-button-right);
1301
+ z-index: var(--chat-bubble-button-z-index);
1302
+ font-family: var(--search-snippet-font-family);
1303
+ font-size: var(--search-snippet-font-size-base);
1304
+ }
1305
+
1306
+ .bubble-button {
1307
+ width: var(--chat-bubble-button-size);
1308
+ height: var(--chat-bubble-button-size);
1309
+ border-radius: var(--chat-bubble-button-radius);
1310
+ background: var(--search-snippet-primary-color);
1311
+ border: none;
1312
+ cursor: pointer;
1313
+ box-shadow: var(--chat-bubble-button-shadow);
1314
+ display: flex;
1315
+ align-items: center;
1316
+ justify-content: center;
1317
+ transition: all 0.3s ease;
1318
+ position: relative;
1319
+ }
1320
+
1321
+ .bubble-button:hover {
1322
+ background: var(--search-snippet-primary-hover);
1323
+ transform: scale(1.05);
1324
+ }
1325
+
1326
+ .bubble-button svg {
1327
+ width: var(--chat-bubble-button-icon-size);
1328
+ height: var(--chat-bubble-button-icon-size);
1329
+ color: var(--chat-bubble-button-icon-color);
1330
+ }
1331
+
1332
+ .bubble-button.hidden {
1333
+ opacity: 0;
1334
+ pointer-events: none;
1335
+ transform: scale(0);
1336
+ }
1337
+
1338
+ .chat-window {
1339
+ position: absolute;
1340
+ bottom: 0;
1341
+ right: 0;
1342
+ width: 380px;
1343
+ height: 500px;
1344
+ background: var(--search-snippet-background);
1345
+ border-radius: var(--search-snippet-border-radius);
1346
+ box-shadow: var(--chat-bubble-window-shadow);
1347
+ display: flex;
1348
+ flex-direction: column;
1349
+ opacity: 0;
1350
+ transform: scale(0.8) translateY(20px);
1351
+ transform-origin: bottom right;
1352
+ transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
1353
+ pointer-events: none;
1354
+ overflow: hidden;
1355
+ border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
1356
+ }
1357
+
1358
+ .chat-window.expanded {
1359
+ opacity: 1;
1360
+ transform: scale(1) translateY(0);
1361
+ pointer-events: auto;
1362
+ }
1363
+
1364
+ .chat-window.minimized {
1365
+ height: 58px;
1366
+ overflow: hidden;
1367
+ }
1368
+
1369
+ .chat-header {
1370
+ background: var(--search-snippet-surface);
1371
+ padding: var(--search-snippet-spacing-md);
1372
+ border-bottom: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
1373
+ display: flex;
1374
+ align-items: center;
1375
+ justify-content: space-between;
1376
+ flex-shrink: 0;
1377
+ }
1378
+
1379
+ .chat-header-title {
1380
+ font-weight: var(--search-snippet-font-weight-bold);
1381
+ color: var(--search-snippet-text-color);
1382
+ display: flex;
1383
+ align-items: center;
1384
+ gap: var(--search-snippet-spacing-sm);
1385
+ font-size: var(--search-snippet-font-size-lg);
1386
+ }
1387
+
1388
+ .chat-header-title svg {
1389
+ width: 20px;
1390
+ height: 20px;
1391
+ }
1392
+
1393
+ .chat-header-actions {
1394
+ display: flex;
1395
+ gap: var(--search-snippet-spacing-xs);
1396
+ }
1397
+
1398
+ .icon-button {
1399
+ width: 32px;
1400
+ height: 32px;
1401
+ border: none;
1402
+ background: transparent;
1403
+ border-radius: var(--search-snippet-border-radius);
1404
+ cursor: pointer;
1405
+ display: flex;
1406
+ align-items: center;
1407
+ justify-content: center;
1408
+ transition: background var(--search-snippet-transition-fast);
1409
+ color: var(--search-snippet-text-color);
1410
+ }
1411
+
1412
+ .icon-button:hover {
1413
+ background: var(--search-snippet-hover-background);
1414
+ }
1415
+
1416
+ .icon-button svg {
1417
+ width: 18px;
1418
+ height: 18px;
1419
+ }
1420
+
1421
+ .chat-content {
1422
+ flex: 1;
1423
+ overflow: hidden;
1424
+ display: flex;
1425
+ flex-direction: column;
1426
+ }
1427
+
1428
+ @media (max-width: 480px) {
1429
+ .chat-window {
1430
+ width: calc(100vw - 40px);
1431
+ max-width: 400px;
1432
+ }
1433
+ }
1434
+ `;
1435
+ }
1436
+ getBaseHTML() {
1437
+ return `
1438
+ <button class="bubble-button" aria-label="Open chat">
1439
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1440
+ <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>
1441
+ </svg>
1442
+ </button>
1443
+ <div class="chat-window">
1444
+ <div class="chat-header">
1445
+ <div class="chat-header-title">
1446
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1447
+ <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>
1448
+ </svg>
1449
+ <span>Chat</span>
1450
+ </div>
1451
+ <div class="chat-header-actions">
1452
+ <button class="icon-button clear-button" aria-label="Clear history">
1453
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1454
+ <polyline points="3 6 5 6 21 6"></polyline>
1455
+ <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
1456
+ </svg>
1457
+ </button>
1458
+ <button class="icon-button minimize-button" aria-label="Minimize">
1459
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1460
+ <line x1="5" y1="12" x2="19" y2="12"></line>
1461
+ </svg>
1462
+ </button>
1463
+ <button class="icon-button close-button" aria-label="Close">
1464
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1465
+ <line x1="18" y1="6" x2="6" y2="18"></line>
1466
+ <line x1="6" y1="6" x2="18" y2="18"></line>
1467
+ </svg>
1468
+ </button>
1469
+ </div>
1470
+ </div>
1471
+ <div class="chat-content"></div>
1472
+ ${this.getProps().hideBranding ? "" : `<div class="powered-by">${x}</div>`}
1473
+ </div>
1474
+ `;
1475
+ }
1476
+ attachEventListeners() {
1477
+ const e = this.shadow.querySelector(".bubble-button"), t = this.shadow.querySelector(".close-button"), s = this.shadow.querySelector(".minimize-button"), a = this.shadow.querySelector(".clear-button");
1478
+ this.handleBubbleClick = () => this.toggleChat(), this.handleCloseClick = () => this.closeChat(), this.handleMinimizeClick = () => this.toggleMinimize(), this.handleClearClick = () => this.clearChat(), e?.addEventListener("click", this.handleBubbleClick), t?.addEventListener("click", this.handleCloseClick), s?.addEventListener("click", this.handleMinimizeClick), a?.addEventListener("click", this.handleClearClick);
1479
+ }
1480
+ removeEventListeners() {
1481
+ const e = this.shadow.querySelector(".bubble-button"), t = this.shadow.querySelector(".close-button"), s = this.shadow.querySelector(".minimize-button"), a = this.shadow.querySelector(".clear-button");
1482
+ this.handleBubbleClick && e?.removeEventListener("click", this.handleBubbleClick), this.handleCloseClick && t?.removeEventListener("click", this.handleCloseClick), this.handleMinimizeClick && s?.removeEventListener("click", this.handleMinimizeClick), this.handleClearClick && a?.removeEventListener("click", this.handleClearClick), this.handleBubbleClick = null, this.handleCloseClick = null, this.handleMinimizeClick = null, this.handleClearClick = null;
1483
+ }
1484
+ toggleChat() {
1485
+ this.isExpanded = !this.isExpanded;
1486
+ const e = this.shadow.querySelector(".bubble-button"), t = this.shadow.querySelector(".chat-window");
1487
+ this.isExpanded ? (e?.classList.add("hidden"), t?.classList.add("expanded"), this.initializeChatView()) : (e?.classList.remove("hidden"), t?.classList.remove("expanded"));
1488
+ }
1489
+ closeChat() {
1490
+ this.isExpanded = !1, this.isMinimized = !1;
1491
+ const e = this.shadow.querySelector(".bubble-button"), t = this.shadow.querySelector(".chat-window");
1492
+ e?.classList.remove("hidden"), t?.classList.remove("expanded", "minimized");
1493
+ }
1494
+ toggleMinimize() {
1495
+ this.isMinimized = !this.isMinimized;
1496
+ const e = this.shadow.querySelector(".chat-window");
1497
+ this.isMinimized ? e?.classList.add("minimized") : e?.classList.remove("minimized");
1498
+ }
1499
+ initializeChatView() {
1500
+ if (this.chatView || !this.client) return;
1501
+ const e = this.shadow.querySelector(".chat-content");
1502
+ if (!e) return;
1503
+ const t = this.getProps();
1504
+ this.chatView = new q(e, this.client, t);
1505
+ }
1506
+ updateTheme(e) {
1507
+ (e === "light" || e === "dark" ? e : null) === null && this.hasAttribute("theme") && this.getAttribute("theme") !== "auto" && this.removeAttribute("theme");
1508
+ }
1509
+ cleanup() {
1510
+ this.removeEventListeners(), this.client && this.client.cancelAllRequests(), this.chatView && this.chatView.destroy();
1511
+ }
1512
+ // Public API
1513
+ clearChat() {
1514
+ this.chatView?.clearMessages();
1515
+ }
1516
+ async sendMessage(e) {
1517
+ this.chatView && await this.chatView.sendMessage(e);
1518
+ }
1519
+ getMessages() {
1520
+ return this.chatView?.getMessages() || [];
1521
+ }
1522
+ }
1523
+ customElements.get(E) || customElements.define(E, O);
1524
+ const M = "chat-page-snippet", L = "chat-page-sessions";
1525
+ class D extends HTMLElement {
1526
+ constructor() {
1527
+ super();
1528
+ r(this, "shadow");
1529
+ r(this, "client", null);
1530
+ r(this, "chatView", null);
1531
+ r(this, "container", null);
1532
+ r(this, "sessions", []);
1533
+ r(this, "currentSessionId", null);
1534
+ r(this, "sidebarCollapsed", !1);
1535
+ // Event handler references for cleanup
1536
+ r(this, "handleClearClick", null);
1537
+ r(this, "handleNewChatClick", null);
1538
+ r(this, "handleToggleSidebarClick", null);
1539
+ r(this, "handleChatListClick", null);
1540
+ r(this, "handleMessageEvent", null);
1541
+ this.shadow = this.attachShadow({ mode: "open" }), this.loadSessions();
1542
+ }
1543
+ static get observedAttributes() {
1544
+ return ["api-url", "placeholder", "theme", "hide-branding"];
1545
+ }
1546
+ connectedCallback() {
1547
+ this.render(), this.initializeClient(), this.setupView(), this.dispatchEvent(m("ready", void 0));
1548
+ }
1549
+ disconnectedCallback() {
1550
+ this.saveCurrentSession(), this.cleanup();
1551
+ }
1552
+ attributeChangedCallback(e, t, s) {
1553
+ t !== s && (e === "api-url" ? (this.initializeClient(), this.setupView()) : e === "theme" && this.updateTheme(s));
1554
+ }
1555
+ getProps() {
1556
+ return {
1557
+ apiUrl: u(this.getAttribute("api-url"), "http://localhost:3000"),
1558
+ placeholder: u(this.getAttribute("placeholder"), "Type a message..."),
1559
+ theme: u(this.getAttribute("theme"), "auto"),
1560
+ hideBranding: f(this.getAttribute("hide-branding"), !1)
1561
+ };
1562
+ }
1563
+ initializeClient() {
1564
+ const e = this.getProps();
1565
+ if (!e.apiUrl) {
1566
+ console.error("ChatPageSnippet: api-url attribute is required");
1567
+ return;
1568
+ }
1569
+ try {
1570
+ this.client = w(e.apiUrl);
1571
+ } catch (t) {
1572
+ console.error("ChatPageSnippet:", t);
1573
+ }
1574
+ }
1575
+ render() {
1576
+ const e = document.createElement("style");
1577
+ e.textContent = `${y}
1578
+ ${$}
1579
+ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.container.className = "chat-page-container", this.container.innerHTML = this.getBaseHTML(), this.shadow.innerHTML = "", this.shadow.appendChild(e), this.shadow.appendChild(this.container), this.attachEventListeners();
1580
+ }
1581
+ getPageStyles() {
1582
+ return `
1583
+ :host {
1584
+ display: block;
1585
+ width: 100%;
1586
+ height: 100vh;
1587
+ }
1588
+
1589
+ .chat-page-container {
1590
+ display: flex;
1591
+ height: 100%;
1592
+ background: var(--search-snippet-background);
1593
+ }
1594
+
1595
+ /* Sidebar styles */
1596
+ .chat-sidebar {
1597
+ width: 280px;
1598
+ min-width: 280px;
1599
+ background: var(--search-snippet-surface);
1600
+ border-right: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
1601
+ display: flex;
1602
+ flex-direction: column;
1603
+ transition: var(--search-snippet-transition);
1604
+ overflow: hidden;
1605
+ }
1606
+
1607
+ .chat-sidebar.collapsed {
1608
+ width: 0;
1609
+ min-width: 0;
1610
+ border-right: none;
1611
+ }
1612
+
1613
+ .sidebar-header {
1614
+ padding: var(--search-snippet-spacing-lg);
1615
+ border-bottom: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
1616
+ display: flex;
1617
+ align-items: center;
1618
+ justify-content: space-between;
1619
+ flex-shrink: 0;
1620
+ height: 69px;
1621
+ }
1622
+
1623
+ .sidebar-title {
1624
+ font-size: var(--search-snippet-font-size-lg);
1625
+ font-weight: var(--search-snippet-font-weight-bold);
1626
+ color: var(--search-snippet-text-color);
1627
+ }
1628
+
1629
+ .new-chat-button {
1630
+ width: 100%;
1631
+ height: var(--search-snippet-button-height);
1632
+ margin: var(--search-snippet-spacing-md) var(--search-snippet-spacing-lg);
1633
+ padding: 0 var(--search-snippet-spacing-lg);
1634
+ font-family: var(--search-snippet-font-family);
1635
+ font-size: var(--search-snippet-font-size-base);
1636
+ font-weight: var(--search-snippet-font-weight-medium);
1637
+ color: #fff;
1638
+ background: var(--search-snippet-primary-color);
1639
+ border: none;
1640
+ border-radius: var(--search-snippet-border-radius);
1641
+ cursor: pointer;
1642
+ outline: none;
1643
+ transition: var(--search-snippet-transition);
1644
+ display: inline-flex;
1645
+ align-items: center;
1646
+ justify-content: center;
1647
+ gap: var(--search-snippet-spacing-sm);
1648
+ box-sizing: border-box;
1649
+ width: calc(100% - var(--search-snippet-spacing-lg) * 2);
1650
+ }
1651
+
1652
+ .new-chat-button:hover {
1653
+ opacity: 0.9;
1654
+ }
1655
+
1656
+ .new-chat-button svg {
1657
+ width: 16px;
1658
+ height: 16px;
1659
+ }
1660
+
1661
+ .chat-list {
1662
+ flex: 1;
1663
+ overflow-y: auto;
1664
+ padding: var(--search-snippet-spacing-sm);
1665
+ }
1666
+
1667
+ .chat-list-item {
1668
+ display: flex;
1669
+ align-items: center;
1670
+ padding: var(--search-snippet-spacing-md) var(--search-snippet-spacing-lg);
1671
+ margin-bottom: var(--search-snippet-spacing-xs);
1672
+ border-radius: var(--search-snippet-border-radius);
1673
+ cursor: pointer;
1674
+ transition: var(--search-snippet-transition);
1675
+ gap: var(--search-snippet-spacing-sm);
1676
+ }
1677
+
1678
+ .chat-list-item:hover {
1679
+ background: var(--search-snippet-hover-background);
1680
+ }
1681
+
1682
+ .chat-list-item.active {
1683
+ background: var(--search-snippet-hover-background);
1684
+ border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
1685
+ }
1686
+
1687
+ .chat-list-item-content {
1688
+ flex: 1;
1689
+ min-width: 0;
1690
+ }
1691
+
1692
+ .chat-list-item-title {
1693
+ font-size: var(--search-snippet-font-size-base);
1694
+ font-weight: var(--search-snippet-font-weight-medium);
1695
+ color: var(--search-snippet-text-color);
1696
+ white-space: nowrap;
1697
+ overflow: hidden;
1698
+ text-overflow: ellipsis;
1699
+ }
1700
+
1701
+ .chat-list-item-date {
1702
+ font-size: var(--search-snippet-font-size-sm);
1703
+ color: var(--search-snippet-text-secondary);
1704
+ margin-top: 2px;
1705
+ }
1706
+
1707
+ .chat-list-item-delete {
1708
+ opacity: 0;
1709
+ background: none;
1710
+ border: none;
1711
+ padding: var(--search-snippet-spacing-xs);
1712
+ cursor: pointer;
1713
+ color: var(--search-snippet-text-secondary);
1714
+ border-radius: var(--search-snippet-border-radius-sm);
1715
+ transition: var(--search-snippet-transition);
1716
+ display: flex;
1717
+ align-items: center;
1718
+ justify-content: center;
1719
+ }
1720
+
1721
+ .chat-list-item:hover .chat-list-item-delete {
1722
+ opacity: 1;
1723
+ }
1724
+
1725
+ .chat-list-item-delete:hover {
1726
+ background: var(--search-snippet-error-background, rgba(239, 68, 68, 0.1));
1727
+ color: var(--search-snippet-error-color, #ef4444);
1728
+ }
1729
+
1730
+ .chat-list-item-delete svg {
1731
+ width: 14px;
1732
+ height: 14px;
1733
+ }
1734
+
1735
+ .chat-list-empty {
1736
+ padding: var(--search-snippet-spacing-xl);
1737
+ text-align: center;
1738
+ color: var(--search-snippet-text-secondary);
1739
+ font-size: var(--search-snippet-font-size-sm);
1740
+ }
1741
+
1742
+ /* Main content area */
1743
+ .chat-main {
1744
+ flex: 1;
1745
+ display: flex;
1746
+ flex-direction: column;
1747
+ min-width: 0;
1748
+ }
1749
+
1750
+ .chat-page-header {
1751
+ background: var(--search-snippet-surface);
1752
+ padding: var(--search-snippet-spacing-lg);
1753
+ border-bottom: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
1754
+ display: flex;
1755
+ align-items: center;
1756
+ justify-content: space-between;
1757
+ flex-shrink: 0;
1758
+ }
1759
+
1760
+ .chat-page-header-left {
1761
+ display: flex;
1762
+ align-items: center;
1763
+ gap: var(--search-snippet-spacing-md);
1764
+ }
1765
+
1766
+ .toggle-sidebar-button {
1767
+ width: 36px;
1768
+ height: 36px;
1769
+ padding: 0;
1770
+ font-family: var(--search-snippet-font-family);
1771
+ color: var(--search-snippet-text-color);
1772
+ background: var(--search-snippet-background);
1773
+ border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
1774
+ border-radius: var(--search-snippet-border-radius);
1775
+ cursor: pointer;
1776
+ outline: none;
1777
+ transition: var(--search-snippet-transition);
1778
+ display: inline-flex;
1779
+ align-items: center;
1780
+ justify-content: center;
1781
+ }
1782
+
1783
+ .toggle-sidebar-button:hover {
1784
+ background: var(--search-snippet-hover-background);
1785
+ }
1786
+
1787
+ .toggle-sidebar-button svg {
1788
+ width: 18px;
1789
+ height: 18px;
1790
+ }
1791
+
1792
+ .chat-page-header-title {
1793
+ font-size: var(--search-snippet-font-size-xl);
1794
+ font-weight: var(--search-snippet-font-weight-bold);
1795
+ color: var(--search-snippet-text-color);
1796
+ display: flex;
1797
+ align-items: center;
1798
+ gap: var(--search-snippet-spacing-md);
1799
+ }
1800
+
1801
+ .chat-page-header-title svg {
1802
+ width: 28px;
1803
+ height: 28px;
1804
+ }
1805
+
1806
+ .chat-page-header-actions {
1807
+ display: flex;
1808
+ gap: var(--search-snippet-spacing-sm);
1809
+ }
1810
+
1811
+ .header-button {
1812
+ height: var(--search-snippet-button-height);
1813
+ padding: 0 var(--search-snippet-spacing-lg);
1814
+ font-family: var(--search-snippet-font-family);
1815
+ font-size: var(--search-snippet-font-size-base);
1816
+ font-weight: var(--search-snippet-font-weight-medium);
1817
+ color: var(--search-snippet-text-color);
1818
+ background: var(--search-snippet-background);
1819
+ border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
1820
+ border-radius: var(--search-snippet-border-radius);
1821
+ cursor: pointer;
1822
+ outline: none;
1823
+ transition: var(--search-snippet-transition);
1824
+ display: inline-flex;
1825
+ align-items: center;
1826
+ justify-content: center;
1827
+ gap: var(--search-snippet-spacing-sm);
1828
+ }
1829
+
1830
+ .header-button:hover {
1831
+ background: var(--search-snippet-hover-background);
1832
+ }
1833
+
1834
+ .header-button svg {
1835
+ width: 16px;
1836
+ height: 16px;
1837
+ }
1838
+
1839
+ .chat-page-content {
1840
+ flex: 1;
1841
+ overflow: hidden;
1842
+ display: flex;
1843
+ flex-direction: column;
1844
+ width: 100%;
1845
+ }
1846
+
1847
+ .container {
1848
+ border: none;
1849
+ box-shadow: none;
1850
+ height: 100%;
1851
+ width: 100%;
1852
+ background: var(--search-snippet-background);
1853
+ border-radius: 0;
1854
+ }
1855
+ `;
1856
+ }
1857
+ getBaseHTML() {
1858
+ return `
1859
+ <div class="chat-sidebar">
1860
+ <div class="sidebar-header">
1861
+ <span class="sidebar-title">History</span>
1862
+ </div>
1863
+ <button class="new-chat-button">
1864
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1865
+ <path d="M12 5v14M5 12h14"></path>
1866
+ </svg>
1867
+ New Chat
1868
+ </button>
1869
+ <div class="chat-list"></div>
1870
+ ${this.getProps().hideBranding ? "" : `<div class="powered-by">${x}</div>`}
1871
+ </div>
1872
+ <div class="chat-main">
1873
+ <div class="chat-page-header">
1874
+ <div class="chat-page-header-left">
1875
+ <button class="toggle-sidebar-button" title="Toggle sidebar">
1876
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1877
+ <path d="M3 12h18M3 6h18M3 18h18"></path>
1878
+ </svg>
1879
+ </button>
1880
+ <div class="chat-page-header-title">
1881
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1882
+ <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>
1883
+ </svg>
1884
+ <span>Chat</span>
1885
+ </div>
1886
+ </div>
1887
+ <div class="chat-page-header-actions">
1888
+ <button class="header-button clear-button">
1889
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1890
+ <path d="M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
1891
+ </svg>
1892
+ Clear Chat
1893
+ </button>
1894
+ </div>
1895
+ </div>
1896
+ <div class="chat-page-content">
1897
+ <div class="container"></div>
1898
+ </div>
1899
+ </div>
1900
+ `;
1901
+ }
1902
+ attachEventListeners() {
1903
+ const e = this.shadow.querySelector(".clear-button"), t = this.shadow.querySelector(".new-chat-button"), s = this.shadow.querySelector(".toggle-sidebar-button"), a = this.shadow.querySelector(".chat-list");
1904
+ this.handleClearClick = () => this.clearCurrentChat(), this.handleNewChatClick = () => this.createNewChat(), this.handleToggleSidebarClick = () => this.toggleSidebar(), this.handleChatListClick = (n) => this.onChatListClick(n), e?.addEventListener("click", this.handleClearClick), t?.addEventListener("click", this.handleNewChatClick), s?.addEventListener("click", this.handleToggleSidebarClick), a?.addEventListener("click", this.handleChatListClick);
1905
+ }
1906
+ removeEventListeners() {
1907
+ const e = this.shadow.querySelector(".clear-button"), t = this.shadow.querySelector(".new-chat-button"), s = this.shadow.querySelector(".toggle-sidebar-button"), a = this.shadow.querySelector(".chat-list"), n = this.shadow.querySelector(".container");
1908
+ this.handleClearClick && e?.removeEventListener("click", this.handleClearClick), this.handleNewChatClick && t?.removeEventListener("click", this.handleNewChatClick), this.handleToggleSidebarClick && s?.removeEventListener("click", this.handleToggleSidebarClick), this.handleChatListClick && a?.removeEventListener("click", this.handleChatListClick), this.handleMessageEvent && n && n.removeEventListener("message", this.handleMessageEvent), this.handleClearClick = null, this.handleNewChatClick = null, this.handleToggleSidebarClick = null, this.handleChatListClick = null, this.handleMessageEvent = null;
1909
+ }
1910
+ setupView() {
1911
+ if (!this.client) return;
1912
+ const e = this.shadow.querySelector(".container");
1913
+ if (!e) return;
1914
+ const t = this.getProps();
1915
+ if (this.chatView = new q(e, this.client, t), this.sessions.length === 0)
1916
+ this.createNewChat();
1917
+ else {
1918
+ const s = this.sessions[0];
1919
+ this.switchToSession(s.id);
1920
+ }
1921
+ this.handleMessageEvent = () => {
1922
+ this.saveCurrentSession(), this.updateSessionTitle(), this.renderChatList();
1923
+ }, e.addEventListener("message", this.handleMessageEvent), this.renderChatList();
1924
+ }
1925
+ generateSessionId() {
1926
+ return `session_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
1927
+ }
1928
+ loadSessions() {
1929
+ try {
1930
+ const e = localStorage.getItem(L);
1931
+ e && (this.sessions = JSON.parse(e), this.sessions.sort((t, s) => s.updatedAt - t.updatedAt));
1932
+ } catch (e) {
1933
+ console.error("Failed to load chat sessions:", e);
1934
+ }
1935
+ }
1936
+ saveSessions() {
1937
+ try {
1938
+ localStorage.setItem(L, JSON.stringify(this.sessions));
1939
+ } catch (e) {
1940
+ console.error("Failed to save chat sessions:", e);
1941
+ }
1942
+ }
1943
+ saveCurrentSession() {
1944
+ if (!this.currentSessionId || !this.chatView) return;
1945
+ const e = this.sessions.findIndex((t) => t.id === this.currentSessionId);
1946
+ e !== -1 && (this.sessions[e].messages = this.chatView.getMessages(), this.sessions[e].updatedAt = Date.now(), this.saveSessions());
1947
+ }
1948
+ updateSessionTitle() {
1949
+ if (!this.currentSessionId) return;
1950
+ const e = this.sessions.find((t) => t.id === this.currentSessionId);
1951
+ if (e && e.messages.length > 0 && e.title === "New Chat") {
1952
+ const t = e.messages.find((s) => s.role === "user");
1953
+ t && (e.title = t.content.slice(0, 50) + (t.content.length > 50 ? "..." : ""), this.saveSessions());
1954
+ }
1955
+ }
1956
+ createNewChat() {
1957
+ this.saveCurrentSession();
1958
+ const e = {
1959
+ id: this.generateSessionId(),
1960
+ title: "New Chat",
1961
+ messages: [],
1962
+ createdAt: Date.now(),
1963
+ updatedAt: Date.now()
1964
+ };
1965
+ this.sessions.unshift(e), this.currentSessionId = e.id, this.saveSessions(), this.chatView?.clearMessages(), this.renderChatList();
1966
+ }
1967
+ switchToSession(e) {
1968
+ if (e === this.currentSessionId) return;
1969
+ this.saveCurrentSession();
1970
+ const t = this.sessions.find((s) => s.id === e);
1971
+ t && this.chatView && (this.currentSessionId = e, this.chatView.setMessages(t.messages), this.renderChatList());
1972
+ }
1973
+ deleteSession(e) {
1974
+ const t = this.sessions.findIndex((s) => s.id === e);
1975
+ t !== -1 && (this.sessions.splice(t, 1), this.saveSessions(), e === this.currentSessionId && (this.sessions.length > 0 ? this.switchToSession(this.sessions[0].id) : this.createNewChat()), this.renderChatList());
1976
+ }
1977
+ clearCurrentChat() {
1978
+ if (!this.currentSessionId) return;
1979
+ const e = this.sessions.find((t) => t.id === this.currentSessionId);
1980
+ e && (e.messages = [], e.title = "New Chat", e.updatedAt = Date.now(), this.saveSessions()), this.chatView?.clearMessages(), this.renderChatList();
1981
+ }
1982
+ toggleSidebar() {
1983
+ this.sidebarCollapsed = !this.sidebarCollapsed, this.shadow.querySelector(".chat-sidebar")?.classList.toggle("collapsed", this.sidebarCollapsed);
1984
+ }
1985
+ onChatListClick(e) {
1986
+ const t = e.target, s = t.closest(".chat-list-item-delete");
1987
+ if (s) {
1988
+ e.stopPropagation();
1989
+ const n = s.getAttribute("data-session-id");
1990
+ n && this.deleteSession(n);
1991
+ return;
1992
+ }
1993
+ const a = t.closest(".chat-list-item");
1994
+ if (a) {
1995
+ const n = a.getAttribute("data-session-id");
1996
+ n && this.switchToSession(n);
1997
+ }
1998
+ }
1999
+ renderChatList() {
2000
+ const e = this.shadow.querySelector(".chat-list");
2001
+ if (e) {
2002
+ if (this.sessions.length === 0) {
2003
+ e.innerHTML = '<div class="chat-list-empty">No chats yet</div>';
2004
+ return;
2005
+ }
2006
+ e.innerHTML = this.sessions.map((t) => this.renderChatListItem(t)).join("");
2007
+ }
2008
+ }
2009
+ renderChatListItem(e) {
2010
+ const t = e.id === this.currentSessionId, s = this.formatDate(e.updatedAt);
2011
+ return `
2012
+ <div class="chat-list-item ${t ? "active" : ""}" data-session-id="${e.id}">
2013
+ <div class="chat-list-item-content">
2014
+ <div class="chat-list-item-title">${this.escapeHTML(e.title)}</div>
2015
+ <div class="chat-list-item-date">${s}</div>
2016
+ </div>
2017
+ <button class="chat-list-item-delete" data-session-id="${e.id}" title="Delete chat">
2018
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2019
+ <path d="M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
2020
+ </svg>
2021
+ </button>
2022
+ </div>
2023
+ `;
2024
+ }
2025
+ formatDate(e) {
2026
+ const t = new Date(e), a = (/* @__PURE__ */ new Date()).getTime() - t.getTime(), n = Math.floor(a / (1e3 * 60 * 60 * 24));
2027
+ return n === 0 ? t.toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit" }) : n === 1 ? "Yesterday" : n < 7 ? t.toLocaleDateString(void 0, { weekday: "long" }) : t.toLocaleDateString(void 0, { month: "short", day: "numeric" });
2028
+ }
2029
+ escapeHTML(e) {
2030
+ const t = document.createElement("div");
2031
+ return t.textContent = e, t.innerHTML;
2032
+ }
2033
+ updateTheme(e) {
2034
+ (e === "light" || e === "dark" ? e : null) === null && this.hasAttribute("theme") && this.getAttribute("theme") !== "auto" && this.removeAttribute("theme");
2035
+ }
2036
+ cleanup() {
2037
+ this.removeEventListeners(), this.client && this.client.cancelAllRequests(), this.chatView && this.chatView.destroy();
2038
+ }
2039
+ // Public API
2040
+ clearChat() {
2041
+ this.clearCurrentChat();
2042
+ }
2043
+ async sendMessage(e) {
2044
+ this.chatView && (await this.chatView.sendMessage(e), this.saveCurrentSession());
2045
+ }
2046
+ getMessages() {
2047
+ return this.chatView?.getMessages() || [];
2048
+ }
2049
+ getSessions() {
2050
+ return [...this.sessions];
2051
+ }
2052
+ getCurrentSession() {
2053
+ return this.sessions.find((e) => e.id === this.currentSessionId) || null;
2054
+ }
2055
+ }
2056
+ customElements.get(M) || customElements.define(M, D);
2057
+ const U = `
2058
+ /* Search view states */
2059
+ .search-view {
2060
+ transition: var(--search-snippet-transition-slow);
2061
+ background: var(--search-snippet-background);
2062
+ border-radius: var(--search-snippet-border-radius);
2063
+ padding: 0px;
2064
+ }
2065
+
2066
+ .search-view-collapsed {
2067
+ max-height: 60px;
2068
+ }
2069
+
2070
+ .search-view-expanded {
2071
+ max-height: var(--search-snippet-max-height);
2072
+ }
2073
+
2074
+
2075
+ .search-icon {
2076
+ width: var(--search-snippet-icon-size);
2077
+ height: var(--search-snippet-icon-size);
2078
+ margin-left: var(--search-snippet-icon-margin-left);
2079
+ color: var(--search-snippet-text-color);
2080
+ }
2081
+
2082
+ /* Search input wrapper */
2083
+ .search-input-wrapper {
2084
+ display: grid;
2085
+ grid-template-columns: auto 1fr auto;
2086
+ align-items: center;
2087
+ gap: var(--search-snippet-spacing-sm);
2088
+ overflow: hidden;
2089
+ transition: max-width var(--search-snippet-transition-slow),
2090
+ opacity var(--search-snippet-transition);
2091
+ padding: var(--search-snippet-spacing-sm);
2092
+ border-radius: var(--search-snippet-border-radius);
2093
+ border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
2094
+ }
2095
+
2096
+
2097
+
2098
+ .search-input {
2099
+ flex: 1;
2100
+ border: none;
2101
+ outline: none;
2102
+ background: transparent;
2103
+ color: var(--search-snippet-text-color);
2104
+ font-size: var(--search-snippet-font-size-base);
2105
+ font-weight: var(--search-snippet-font-weight-medium);
2106
+ box-shadow: none;
2107
+ padding: 0;
2108
+ }
2109
+
2110
+ .search-input::placeholder {
2111
+ color: var(--search-snippet-text-secondary);
2112
+ }
2113
+
2114
+ .search-view:has(.search-input:not(:placeholder-shown)) .search-input-wrapper, .search-view:has(.search-input:not(:placeholder-shown)) {
2115
+ border-bottom-left-radius: 0;
2116
+ border-bottom-right-radius: 0;
2117
+ }
2118
+
2119
+ .search-view:focus-within {
2120
+ border-color: var(--search-snippet-primary-color);
2121
+ box-shadow: inset 0 0 0 3px var(--search-snippet-focus-ring);
2122
+ }
2123
+
2124
+ .search-view:has(.search-input:not(:placeholder-shown)) .search-content {
2125
+ max-height: 600px;
2126
+ opacity: 1;
2127
+ overflow-y: auto;
2128
+ padding: 8px;
2129
+ }
2130
+
2131
+ .search-submit-button {
2132
+ flex-shrink: 0;
2133
+
2134
+ border-radius: max(var(--search-snippet-button-min-border-radius, 4px), calc(var(--search-snippet-border-radius) - var(--search-snippet-spacing-sm)))
2135
+ }
2136
+
2137
+ /* Search content */
2138
+ .search-content {
2139
+ max-height: 0;
2140
+ opacity: 0;
2141
+ transition: max-height var(--search-snippet-transition-slow),
2142
+ opacity var(--search-snippet-transition);
2143
+ position: absolute;
2144
+ width: 100%;
2145
+ background: var(--search-snippet-background);
2146
+ border-bottom-left-radius: var(--search-snippet-border-radius);
2147
+ border-bottom-right-radius: var(--search-snippet-border-radius);
2148
+ border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
2149
+ border-top: none;
2150
+ }
2151
+
2152
+ .search-content::-webkit-scrollbar {
2153
+ width: 8px;
2154
+ height: 100px;
2155
+ }
2156
+
2157
+ .search-content::-webkit-scrollbar-track {
2158
+ background: var(--search-snippet-surface);
2159
+
2160
+ }
2161
+
2162
+ .search-content::-webkit-scrollbar-thumb {
2163
+ background: var(--search-snippet-border-color);
2164
+ border-radius: var(--search-snippet-border-radius);
2165
+ }
2166
+
2167
+ .search-content::-webkit-scrollbar-thumb:hover {
2168
+ background: var(--search-snippet-text-secondary);
2169
+ }
2170
+
2171
+ .container {
2172
+ overflow: unset;
2173
+ position: relative;
2174
+ border: none;
2175
+ }
2176
+
2177
+ .container:has(.search-input:not(:placeholder-shown)) {
2178
+ border-bottom-left-radius: 0;
2179
+ border-bottom-right-radius: 0;
2180
+ }
2181
+
2182
+
2183
+ /* Override header for search mode */
2184
+
2185
+ /* Search results */
2186
+ .search-results {
2187
+ display: flex;
2188
+ flex-direction: column;
2189
+ gap: var(--search-snippet-spacing-sm);
2190
+ }
2191
+
2192
+ .search-result-item {
2193
+ display: flex;
2194
+ flex-direction: row;
2195
+ align-items: flex-start;
2196
+ gap: var(--search-snippet-spacing-md);
2197
+ padding: var(--search-snippet-spacing-md);
2198
+ background: var(--search-snippet-surface);
2199
+ border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
2200
+ border-radius: var(--search-snippet-border-radius);
2201
+ cursor: pointer;
2202
+ transition: var(--search-snippet-transition);
2203
+ }
2204
+
2205
+ /* Image thumbnail container */
2206
+ .search-result-image-container {
2207
+ flex-shrink: 0;
2208
+ width: 64px;
2209
+ height: 64px;
2210
+ border-radius: calc(var(--search-snippet-border-radius) - 4px);
2211
+ overflow: hidden;
2212
+ background: var(--search-snippet-surface);
2213
+ border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
2214
+ position: relative;
2215
+ }
2216
+
2217
+ .search-result-image {
2218
+ width: 100%;
2219
+ height: 100%;
2220
+ object-fit: cover;
2221
+ opacity: 0;
2222
+ transition: opacity var(--search-snippet-transition);
2223
+ }
2224
+
2225
+ .search-result-image.loaded {
2226
+ opacity: 1;
2227
+ }
2228
+
2229
+ /* Loading shimmer */
2230
+ .search-result-image-loading {
2231
+ position: absolute;
2232
+ inset: 0;
2233
+ background: linear-gradient(
2234
+ 90deg,
2235
+ var(--search-snippet-surface) 25%,
2236
+ var(--search-snippet-border-color) 50%,
2237
+ var(--search-snippet-surface) 75%
2238
+ );
2239
+ background-size: 200% 100%;
2240
+ animation: search-image-shimmer 1.5s infinite;
2241
+ }
2242
+
2243
+ @keyframes search-image-shimmer {
2244
+ 0% { background-position: 200% 0; }
2245
+ 100% { background-position: -200% 0; }
2246
+ }
2247
+
2248
+ /* Placeholder icon */
2249
+ .search-result-image-placeholder {
2250
+ position: absolute;
2251
+ inset: 0;
2252
+ display: flex;
2253
+ align-items: center;
2254
+ justify-content: center;
2255
+ color: var(--search-snippet-text-secondary);
2256
+ opacity: 0.5;
2257
+ }
2258
+
2259
+ .search-result-image-placeholder svg {
2260
+ width: 24px;
2261
+ height: 24px;
2262
+ }
2263
+
2264
+ /* Content wrapper */
2265
+ .search-result-content {
2266
+ flex: 1;
2267
+ min-width: 0;
2268
+ }
2269
+
2270
+ .search-result-item:hover {
2271
+ background: var(--search-snippet-hover-background);
2272
+ border-color: var(--search-snippet-primary-color);
2273
+ transform: translateY(-1px);
2274
+ box-shadow: var(--search-snippet-result-item-shadow);
2275
+ }
2276
+
2277
+ .search-result-item:focus-visible {
2278
+ outline: 2px solid var(--search-snippet-primary-color);
2279
+ outline-offset: 2px;
2280
+ }
2281
+
2282
+ .search-result-title {
2283
+ font-size: var(--search-snippet-font-size-base);
2284
+ font-weight: var(--search-snippet-font-weight-medium);
2285
+ color: var(--search-snippet-text-color);
2286
+ margin-bottom: var(--search-snippet-spacing-xs);
2287
+ display: -webkit-box;
2288
+ -webkit-line-clamp: 2;
2289
+ -webkit-box-orient: vertical;
2290
+ overflow: hidden;
2291
+ }
2292
+
2293
+ .search-result-snippet {
2294
+ font-size: var(--search-snippet-font-size-sm);
2295
+ color: var(--search-snippet-text-secondary);
2296
+ line-height: 1.6;
2297
+ display: -webkit-box;
2298
+ -webkit-line-clamp: 3;
2299
+ -webkit-box-orient: vertical;
2300
+ overflow: hidden;
2301
+ }
2302
+
2303
+ .search-result-url {
2304
+ font-size: var(--search-snippet-font-size-sm);
2305
+ color: var(--search-snippet-primary-color);
2306
+ margin-top: var(--search-snippet-spacing-xs);
2307
+ text-decoration: none;
2308
+ display: inline-block;
2309
+ }
2310
+
2311
+ .search-result-url:hover {
2312
+ text-decoration: underline;
2313
+ }
2314
+
2315
+ /* Search header */
2316
+ .search-header {
2317
+ display: flex;
2318
+ align-items: center;
2319
+ justify-content: space-between;
2320
+ margin-bottom: var(--search-snippet-spacing-md);
2321
+ padding-bottom: var(--search-snippet-spacing-sm);
2322
+ border-bottom: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
2323
+ }
2324
+
2325
+ .search-count {
2326
+ font-size: var(--search-snippet-font-size-sm);
2327
+ color: var(--search-snippet-text-secondary);
2328
+ }
2329
+
2330
+ /* Search footer */
2331
+ .search-footer {
2332
+ padding: var(--search-snippet-spacing-md);
2333
+ border-top: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
2334
+ display: flex;
2335
+ align-items: center;
2336
+ justify-content: center;
2337
+ gap: var(--search-snippet-spacing-sm);
2338
+ }
2339
+
2340
+ /* Loading state for search */
2341
+ .search-loading {
2342
+ display: flex;
2343
+ flex-direction: column;
2344
+ align-items: center;
2345
+ justify-content: center;
2346
+ padding: var(--search-snippet-spacing-xxl);
2347
+ gap: var(--search-snippet-spacing-md);
2348
+ color: var(--search-snippet-text-secondary);
2349
+ }
2350
+
2351
+ /* Empty search state */
2352
+ .search-empty {
2353
+ display: flex;
2354
+ flex-direction: column;
2355
+ align-items: center;
2356
+ justify-content: center;
2357
+ padding: var(--search-snippet-spacing-xxl);
2358
+ gap: var(--search-snippet-spacing-md);
2359
+ color: var(--search-snippet-text-secondary);
2360
+ text-align: center;
2361
+ }
2362
+
2363
+ .search-empty-icon {
2364
+ width: 64px;
2365
+ height: 64px;
2366
+ opacity: 0.5;
2367
+ }
2368
+
2369
+ .search-empty-title {
2370
+ font-size: var(--search-snippet-font-size-lg);
2371
+ font-weight: var(--search-snippet-font-weight-medium);
2372
+ color: var(--search-snippet-text-color);
2373
+ }
2374
+
2375
+ .search-empty-description {
2376
+ font-size: var(--search-snippet-font-size-sm);
2377
+ }
2378
+
2379
+ /* Highlight matching text */
2380
+ .search-highlight {
2381
+ background: var(--search-snippet-warning-background);
2382
+ color: var(--search-snippet-warning-color);
2383
+ padding: 1px 2px;
2384
+ border-radius: 2px;
2385
+ font-weight: var(--search-snippet-font-weight-medium);
2386
+ }
2387
+ `, z = "search-bar-snippet";
2388
+ class F extends HTMLElement {
2389
+ constructor() {
2390
+ super();
2391
+ r(this, "shadow");
2392
+ r(this, "client", null);
2393
+ r(this, "container", null);
2394
+ r(this, "inputElement", null);
2395
+ r(this, "resultsContainer", null);
2396
+ r(this, "searchButton", null);
2397
+ r(this, "isLoading", !1);
2398
+ r(this, "currentQuery", "");
2399
+ r(this, "debouncedSearch", null);
2400
+ // Event handler references for cleanup
2401
+ r(this, "handleInputChange", null);
2402
+ r(this, "handleInputKeydownEnter", null);
2403
+ r(this, "handleInputKeydownEscape", null);
2404
+ r(this, "handleSearchButtonClick", null);
2405
+ this.shadow = this.attachShadow({ mode: "open" });
2406
+ }
2407
+ static get observedAttributes() {
2408
+ return [
2409
+ "api-url",
2410
+ "placeholder",
2411
+ "max-results",
2412
+ "debounce-ms",
2413
+ "theme",
2414
+ "hide-branding"
2415
+ ];
2416
+ }
2417
+ connectedCallback() {
2418
+ this.initializeClient(), this.render(), this.dispatchEvent(m("ready", void 0));
2419
+ }
2420
+ disconnectedCallback() {
2421
+ this.cleanup();
2422
+ }
2423
+ attributeChangedCallback(e, t, s) {
2424
+ t !== s && (e === "api-url" ? this.initializeClient() : e === "theme" && this.updateTheme(s));
2425
+ }
2426
+ getProps() {
2427
+ return {
2428
+ apiUrl: u(this.getAttribute("api-url"), "http://localhost:3000"),
2429
+ placeholder: u(this.getAttribute("placeholder"), "Search..."),
2430
+ maxResults: b(this.getAttribute("max-results"), 10),
2431
+ debounceMs: b(this.getAttribute("debounce-ms"), 300),
2432
+ theme: u(this.getAttribute("theme"), "auto"),
2433
+ hideBranding: f(this.getAttribute("hide-branding"), !1)
2434
+ };
2435
+ }
2436
+ initializeClient() {
2437
+ const e = this.getProps();
2438
+ if (!e.apiUrl) {
2439
+ console.error("SearchBarSnippet: api-url attribute is required");
2440
+ return;
2441
+ }
2442
+ try {
2443
+ this.client = w(e.apiUrl);
2444
+ } catch (t) {
2445
+ console.error("SearchBarSnippet:", t);
2446
+ }
2447
+ }
2448
+ render() {
2449
+ const e = this.getProps(), t = (a) => this.performSearch(a);
2450
+ this.debouncedSearch = T(
2451
+ t,
2452
+ e.debounceMs || 400
2453
+ );
2454
+ const s = document.createElement("style");
2455
+ s.textContent = `${y}
2456
+ ${U}`, this.container = document.createElement("div"), this.container.className = "container", this.container.innerHTML = `
2457
+ <div class="search-view">
2458
+ <div class="search-input-wrapper">
2459
+ <svg xmlns="http://www.w3.org/2000/svg" class="search-icon" height="24px" viewBox="0 -960 960 960" width="24px" fill="currentColor"><path d="M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z"/></svg>
2460
+ <input
2461
+ type="text"
2462
+ name="search-input"
2463
+ class="search-input"
2464
+ placeholder="${l(e.placeholder || "Search...")}"
2465
+ aria-label="Search input"
2466
+ autocomplete="off"
2467
+ />
2468
+ <button class="button search-submit-button" aria-label="Search">
2469
+ <span>Search</span>
2470
+ </button>
2471
+ </div>
2472
+ <div class="search-content">
2473
+ <div class="search-results-wrapper">
2474
+ <!-- Results will be inserted here -->
2475
+ </div>
2476
+ </div>
2477
+ </div>
2478
+ `, this.shadow.innerHTML = "", this.shadow.appendChild(s), this.shadow.appendChild(this.container), this.inputElement = this.container.querySelector(".search-input"), this.resultsContainer = this.container.querySelector(".search-results-wrapper"), this.searchButton = this.container.querySelector(".search-submit-button"), this.attachEventListeners();
2479
+ }
2480
+ attachEventListeners() {
2481
+ this.inputElement && (this.handleInputChange = (e) => {
2482
+ const s = e.target.value.trim();
2483
+ s.length > 0 && this.debouncedSearch ? this.debouncedSearch(s) : this.showEmptyState();
2484
+ }, this.inputElement.addEventListener("input", this.handleInputChange), this.handleInputKeydownEnter = (e) => {
2485
+ if (e.key === "Enter") {
2486
+ const t = e.target.value.trim();
2487
+ t.length > 0 && this.performSearch(t);
2488
+ }
2489
+ }, this.inputElement.addEventListener("keydown", this.handleInputKeydownEnter), this.handleInputKeydownEscape = (e) => {
2490
+ e.key === "Escape" && this.inputElement && (this.inputElement.value = "");
2491
+ }, window.addEventListener("keydown", this.handleInputKeydownEscape), this.searchButton && (this.handleSearchButtonClick = () => {
2492
+ const e = this.inputElement?.value.trim() || "";
2493
+ e.length > 0 && this.performSearch(e);
2494
+ }, this.searchButton.addEventListener("click", this.handleSearchButtonClick)));
2495
+ }
2496
+ async performSearch(e) {
2497
+ if (!(this.isLoading || e === this.currentQuery || !this.client)) {
2498
+ this.currentQuery = e, this.isLoading = !0, this.showLoadingState();
2499
+ try {
2500
+ const t = await this.client.search(e, { streaming: !1 });
2501
+ this.displayResults(t, e);
2502
+ } catch (t) {
2503
+ this.showErrorState(t.message);
2504
+ } finally {
2505
+ this.isLoading = !1;
2506
+ }
2507
+ }
2508
+ }
2509
+ displayResults(e, t) {
2510
+ if (!this.resultsContainer) return;
2511
+ if (e.length === 0) {
2512
+ this.showNoResultsState(t);
2513
+ return;
2514
+ }
2515
+ const a = this.getProps().hideBranding ? "" : `<div class="powered-by-inline">${x}</div>`, n = `
2516
+ <div class="search-header">
2517
+ <div class="search-count">
2518
+ Found ${e.length} result${e.length === 1 ? "" : "s"}
2519
+ </div>
2520
+ ${a}
2521
+ </div>
2522
+ <div class="search-results">
2523
+ ${e.map((c) => this.renderResult(c)).join("")}
2524
+ </div>
2525
+ `;
2526
+ this.resultsContainer.innerHTML = n, this.attachResultHandlers();
2527
+ }
2528
+ renderResult(e) {
2529
+ const t = this.renderResultImage(e.image, e.title);
2530
+ return `
2531
+ <div class="search-result-item" role="button" tabindex="0" data-result-id="${l(e.url || "")}">
2532
+ ${t}
2533
+ <div class="search-result-content">
2534
+ <div class="search-result-title">${l(e.title || "")}</div>
2535
+ <div class="search-result-snippet">${l(e.description || "")}</div>
2536
+ ${e.url ? `<a href="${l(e.url)}" class="search-result-url">${l(e.url)}</a>` : ""}
2537
+ </div>
2538
+ </div>
2539
+ `;
2540
+ }
2541
+ renderResultImage(e, t) {
2542
+ const s = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>';
2543
+ return e ? `
2544
+ <div class="search-result-image-container">
2545
+ <div class="search-result-image-loading"></div>
2546
+ <div class="search-result-image-placeholder" style="display: none;">${s}</div>
2547
+ <img
2548
+ class="search-result-image"
2549
+ src="${l(e)}"
2550
+ alt="${l(t)}"
2551
+ loading="lazy"
2552
+ />
2553
+ </div>
2554
+ ` : `
2555
+ <div class="search-result-image-container">
2556
+ <div class="search-result-image-placeholder">${s}</div>
2557
+ </div>
2558
+ `;
2559
+ }
2560
+ attachResultHandlers() {
2561
+ const e = this.container?.querySelectorAll(".search-result-item");
2562
+ if (!e) return;
2563
+ for (const s of e)
2564
+ s.addEventListener("click", () => {
2565
+ const a = s.getAttribute("data-result-id");
2566
+ console.log("Result clicked:", a);
2567
+ }), s.addEventListener("keydown", (a) => {
2568
+ (a.key === "Enter" || a.key === " ") && s.click();
2569
+ });
2570
+ this.container?.querySelectorAll(".search-result-image")?.forEach((s) => {
2571
+ s.addEventListener("load", () => {
2572
+ s.classList.add("loaded"), s.closest(".search-result-image-container")?.querySelector(".search-result-image-loading")?.remove();
2573
+ }), s.addEventListener("error", () => {
2574
+ const a = s.closest(".search-result-image-container");
2575
+ a?.querySelector(".search-result-image-loading")?.remove();
2576
+ const n = a?.querySelector(
2577
+ ".search-result-image-placeholder"
2578
+ );
2579
+ n && (n.style.display = "flex"), s.style.display = "none";
2580
+ });
2581
+ });
2582
+ }
2583
+ showLoadingState() {
2584
+ this.resultsContainer && (this.resultsContainer.innerHTML = `
2585
+ <div class="search-loading">
2586
+ <div class="loading" aria-label="Loading"></div>
2587
+ <div>Searching...</div>
2588
+ </div>
2589
+ `);
2590
+ }
2591
+ showEmptyState() {
2592
+ this.resultsContainer && (this.resultsContainer.innerHTML = `
2593
+ <div class="search-empty">
2594
+ <svg class="search-empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2595
+ <circle cx="11" cy="11" r="8"></circle>
2596
+ <path d="m21 21-4.35-4.35"></path>
2597
+ </svg>
2598
+ <div class="search-empty-title">Start Searching</div>
2599
+ <div class="search-empty-description">
2600
+ Enter a query to search for results
2601
+ </div>
2602
+ </div>
2603
+ `);
2604
+ }
2605
+ showNoResultsState(e) {
2606
+ this.resultsContainer && (this.resultsContainer.innerHTML = `
2607
+ <div class="search-empty">
2608
+ <svg class="search-empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2609
+ <circle cx="11" cy="11" r="8"></circle>
2610
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
2611
+ </svg>
2612
+ <div class="search-empty-title">No Results Found</div>
2613
+ <div class="search-empty-description">
2614
+ No results found for "${l(e)}"
2615
+ </div>
2616
+ </div>
2617
+ `);
2618
+ }
2619
+ showErrorState(e) {
2620
+ this.resultsContainer && (this.resultsContainer.innerHTML = `
2621
+ <div class="error">
2622
+ <strong>Error:</strong> ${l(e)}
2623
+ </div>
2624
+ `);
2625
+ }
2626
+ updateTheme(e) {
2627
+ const t = e === "light" || e === "dark" || e === "auto" ? e : "auto";
2628
+ t === "auto" ? this.removeAttribute("theme") : this.setAttribute("theme", t);
2629
+ }
2630
+ cleanup() {
2631
+ this.client && this.client.cancelAllRequests(), this.inputElement && (this.handleInputChange && this.inputElement.removeEventListener("input", this.handleInputChange), this.handleInputKeydownEnter && this.inputElement.removeEventListener("keydown", this.handleInputKeydownEnter), this.handleInputKeydownEscape && window.removeEventListener("keydown", this.handleInputKeydownEscape)), this.searchButton && this.handleSearchButtonClick && this.searchButton.removeEventListener("click", this.handleSearchButtonClick), this.handleInputChange = null, this.handleInputKeydownEnter = null, this.handleInputKeydownEscape = null, this.handleSearchButtonClick = null;
2632
+ }
2633
+ // Public API
2634
+ async search(e) {
2635
+ await this.performSearch(e);
2636
+ }
2637
+ }
2638
+ customElements.get(z) || customElements.define(z, F);
2639
+ const G = `
2640
+ /* Modal backdrop */
2641
+ .modal-backdrop {
2642
+ position: fixed;
2643
+ inset: 0;
2644
+ background: rgba(0, 0, 0, 0.5);
2645
+ backdrop-filter: blur(4px);
2646
+ z-index: var(--search-snippet-z-modal);
2647
+ opacity: 0;
2648
+ visibility: hidden;
2649
+ transition: opacity var(--search-snippet-transition), visibility var(--search-snippet-transition);
2650
+ }
2651
+
2652
+ .modal-backdrop.open {
2653
+ opacity: 1;
2654
+ visibility: visible;
2655
+ }
2656
+
2657
+ /* Modal container */
2658
+ .modal-container {
2659
+ position: fixed;
2660
+ top: 15%;
2661
+ left: 50%;
2662
+ transform: translateX(-50%) scale(0.95);
2663
+ width: 90%;
2664
+ max-width: 600px;
2665
+ max-height: 70vh;
2666
+ background: var(--search-snippet-background);
2667
+ border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
2668
+ border-radius: var(--search-snippet-border-radius);
2669
+ box-shadow: var(--search-snippet-shadow-lg);
2670
+ z-index: calc(var(--search-snippet-z-modal) + 1);
2671
+ display: flex;
2672
+ flex-direction: column;
2673
+ opacity: 0;
2674
+ visibility: hidden;
2675
+ transition: opacity var(--search-snippet-transition),
2676
+ visibility var(--search-snippet-transition),
2677
+ transform var(--search-snippet-transition);
2678
+ }
2679
+
2680
+ .modal-container.open {
2681
+ opacity: 1;
2682
+ visibility: visible;
2683
+ transform: translateX(-50%) scale(1);
2684
+ }
2685
+
2686
+ /* Modal header with search input */
2687
+ .modal-header {
2688
+ display: flex;
2689
+ align-items: center;
2690
+ gap: var(--search-snippet-spacing-sm);
2691
+ padding: var(--search-snippet-spacing-md);
2692
+ border-bottom: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
2693
+ }
2694
+
2695
+ .modal-search-icon {
2696
+ width: var(--search-snippet-icon-size);
2697
+ height: var(--search-snippet-icon-size);
2698
+ color: var(--search-snippet-text-secondary);
2699
+ flex-shrink: 0;
2700
+ }
2701
+
2702
+ .modal-search-input {
2703
+ flex: 1;
2704
+ border: none;
2705
+ outline: none;
2706
+ background: transparent;
2707
+ color: var(--search-snippet-text-color);
2708
+ font-size: var(--search-snippet-font-size-lg);
2709
+ font-family: var(--search-snippet-font-family);
2710
+ font-weight: var(--search-snippet-font-weight-normal);
2711
+ padding: var(--search-snippet-spacing-xs) 0;
2712
+ }
2713
+
2714
+ .modal-search-input::placeholder {
2715
+ color: var(--search-snippet-text-secondary);
2716
+ }
2717
+
2718
+ .modal-shortcut-hint {
2719
+ display: flex;
2720
+ align-items: center;
2721
+ gap: var(--search-snippet-spacing-xs);
2722
+ color: var(--search-snippet-text-secondary);
2723
+ font-size: var(--search-snippet-font-size-sm);
2724
+ flex-shrink: 0;
2725
+ }
2726
+
2727
+ .modal-kbd {
2728
+ display: inline-flex;
2729
+ align-items: center;
2730
+ justify-content: center;
2731
+ min-width: 24px;
2732
+ height: 22px;
2733
+ padding: 0 var(--search-snippet-spacing-xs);
2734
+ background: var(--search-snippet-surface);
2735
+ border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
2736
+ border-radius: 4px;
2737
+ font-size: var(--search-snippet-font-size-sm);
2738
+ font-family: var(--search-snippet-font-family);
2739
+ color: var(--search-snippet-text-secondary);
2740
+ }
2741
+
2742
+ /* Modal content (results area) */
2743
+ .modal-content {
2744
+ flex: 1;
2745
+ overflow-y: auto;
2746
+ overflow-x: hidden;
2747
+ padding: var(--search-snippet-spacing-sm);
2748
+ }
2749
+
2750
+ .modal-content::-webkit-scrollbar {
2751
+ width: 8px;
2752
+ }
2753
+
2754
+ .modal-content::-webkit-scrollbar-track {
2755
+ background: var(--search-snippet-surface);
2756
+ }
2757
+
2758
+ .modal-content::-webkit-scrollbar-thumb {
2759
+ background: var(--search-snippet-border-color);
2760
+ border-radius: var(--search-snippet-border-radius);
2761
+ }
2762
+
2763
+ .modal-content::-webkit-scrollbar-thumb:hover {
2764
+ background: var(--search-snippet-text-secondary);
2765
+ }
2766
+
2767
+ /* Results list */
2768
+ .modal-results {
2769
+ display: flex;
2770
+ flex-direction: column;
2771
+ gap: var(--search-snippet-spacing-xs);
2772
+ }
2773
+
2774
+ .modal-result-item {
2775
+ padding: var(--search-snippet-spacing-md);
2776
+ background: transparent;
2777
+ border: var(--search-snippet-border-width) solid transparent;
2778
+ border-radius: calc(var(--search-snippet-border-radius) - 4px);
2779
+ cursor: pointer;
2780
+ transition: var(--search-snippet-transition-fast);
2781
+ display: flex;
2782
+ flex-direction: row;
2783
+ align-items: flex-start;
2784
+ gap: var(--search-snippet-spacing-md);
2785
+ }
2786
+
2787
+ /* Image thumbnail container */
2788
+ .modal-result-image-container {
2789
+ flex-shrink: 0;
2790
+ width: 48px;
2791
+ height: 48px;
2792
+ border-radius: 6px;
2793
+ overflow: hidden;
2794
+ background: var(--search-snippet-surface);
2795
+ border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
2796
+ position: relative;
2797
+ }
2798
+
2799
+ .modal-result-image {
2800
+ width: 100%;
2801
+ height: 100%;
2802
+ object-fit: cover;
2803
+ opacity: 0;
2804
+ transition: opacity var(--search-snippet-transition);
2805
+ }
2806
+
2807
+ .modal-result-image.loaded {
2808
+ opacity: 1;
2809
+ }
2810
+
2811
+ /* Loading shimmer */
2812
+ .modal-result-image-loading {
2813
+ position: absolute;
2814
+ inset: 0;
2815
+ background: linear-gradient(
2816
+ 90deg,
2817
+ var(--search-snippet-surface) 25%,
2818
+ var(--search-snippet-border-color) 50%,
2819
+ var(--search-snippet-surface) 75%
2820
+ );
2821
+ background-size: 200% 100%;
2822
+ animation: modal-image-shimmer 1.5s infinite;
2823
+ }
2824
+
2825
+ @keyframes modal-image-shimmer {
2826
+ 0% { background-position: 200% 0; }
2827
+ 100% { background-position: -200% 0; }
2828
+ }
2829
+
2830
+ /* Placeholder icon */
2831
+ .modal-result-image-placeholder {
2832
+ position: absolute;
2833
+ inset: 0;
2834
+ display: flex;
2835
+ align-items: center;
2836
+ justify-content: center;
2837
+ color: var(--search-snippet-text-secondary);
2838
+ opacity: 0.5;
2839
+ }
2840
+
2841
+ .modal-result-image-placeholder svg {
2842
+ width: 20px;
2843
+ height: 20px;
2844
+ }
2845
+
2846
+ /* Content wrapper */
2847
+ .modal-result-content {
2848
+ flex: 1;
2849
+ min-width: 0;
2850
+ display: flex;
2851
+ flex-direction: column;
2852
+ gap: var(--search-snippet-spacing-xs);
2853
+ }
2854
+
2855
+ .modal-result-item:hover,
2856
+ .modal-result-item.active {
2857
+ background: var(--search-snippet-hover-background);
2858
+ border-color: var(--search-snippet-border-color);
2859
+ }
2860
+
2861
+ .modal-result-item.active {
2862
+ border-color: var(--search-snippet-primary-color);
2863
+ background: var(--search-snippet-focus-ring);
2864
+ }
2865
+
2866
+ .modal-result-item:focus-visible {
2867
+ outline: 2px solid var(--search-snippet-primary-color);
2868
+ outline-offset: -2px;
2869
+ }
2870
+
2871
+ .modal-result-title {
2872
+ font-size: var(--search-snippet-font-size-base);
2873
+ font-weight: var(--search-snippet-font-weight-medium);
2874
+ color: var(--search-snippet-text-color);
2875
+ display: -webkit-box;
2876
+ -webkit-line-clamp: 1;
2877
+ -webkit-box-orient: vertical;
2878
+ overflow: hidden;
2879
+ }
2880
+
2881
+ .modal-result-description {
2882
+ font-size: var(--search-snippet-font-size-sm);
2883
+ color: var(--search-snippet-text-secondary);
2884
+ line-height: 1.5;
2885
+ display: -webkit-box;
2886
+ -webkit-line-clamp: 2;
2887
+ -webkit-box-orient: vertical;
2888
+ overflow: hidden;
2889
+ }
2890
+
2891
+ .modal-result-url {
2892
+ font-size: var(--search-snippet-font-size-sm);
2893
+ color: var(--search-snippet-primary-color);
2894
+ text-decoration: none;
2895
+ display: inline-block;
2896
+ max-width: 100%;
2897
+ overflow: hidden;
2898
+ text-overflow: ellipsis;
2899
+ white-space: nowrap;
2900
+ }
2901
+
2902
+ .modal-result-url:hover {
2903
+ text-decoration: underline;
2904
+ }
2905
+
2906
+ /* Result group header */
2907
+ .modal-group-header {
2908
+ padding: var(--search-snippet-spacing-sm) var(--search-snippet-spacing-md);
2909
+ font-size: var(--search-snippet-font-size-sm);
2910
+ font-weight: var(--search-snippet-font-weight-medium);
2911
+ color: var(--search-snippet-text-secondary);
2912
+ text-transform: uppercase;
2913
+ letter-spacing: 0.05em;
2914
+ }
2915
+
2916
+ /* Loading state */
2917
+ .modal-loading {
2918
+ display: flex;
2919
+ flex-direction: column;
2920
+ align-items: center;
2921
+ justify-content: center;
2922
+ padding: var(--search-snippet-spacing-xxl);
2923
+ gap: var(--search-snippet-spacing-md);
2924
+ color: var(--search-snippet-text-secondary);
2925
+ }
2926
+
2927
+ /* Empty state */
2928
+ .modal-empty {
2929
+ display: flex;
2930
+ flex-direction: column;
2931
+ align-items: center;
2932
+ justify-content: center;
2933
+ padding: var(--search-snippet-spacing-xxl);
2934
+ gap: var(--search-snippet-spacing-md);
2935
+ color: var(--search-snippet-text-secondary);
2936
+ text-align: center;
2937
+ }
2938
+
2939
+ .modal-empty-icon {
2940
+ width: 48px;
2941
+ height: 48px;
2942
+ opacity: 0.5;
2943
+ }
2944
+
2945
+ .modal-empty-title {
2946
+ font-size: var(--search-snippet-font-size-base);
2947
+ font-weight: var(--search-snippet-font-weight-medium);
2948
+ color: var(--search-snippet-text-color);
2949
+ }
2950
+
2951
+ .modal-empty-description {
2952
+ font-size: var(--search-snippet-font-size-sm);
2953
+ }
2954
+
2955
+ /* Footer */
2956
+ .modal-footer {
2957
+ display: flex;
2958
+ align-items: center;
2959
+ justify-content: space-between;
2960
+ padding: var(--search-snippet-spacing-sm) var(--search-snippet-spacing-md);
2961
+ border-top: var(--search-snippet-border-width) solid var(--search-snippet-border-color);
2962
+ background: var(--search-snippet-surface);
2963
+ font-size: var(--search-snippet-font-size-sm);
2964
+ color: var(--search-snippet-text-secondary);
2965
+ border-radius: 0 0 var(--search-snippet-border-radius) var(--search-snippet-border-radius);
2966
+ }
2967
+
2968
+ .modal-footer-hints {
2969
+ display: flex;
2970
+ align-items: center;
2971
+ gap: var(--search-snippet-spacing-md);
2972
+ }
2973
+
2974
+ .modal-footer-hint {
2975
+ display: flex;
2976
+ align-items: center;
2977
+ gap: var(--search-snippet-spacing-xs);
2978
+ }
2979
+
2980
+ .modal-footer-hint .modal-kbd {
2981
+ min-width: 20px;
2982
+ height: 20px;
2983
+ font-size: 11px;
2984
+ }
2985
+
2986
+ /* Results count */
2987
+ .modal-results-count {
2988
+ font-size: var(--search-snippet-font-size-sm);
2989
+ color: var(--search-snippet-text-secondary);
2990
+ }
2991
+
2992
+ /* Powered by in modal footer */
2993
+ .modal-footer .powered-by-inline {
2994
+ font-size: var(--search-snippet-font-size-sm);
2995
+ color: var(--search-snippet-text-secondary);
2996
+ }
2997
+
2998
+ .modal-footer .powered-by-inline a {
2999
+ color: var(--search-snippet-text-secondary);
3000
+ text-decoration: none;
3001
+ transition: color var(--search-snippet-transition-fast);
3002
+ }
3003
+
3004
+ .modal-footer .powered-by-inline a:hover {
3005
+ color: var(--search-snippet-primary-color);
3006
+ }
3007
+
3008
+ /* Responsive adjustments */
3009
+ @media (max-width: 640px) {
3010
+ .modal-container {
3011
+ top: 10%;
3012
+ width: 95%;
3013
+ max-height: 80vh;
3014
+ }
3015
+
3016
+ .modal-footer-hints {
3017
+ display: none;
3018
+ }
3019
+ }
3020
+
3021
+ /* Animation for modal open */
3022
+ @keyframes modal-slide-in {
3023
+ from {
3024
+ opacity: 0;
3025
+ transform: translateX(-50%) scale(0.95) translateY(-10px);
3026
+ }
3027
+ to {
3028
+ opacity: 1;
3029
+ transform: translateX(-50%) scale(1) translateY(0);
3030
+ }
3031
+ }
3032
+
3033
+ .modal-container.open {
3034
+ animation: modal-slide-in var(--search-snippet-transition) ease-out;
3035
+ }
3036
+ `, I = "search-modal-snippet";
3037
+ class Y extends HTMLElement {
3038
+ constructor() {
3039
+ super();
3040
+ r(this, "shadow");
3041
+ r(this, "client", null);
3042
+ r(this, "backdrop", null);
3043
+ r(this, "modal", null);
3044
+ r(this, "inputElement", null);
3045
+ r(this, "resultsContainer", null);
3046
+ r(this, "footerCount", null);
3047
+ r(this, "isOpen", !1);
3048
+ r(this, "isLoading", !1);
3049
+ r(this, "results", []);
3050
+ r(this, "activeIndex", -1);
3051
+ r(this, "debouncedSearch", null);
3052
+ // Event handler references for cleanup
3053
+ r(this, "handleGlobalKeydown", null);
3054
+ r(this, "handleInputChange", null);
3055
+ r(this, "handleInputKeydown", null);
3056
+ r(this, "handleBackdropClick", null);
3057
+ // Scroll lock state
3058
+ r(this, "savedBodyStyles", null);
3059
+ r(this, "savedHtmlOverflow", null);
3060
+ this.shadow = this.attachShadow({ mode: "open" });
3061
+ }
3062
+ static get observedAttributes() {
3063
+ return [
3064
+ "api-url",
3065
+ "placeholder",
3066
+ "max-results",
3067
+ "theme",
3068
+ "shortcut",
3069
+ "use-meta-key",
3070
+ "debounce-ms",
3071
+ "hide-branding"
3072
+ ];
3073
+ }
3074
+ connectedCallback() {
3075
+ this.initializeClient(), this.render(), this.attachGlobalKeyboardShortcut(), this.dispatchEvent(m("ready", void 0));
3076
+ }
3077
+ disconnectedCallback() {
3078
+ this.cleanup();
3079
+ }
3080
+ attributeChangedCallback(e, t, s) {
3081
+ t !== s && (e === "api-url" ? this.initializeClient() : e === "theme" && this.updateTheme(s));
3082
+ }
3083
+ getProps() {
3084
+ return {
3085
+ apiUrl: u(this.getAttribute("api-url"), "http://localhost:3000"),
3086
+ placeholder: u(this.getAttribute("placeholder"), "Search..."),
3087
+ maxResults: b(this.getAttribute("max-results"), 10),
3088
+ debounceMs: b(this.getAttribute("debounce-ms"), 300),
3089
+ theme: u(this.getAttribute("theme"), "auto"),
3090
+ shortcut: u(this.getAttribute("shortcut"), "k"),
3091
+ useMetaKey: this.getAttribute("use-meta-key") !== "false",
3092
+ hideBranding: f(this.getAttribute("hide-branding"), !1)
3093
+ };
3094
+ }
3095
+ initializeClient() {
3096
+ const e = this.getProps();
3097
+ if (!e.apiUrl) {
3098
+ console.error("SearchModalSnippet: api-url attribute is required");
3099
+ return;
3100
+ }
3101
+ try {
3102
+ this.client = w(e.apiUrl);
3103
+ } catch (t) {
3104
+ console.error("SearchModalSnippet:", t);
3105
+ }
3106
+ }
3107
+ render() {
3108
+ const e = this.getProps(), t = (c) => this.performSearch(c);
3109
+ this.debouncedSearch = T(
3110
+ t,
3111
+ e.debounceMs || 300
3112
+ );
3113
+ const s = document.createElement("style");
3114
+ s.textContent = `${y}
3115
+ ${G}`;
3116
+ const a = e.hideBranding ? "" : `<div class="powered-by-inline">${x}</div>`, n = document.createElement("div");
3117
+ n.innerHTML = `
3118
+ <div class="modal-backdrop" role="presentation"></div>
3119
+ <div class="modal-container" role="dialog" aria-modal="true" aria-labelledby="modal-title">
3120
+ <div class="modal-header">
3121
+ <svg class="modal-search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor">
3122
+ <path d="M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z"/>
3123
+ </svg>
3124
+ <input
3125
+ type="text"
3126
+ class="modal-search-input"
3127
+ placeholder="${l(e.placeholder || "Search...")}"
3128
+ aria-label="Search"
3129
+ aria-autocomplete="list"
3130
+ aria-controls="modal-results-list"
3131
+ aria-expanded="false"
3132
+ autocomplete="off"
3133
+ spellcheck="false"
3134
+ />
3135
+ </div>
3136
+ <div class="modal-content">
3137
+ <div class="modal-results" id="modal-results-list" role="listbox" aria-label="Search results">
3138
+ ${this.renderEmptyState()}
3139
+ </div>
3140
+ </div>
3141
+ <div class="modal-footer">
3142
+ <div class="modal-footer-hints">
3143
+ <div class="modal-footer-hint">
3144
+ <kbd class="modal-kbd">↑</kbd>
3145
+ <kbd class="modal-kbd">↓</kbd>
3146
+ <span>Navigate</span>
3147
+ </div>
3148
+ <div class="modal-footer-hint">
3149
+ <kbd class="modal-kbd">↵</kbd>
3150
+ <span>Select</span>
3151
+ </div>
3152
+ <div class="modal-footer-hint">
3153
+ <kbd class="modal-kbd">Esc</kbd>
3154
+ <span>Close</span>
3155
+ </div>
3156
+ </div>
3157
+ ${a}
3158
+ </div>
3159
+ </div>
3160
+ `, this.shadow.innerHTML = "", this.shadow.appendChild(s), this.shadow.appendChild(n), this.backdrop = this.shadow.querySelector(".modal-backdrop"), this.modal = this.shadow.querySelector(".modal-container"), this.inputElement = this.shadow.querySelector(".modal-search-input"), this.resultsContainer = this.shadow.querySelector(".modal-results"), this.footerCount = this.shadow.querySelector(".modal-results-count"), this.attachEventListeners();
3161
+ }
3162
+ attachGlobalKeyboardShortcut() {
3163
+ const e = this.getProps(), t = e.shortcut?.toLowerCase() || "k";
3164
+ this.handleGlobalKeydown = (s) => {
3165
+ (e.useMetaKey && s.metaKey || s.ctrlKey) && s.key.toLowerCase() === t && !this.isOpen && (s.preventDefault(), this.open());
3166
+ }, document.addEventListener("keydown", this.handleGlobalKeydown);
3167
+ }
3168
+ attachEventListeners() {
3169
+ !this.inputElement || !this.backdrop || (this.handleInputChange = (e) => {
3170
+ const s = e.target.value.trim();
3171
+ s.length > 0 && this.debouncedSearch ? this.debouncedSearch(s) : (this.results = [], this.activeIndex = -1, this.showEmptyState());
3172
+ }, this.inputElement.addEventListener("input", this.handleInputChange), this.handleInputKeydown = (e) => {
3173
+ switch (e.key) {
3174
+ case "ArrowDown":
3175
+ e.preventDefault(), this.navigateResults(1);
3176
+ break;
3177
+ case "ArrowUp":
3178
+ e.preventDefault(), this.navigateResults(-1);
3179
+ break;
3180
+ case "Enter":
3181
+ e.preventDefault(), this.selectActiveResult();
3182
+ break;
3183
+ case "Escape":
3184
+ e.preventDefault(), this.close();
3185
+ break;
3186
+ }
3187
+ }, this.inputElement.addEventListener("keydown", this.handleInputKeydown), this.handleBackdropClick = (e) => {
3188
+ e.target === this.backdrop && this.close();
3189
+ }, this.backdrop.addEventListener("click", this.handleBackdropClick));
3190
+ }
3191
+ navigateResults(e) {
3192
+ if (this.results.length === 0) return;
3193
+ const t = this.activeIndex + e;
3194
+ t < 0 ? this.activeIndex = this.results.length - 1 : t >= this.results.length ? this.activeIndex = 0 : this.activeIndex = t, this.updateActiveResult();
3195
+ }
3196
+ updateActiveResult() {
3197
+ const e = this.resultsContainer?.querySelectorAll(".modal-result-item");
3198
+ e && (e.forEach((t, s) => {
3199
+ s === this.activeIndex ? (t.classList.add("active"), t.setAttribute("aria-selected", "true"), t.scrollIntoView({ block: "nearest" })) : (t.classList.remove("active"), t.setAttribute("aria-selected", "false"));
3200
+ }), this.inputElement && this.activeIndex >= 0 ? this.inputElement.setAttribute("aria-activedescendant", `result-${this.activeIndex}`) : this.inputElement && this.inputElement.removeAttribute("aria-activedescendant"));
3201
+ }
3202
+ selectActiveResult() {
3203
+ if (this.activeIndex < 0 || this.activeIndex >= this.results.length) {
3204
+ const t = this.inputElement?.value.trim();
3205
+ t && t.length > 0 && this.performSearch(t);
3206
+ return;
3207
+ }
3208
+ const e = this.results[this.activeIndex];
3209
+ this.dispatchEvent(
3210
+ m("result-select", {
3211
+ result: e,
3212
+ index: this.activeIndex
3213
+ })
3214
+ ), e.url && (window.location.href = e.url), this.close();
3215
+ }
3216
+ async performSearch(e) {
3217
+ if (!(this.isLoading || !this.client)) {
3218
+ this.isLoading = !0, this.showLoadingState();
3219
+ try {
3220
+ const t = await this.client.search(e, { streaming: !1 }), s = this.getProps();
3221
+ this.results = t.slice(0, s.maxResults || 10), this.activeIndex = this.results.length > 0 ? 0 : -1, this.displayResults(this.results, e);
3222
+ } catch (t) {
3223
+ this.showErrorState(t.message);
3224
+ } finally {
3225
+ this.isLoading = !1;
3226
+ }
3227
+ }
3228
+ }
3229
+ displayResults(e, t) {
3230
+ if (!this.resultsContainer) return;
3231
+ if (e.length === 0) {
3232
+ this.showNoResultsState(t);
3233
+ return;
3234
+ }
3235
+ const s = e.map((a, n) => this.renderResult(a, n)).join("");
3236
+ this.resultsContainer.innerHTML = s, this.footerCount && (this.footerCount.textContent = `${e.length} result${e.length === 1 ? "" : "s"}`), this.inputElement && this.inputElement.setAttribute("aria-expanded", "true"), this.attachResultHandlers(), this.updateActiveResult();
3237
+ }
3238
+ renderResult(e, t) {
3239
+ const s = this.renderResultImage(e.image, e.title);
3240
+ return `
3241
+ <div
3242
+ class="modal-result-item${t === this.activeIndex ? " active" : ""}"
3243
+ role="option"
3244
+ id="result-${t}"
3245
+ aria-selected="${t === this.activeIndex}"
3246
+ tabindex="-1"
3247
+ data-index="${t}"
3248
+ data-url="${l(e.url || "")}"
3249
+ >
3250
+ ${s}
3251
+ <div class="modal-result-content">
3252
+ <div class="modal-result-title">${l(e.title || "")}</div>
3253
+ ${e.description ? `<div class="modal-result-description">${l(e.description)}</div>` : ""}
3254
+ ${e.url ? `<a href="${l(e.url)}" class="modal-result-url" tabindex="-1">${l(e.url)}</a>` : ""}
3255
+ </div>
3256
+ </div>
3257
+ `;
3258
+ }
3259
+ renderResultImage(e, t) {
3260
+ const s = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>';
3261
+ return e ? `
3262
+ <div class="modal-result-image-container">
3263
+ <div class="modal-result-image-loading"></div>
3264
+ <div class="modal-result-image-placeholder" style="display: none;">${s}</div>
3265
+ <img
3266
+ class="modal-result-image"
3267
+ src="${l(e)}"
3268
+ alt="${l(t)}"
3269
+ loading="lazy"
3270
+ />
3271
+ </div>
3272
+ ` : `
3273
+ <div class="modal-result-image-container">
3274
+ <div class="modal-result-image-placeholder">${s}</div>
3275
+ </div>
3276
+ `;
3277
+ }
3278
+ attachResultHandlers() {
3279
+ const e = this.resultsContainer?.querySelectorAll(".modal-result-item");
3280
+ if (!e) return;
3281
+ e.forEach((s, a) => {
3282
+ s.addEventListener("click", (n) => {
3283
+ n.target.classList.contains("modal-result-url") || (this.activeIndex = a, this.selectActiveResult());
3284
+ }), s.addEventListener("mouseenter", () => {
3285
+ this.activeIndex = a, this.updateActiveResult();
3286
+ });
3287
+ }), this.resultsContainer?.querySelectorAll(".modal-result-image")?.forEach((s) => {
3288
+ s.addEventListener("load", () => {
3289
+ s.classList.add("loaded"), s.closest(".modal-result-image-container")?.querySelector(".modal-result-image-loading")?.remove();
3290
+ }), s.addEventListener("error", () => {
3291
+ const a = s.closest(".modal-result-image-container");
3292
+ a?.querySelector(".modal-result-image-loading")?.remove();
3293
+ const n = a?.querySelector(
3294
+ ".modal-result-image-placeholder"
3295
+ );
3296
+ n && (n.style.display = "flex"), s.style.display = "none";
3297
+ });
3298
+ });
3299
+ }
3300
+ renderEmptyState() {
3301
+ return `
3302
+ <div class="modal-empty">
3303
+ <svg class="modal-empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
3304
+ <circle cx="11" cy="11" r="8"></circle>
3305
+ <path d="m21 21-4.35-4.35"></path>
3306
+ </svg>
3307
+ <div class="modal-empty-description">Start typing to search</div>
3308
+ </div>
3309
+ `;
3310
+ }
3311
+ showEmptyState() {
3312
+ this.resultsContainer && (this.resultsContainer.innerHTML = this.renderEmptyState(), this.footerCount && (this.footerCount.textContent = ""), this.inputElement && this.inputElement.setAttribute("aria-expanded", "false"));
3313
+ }
3314
+ showLoadingState() {
3315
+ this.resultsContainer && (this.resultsContainer.innerHTML = `
3316
+ <div class="modal-loading">
3317
+ <div class="loading" aria-label="Loading"></div>
3318
+ <div>Searching...</div>
3319
+ </div>
3320
+ `, this.footerCount && (this.footerCount.textContent = "Searching..."));
3321
+ }
3322
+ showNoResultsState(e) {
3323
+ this.resultsContainer && (this.resultsContainer.innerHTML = `
3324
+ <div class="modal-empty">
3325
+ <svg class="modal-empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
3326
+ <circle cx="11" cy="11" r="8"></circle>
3327
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
3328
+ </svg>
3329
+ <div class="modal-empty-title">No results found</div>
3330
+ <div class="modal-empty-description">No results for "${l(e)}"</div>
3331
+ </div>
3332
+ `, this.footerCount && (this.footerCount.textContent = "0 results"), this.inputElement && this.inputElement.setAttribute("aria-expanded", "false"));
3333
+ }
3334
+ showErrorState(e) {
3335
+ this.resultsContainer && (this.resultsContainer.innerHTML = `
3336
+ <div class="error">
3337
+ <strong>Error:</strong> ${l(e)}
3338
+ </div>
3339
+ `, this.footerCount && (this.footerCount.textContent = "Error"));
3340
+ }
3341
+ updateTheme(e) {
3342
+ const t = e === "light" || e === "dark" || e === "auto" ? e : "auto";
3343
+ t === "auto" ? this.removeAttribute("theme") : this.setAttribute("theme", t);
3344
+ }
3345
+ lockBodyScroll() {
3346
+ const e = window.scrollY;
3347
+ this.savedBodyStyles = {
3348
+ overflow: document.body.style.overflow,
3349
+ position: document.body.style.position,
3350
+ top: document.body.style.top,
3351
+ width: document.body.style.width
3352
+ }, this.savedHtmlOverflow = document.documentElement.style.overflow, document.documentElement.style.overflow = "hidden", document.body.style.overflow = "hidden", document.body.style.position = "fixed", document.body.style.top = `-${e}px`, document.body.style.width = "100%";
3353
+ }
3354
+ unlockBodyScroll() {
3355
+ if (!this.savedBodyStyles) return;
3356
+ const e = Math.abs(Number.parseInt(document.body.style.top || "0", 10));
3357
+ document.documentElement.style.overflow = this.savedHtmlOverflow || "", document.body.style.overflow = this.savedBodyStyles.overflow, document.body.style.position = this.savedBodyStyles.position, document.body.style.top = this.savedBodyStyles.top, document.body.style.width = this.savedBodyStyles.width, window.scrollTo(0, e), this.savedBodyStyles = null, this.savedHtmlOverflow = null;
3358
+ }
3359
+ cleanup() {
3360
+ this.handleGlobalKeydown && (document.removeEventListener("keydown", this.handleGlobalKeydown), this.handleGlobalKeydown = null), this.inputElement && (this.handleInputChange && this.inputElement.removeEventListener("input", this.handleInputChange), this.handleInputKeydown && this.inputElement.removeEventListener("keydown", this.handleInputKeydown)), this.backdrop && this.handleBackdropClick && this.backdrop.removeEventListener("click", this.handleBackdropClick), this.handleInputChange = null, this.handleInputKeydown = null, this.handleBackdropClick = null, this.client && this.client.cancelAllRequests();
3361
+ }
3362
+ // Public API
3363
+ /**
3364
+ * Open the search modal
3365
+ */
3366
+ open() {
3367
+ this.isOpen || (this.isOpen = !0, this.backdrop?.classList.add("open"), this.modal?.classList.add("open"), requestAnimationFrame(() => {
3368
+ requestAnimationFrame(() => {
3369
+ this.inputElement?.focus();
3370
+ });
3371
+ }), this.lockBodyScroll(), this.dispatchEvent(m("open", void 0)));
3372
+ }
3373
+ /**
3374
+ * Close the search modal
3375
+ */
3376
+ close() {
3377
+ this.isOpen && (this.isOpen = !1, this.backdrop?.classList.remove("open"), this.modal?.classList.remove("open"), this.inputElement && (this.inputElement.value = ""), this.results = [], this.activeIndex = -1, this.showEmptyState(), this.unlockBodyScroll(), this.dispatchEvent(m("close", void 0)));
3378
+ }
3379
+ /**
3380
+ * Toggle the search modal open/closed
3381
+ */
3382
+ toggle() {
3383
+ this.isOpen ? this.close() : this.open();
3384
+ }
3385
+ /**
3386
+ * Perform a search programmatically
3387
+ */
3388
+ async search(e) {
3389
+ this.isOpen || this.open(), this.inputElement && (this.inputElement.value = e), await this.performSearch(e);
3390
+ }
3391
+ /**
3392
+ * Get current search results
3393
+ */
3394
+ getResults() {
3395
+ return [...this.results];
3396
+ }
3397
+ /**
3398
+ * Check if modal is currently open
3399
+ */
3400
+ isModalOpen() {
3401
+ return this.isOpen;
3402
+ }
3403
+ }
3404
+ customElements.get(I) || customElements.define(I, Y);
3405
+ export {
3406
+ P as AISearchClient,
3407
+ O as ChatBubbleSnippet,
3408
+ D as ChatPageSnippet,
3409
+ F as SearchBarSnippet,
3410
+ Y as SearchModalSnippet,
3411
+ F as default
3412
+ };
3413
+ //# sourceMappingURL=search-snippet.es.js.map