@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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Cloudflare, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,573 @@
1
+ # ๐Ÿ” Search Snippet Library
2
+
3
+ A production-ready, self-contained TypeScript Web Component library providing both search and chat interfaces with streaming support. Zero dependencies, fully customizable, and framework-agnostic.
4
+
5
+ ## โœจ Features
6
+
7
+ - **๐ŸŽจ Fully Customizable** - 20+ CSS variables for complete theme control
8
+ - **โšก Zero Dependencies** - Self-contained with everything bundled
9
+ - **๐Ÿ“ฑ Responsive Design** - Works seamlessly on all devices
10
+ - **๐ŸŽญ Framework Agnostic** - Native Web Components work everywhere
11
+ - **โ™ฟ Accessible** - WCAG 2.1 AA compliant with ARIA support
12
+ - **๐Ÿš€ Streaming Support** - Real-time streaming responses with low latency
13
+ - **๐ŸŒ“ Dark Mode** - Automatic theme switching based on system preferences
14
+ - **๐Ÿ“ฆ Tiny Bundle** - Optimized for minimal size (< 50KB gzipped)
15
+ - **๐Ÿ”’ Secure** - XSS protection with HTML sanitization
16
+ - **โš™๏ธ TypeScript** - Full type definitions included
17
+
18
+ ## ๐Ÿš€ Quick Start
19
+
20
+ > **Note:** This library requires an active AI Search endpoint in your Cloudflare Dashboard. Make sure to enable and configure your AI Search service before using these components.
21
+
22
+ ### Installation
23
+
24
+ ```bash
25
+ npm install @cloudflare/ai-search-snippet
26
+ # or
27
+ yarn add @cloudflare/ai-search-snippet
28
+ ```
29
+
30
+ ### Basic Usage
31
+
32
+ > **Note:** Replace `<hash>` with your Cloudflare AI Search endpoint hash (you can find it in the Cloudflare Dashboard).
33
+
34
+ ```html
35
+ <!-- Import the library -->
36
+ <script
37
+ type="module"
38
+ src="https:/<hash>/search.ai.cloudflare.com/search-snippet.es.js"
39
+ ></script>
40
+
41
+ <!-- Search bar with results -->
42
+ <search-bar-snippet
43
+ api-url="https:/<hash>/search.ai.cloudflare.com/"
44
+ placeholder="Search..."
45
+ max-results="10"
46
+ >
47
+ </search-bar-snippet>
48
+
49
+ <!-- Modal search (opens with Cmd/Ctrl+K) -->
50
+ <search-modal-snippet
51
+ api-url="https:/<hash>/search.ai.cloudflare.com/"
52
+ placeholder="Search documentation..."
53
+ max-results="10"
54
+ >
55
+ </search-modal-snippet>
56
+
57
+ <!-- Floating chat bubble -->
58
+ <chat-bubble-snippet
59
+ api-url="https:/<hash>/search.ai.cloudflare.com/"
60
+ placeholder="Type a message..."
61
+ >
62
+ </chat-bubble-snippet>
63
+
64
+ <!-- Full-page chat with history -->
65
+ <chat-page-snippet
66
+ api-url="https:/<hash>/search.ai.cloudflare.com/"
67
+ placeholder="Type a message..."
68
+ >
69
+ </chat-page-snippet>
70
+ ```
71
+
72
+ ## ๐Ÿ“– API Reference
73
+
74
+ ### Components
75
+
76
+ The library provides four Web Components:
77
+
78
+ | Component | Tag | Description |
79
+ | -------------------- | ------------------------ | ------------------------------------- |
80
+ | `SearchBarSnippet` | `<search-bar-snippet>` | Search input with results dropdown |
81
+ | `SearchModalSnippet` | `<search-modal-snippet>` | Modal search with Cmd/Ctrl+K shortcut |
82
+ | `ChatBubbleSnippet` | `<chat-bubble-snippet>` | Floating chat bubble overlay |
83
+ | `ChatPageSnippet` | `<chat-page-snippet>` | Full-page chat with session history |
84
+
85
+ ### Common Attributes
86
+
87
+ These attributes are available on all components:
88
+
89
+ | Attribute | Type | Default | Description |
90
+ | --------------- | ----------------------------- | ----------------------- | ------------------------------ |
91
+ | `api-url` | string | `http://localhost:3000` | API endpoint URL |
92
+ | `placeholder` | string | Component-specific | Input placeholder text |
93
+ | `theme` | `'light' \| 'dark' \| 'auto'` | `'auto'` | Color scheme |
94
+ | `hide-branding` | boolean | `false` | Hide the "Powered by" branding |
95
+
96
+ ### Search Components Attributes
97
+
98
+ Additional attributes for `<search-bar-snippet>` and `<search-modal-snippet>`:
99
+
100
+ | Attribute | Type | Default | Description |
101
+ | ------------- | ------ | ------- | ------------------------------------ |
102
+ | `max-results` | number | `10` | Maximum search results to display |
103
+ | `debounce-ms` | number | `300` | Input debounce delay in milliseconds |
104
+
105
+ ### Modal-Specific Attributes
106
+
107
+ Additional attributes for `<search-modal-snippet>`:
108
+
109
+ | Attribute | Type | Default | Description |
110
+ | -------------- | ------- | ------- | ------------------------------------------ |
111
+ | `shortcut` | string | `'k'` | Keyboard shortcut key (with Cmd/Ctrl) |
112
+ | `use-meta-key` | boolean | `true` | Use meta key (Cmd on Mac, Ctrl on Windows) |
113
+
114
+ ### JavaScript API
115
+
116
+ #### Search Bar (`<search-bar-snippet>`)
117
+
118
+ ```typescript
119
+ const searchBar = document.querySelector("search-bar-snippet");
120
+
121
+ // Perform a search programmatically
122
+ searchBar.search("query");
123
+ ```
124
+
125
+ #### Search Modal (`<search-modal-snippet>`)
126
+
127
+ ```typescript
128
+ const modal = document.querySelector("search-modal-snippet");
129
+
130
+ modal.open(); // Open the modal
131
+ modal.close(); // Close the modal
132
+ modal.toggle(); // Toggle open/closed
133
+ modal.search("query"); // Open and search
134
+ const results = modal.getResults(); // Get current results
135
+ const isOpen = modal.isModalOpen(); // Check if open
136
+ ```
137
+
138
+ #### Chat Bubble (`<chat-bubble-snippet>`)
139
+
140
+ ```typescript
141
+ const chatBubble = document.querySelector("chat-bubble-snippet");
142
+
143
+ await chatBubble.sendMessage("Hello!"); // Send a message
144
+ const messages = chatBubble.getMessages(); // Get message history
145
+ chatBubble.clearChat(); // Clear chat history
146
+ ```
147
+
148
+ #### Chat Page (`<chat-page-snippet>`)
149
+
150
+ ```typescript
151
+ const chatPage = document.querySelector("chat-page-snippet");
152
+
153
+ await chatPage.sendMessage("Hello!"); // Send a message
154
+ const messages = chatPage.getMessages(); // Get message history
155
+ chatPage.clearChat(); // Clear current chat
156
+ const sessions = chatPage.getSessions(); // Get all chat sessions
157
+ const current = chatPage.getCurrentSession(); // Get current session
158
+ ```
159
+
160
+ ### Events
161
+
162
+ #### Common Events (all components)
163
+
164
+ ```javascript
165
+ const component = document.querySelector("search-bar-snippet");
166
+
167
+ component.addEventListener("ready", () => {
168
+ console.log("Component ready");
169
+ });
170
+
171
+ component.addEventListener("error", (e) => {
172
+ console.error("Error:", e.detail.error);
173
+ });
174
+ ```
175
+
176
+ #### Modal-Specific Events
177
+
178
+ ```javascript
179
+ const modal = document.querySelector("search-modal-snippet");
180
+
181
+ modal.addEventListener("open", () => {
182
+ console.log("Modal opened");
183
+ });
184
+
185
+ modal.addEventListener("close", () => {
186
+ console.log("Modal closed");
187
+ });
188
+
189
+ modal.addEventListener("result-select", (e) => {
190
+ console.log("Selected result:", e.detail.result);
191
+ });
192
+ ```
193
+
194
+ #### Chat Events
195
+
196
+ ```javascript
197
+ const chat = document.querySelector("chat-bubble-snippet");
198
+
199
+ chat.addEventListener("message", (e) => {
200
+ console.log("New message:", e.detail.message);
201
+ });
202
+ ```
203
+
204
+ ## ๐ŸŽจ Customization
205
+
206
+ ### CSS Variables
207
+
208
+ Customize the appearance using CSS variables:
209
+
210
+ ```css
211
+ search-bar-snippet,
212
+ search-modal-snippet,
213
+ chat-bubble-snippet,
214
+ chat-page-snippet {
215
+ /* ========== COLORS ========== */
216
+ /* Primary */
217
+ --search-snippet-primary-color: #2563eb;
218
+ --search-snippet-primary-hover: #0f51dfff;
219
+
220
+ /* Background & Surface */
221
+ --search-snippet-background: #ffffff;
222
+ --search-snippet-surface: #f8f9fa;
223
+ --search-snippet-hover-background: #f1f3f5;
224
+
225
+ /* Text */
226
+ --search-snippet-text-color: #212529;
227
+ --search-snippet-text-secondary: #6c757d;
228
+
229
+ /* Border & Focus */
230
+ --search-snippet-border-color: #dee2e6;
231
+ --search-snippet-focus-ring: #0066cc40;
232
+
233
+ /* Status Colors */
234
+ --search-snippet-error-color: #dc3545;
235
+ --search-snippet-error-background: #f8d7da;
236
+ --search-snippet-success-color: #28a745;
237
+ --search-snippet-success-background: #d4edda;
238
+ --search-snippet-warning-color: #ffc107;
239
+ --search-snippet-warning-background: #fff3cd;
240
+
241
+ /* Message Colors */
242
+ --search-snippet-user-message-bg: #0066cc;
243
+ --search-snippet-user-message-text: #ffffff;
244
+ --search-snippet-assistant-message-bg: #f1f3f5;
245
+ --search-snippet-assistant-message-text: #212529;
246
+ --search-snippet-system-message-bg: #fff3cd;
247
+ --search-snippet-system-message-text: #856404;
248
+
249
+ /* ========== TYPOGRAPHY ========== */
250
+ --search-snippet-font-family:
251
+ -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue",
252
+ Arial, sans-serif;
253
+ --search-snippet-font-family-mono:
254
+ "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
255
+ --search-snippet-font-size-base: 14px;
256
+ --search-snippet-font-size-sm: 12px;
257
+ --search-snippet-font-size-lg: 16px;
258
+ --search-snippet-font-size-xl: 18px;
259
+ --search-snippet-line-height: 1.5;
260
+ --search-snippet-font-weight-normal: 400;
261
+ --search-snippet-font-weight-medium: 500;
262
+ --search-snippet-font-weight-bold: 600;
263
+
264
+ /* ========== SPACING ========== */
265
+ --search-snippet-spacing-xs: 4px;
266
+ --search-snippet-spacing-sm: 8px;
267
+ --search-snippet-spacing-md: 12px;
268
+ --search-snippet-spacing-lg: 16px;
269
+ --search-snippet-spacing-xl: 24px;
270
+ --search-snippet-spacing-xxl: 32px;
271
+
272
+ /* ========== SIZING ========== */
273
+ --search-snippet-width: 100%;
274
+ --search-snippet-max-width: 100%;
275
+ --search-snippet-min-width: 320px;
276
+ --search-snippet-max-height: 600px;
277
+ --search-snippet-input-height: 44px;
278
+ --search-snippet-button-height: 36px;
279
+ --search-snippet-icon-size: 20px;
280
+ --search-snippet-icon-margin-left: 6px;
281
+
282
+ /* ========== BORDER ========== */
283
+ --search-snippet-border-width: 1px;
284
+ --search-snippet-border-radius: 18px;
285
+
286
+ /* ========== SHADOWS ========== */
287
+ --search-snippet-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
288
+ --search-snippet-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
289
+ --search-snippet-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.15);
290
+ --search-snippet-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.2);
291
+ --search-snippet-shadow-inner: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
292
+ --search-snippet-result-item-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
293
+
294
+ /* ========== ANIMATION ========== */
295
+ --search-snippet-transition-fast: 150ms ease;
296
+ --search-snippet-transition: 200ms ease;
297
+ --search-snippet-transition-slow: 300ms ease;
298
+ --search-snippet-animation-duration: 0.2s;
299
+
300
+ /* ========== Z-INDEX ========== */
301
+ --search-snippet-z-dropdown: 1000;
302
+ --search-snippet-z-modal: 1050;
303
+ --search-snippet-z-popover: 1060;
304
+ --search-snippet-z-tooltip: 1070;
305
+
306
+ /* ========== CHAT BUBBLE SPECIFIC ========== */
307
+ --chat-bubble-button-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
308
+ --chat-bubble-window-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
309
+ --chat-bubble-button-size: 60px;
310
+ --chat-bubble-button-radius: 50%;
311
+ --chat-bubble-button-icon-size: 28px;
312
+ --chat-bubble-button-icon-color: white;
313
+ --chat-bubble-button-bottom: 20px;
314
+ --chat-bubble-button-right: 20px;
315
+ --chat-bubble-button-z-index: 9999;
316
+ --chat-bubble-position: fixed;
317
+ }
318
+ ```
319
+
320
+ ### Theme Examples
321
+
322
+ **Dark Theme:**
323
+
324
+ ```css
325
+ search-bar-snippet {
326
+ --search-snippet-primary-color: #4dabf7;
327
+ --search-snippet-background: #1a1b1e;
328
+ --search-snippet-text-color: #c1c2c5;
329
+ --search-snippet-border-color: #373a40;
330
+ }
331
+ ```
332
+
333
+ **Custom Brand:**
334
+
335
+ ```css
336
+ chat-bubble-snippet {
337
+ --search-snippet-primary-color: #667eea;
338
+ --search-snippet-primary-hover: #5568d3;
339
+ --search-snippet-border-radius: 12px;
340
+ --search-snippet-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
341
+ }
342
+ ```
343
+
344
+ ## ๐Ÿ”ง Advanced Usage
345
+
346
+ ### TypeScript
347
+
348
+ ```typescript
349
+ import {
350
+ SearchBarSnippet,
351
+ SearchModalSnippet,
352
+ ChatBubbleSnippet,
353
+ ChatPageSnippet,
354
+ type SearchSnippetProps,
355
+ } from "@cloudflare/ai-search-snippet";
356
+
357
+ // Type-safe usage
358
+ const searchBar = document.createElement(
359
+ "search-bar-snippet",
360
+ ) as SearchBarSnippet;
361
+ searchBar.setAttribute("api-url", "https://api.example.com");
362
+ searchBar.setAttribute("max-results", "10");
363
+
364
+ const chatBubble = document.createElement(
365
+ "chat-bubble-snippet",
366
+ ) as ChatBubbleSnippet;
367
+ chatBubble.setAttribute("api-url", "https://api.example.com");
368
+ await chatBubble.sendMessage("Hello, world!");
369
+ ```
370
+
371
+ ### React Integration
372
+
373
+ ```tsx
374
+ import { useEffect, useRef } from "react";
375
+ import "@cloudflare/ai-search-snippet";
376
+
377
+ function ChatWidget() {
378
+ const ref = useRef<HTMLElement>(null);
379
+
380
+ useEffect(() => {
381
+ const chat = ref.current;
382
+
383
+ const handleMessage = (e: CustomEvent) => {
384
+ console.log("Message:", e.detail);
385
+ };
386
+
387
+ chat?.addEventListener("message", handleMessage as EventListener);
388
+
389
+ return () => {
390
+ chat?.removeEventListener("message", handleMessage as EventListener);
391
+ };
392
+ }, []);
393
+
394
+ return (
395
+ <chat-bubble-snippet
396
+ ref={ref}
397
+ api-url="https://api.example.com"
398
+ placeholder="Ask a question..."
399
+ />
400
+ );
401
+ }
402
+ ```
403
+
404
+ ### Vue Integration
405
+
406
+ ```vue
407
+ <template>
408
+ <chat-bubble-snippet
409
+ :api-url="apiUrl"
410
+ placeholder="Ask a question..."
411
+ @message="handleMessage"
412
+ @error="handleError"
413
+ />
414
+ </template>
415
+
416
+ <script setup>
417
+ import { ref } from "vue";
418
+ import "@cloudflare/ai-search-snippet";
419
+
420
+ const apiUrl = ref("https://api.example.com");
421
+
422
+ const handleMessage = (event) => {
423
+ console.log("Message:", event.detail.message);
424
+ };
425
+
426
+ const handleError = (event) => {
427
+ console.error("Error:", event.detail.error);
428
+ };
429
+ </script>
430
+ ```
431
+
432
+ ## ๐Ÿ—๏ธ Development
433
+
434
+ ### Build from Source
435
+
436
+ ```bash
437
+ # Install dependencies
438
+ npm install
439
+
440
+ # Development mode with hot reload
441
+ npm run dev
442
+
443
+ # Build for production
444
+ npm run build
445
+
446
+ # Preview production build
447
+ npm run preview
448
+
449
+ # Lint and format
450
+ npm run lint
451
+ npm run format
452
+ ```
453
+
454
+ ### Project Structure
455
+
456
+ ```
457
+ nlweb-cl-snippet/
458
+ โ”œโ”€โ”€ src/
459
+ โ”‚ โ”œโ”€โ”€ api/
460
+ โ”‚ โ”‚ โ”œโ”€โ”€ index.ts # Base Client abstract class
461
+ โ”‚ โ”‚ โ””โ”€โ”€ ai-search.ts # AISearchClient with streaming
462
+ โ”‚ โ”œโ”€โ”€ components/
463
+ โ”‚ โ”‚ โ”œโ”€โ”€ search-bar-snippet.ts # Search input with results
464
+ โ”‚ โ”‚ โ”œโ”€โ”€ search-modal-snippet.ts # Modal search with Cmd/Ctrl+K
465
+ โ”‚ โ”‚ โ”œโ”€โ”€ chat-bubble-snippet.ts # Floating chat bubble
466
+ โ”‚ โ”‚ โ”œโ”€โ”€ chat-page-snippet.ts # Full-page chat with history
467
+ โ”‚ โ”‚ โ””โ”€โ”€ chat-view.ts # Shared chat interface
468
+ โ”‚ โ”œโ”€โ”€ styles/
469
+ โ”‚ โ”‚ โ”œโ”€โ”€ theme.ts # Base styles & CSS variables
470
+ โ”‚ โ”‚ โ”œโ”€โ”€ search.ts # Search-specific styles
471
+ โ”‚ โ”‚ โ””โ”€โ”€ chat.ts # Chat-specific styles
472
+ โ”‚ โ”œโ”€โ”€ types/
473
+ โ”‚ โ”‚ โ””โ”€โ”€ index.ts # TypeScript definitions
474
+ โ”‚ โ”œโ”€โ”€ utils/
475
+ โ”‚ โ”‚ โ””โ”€โ”€ index.ts # Utility functions
476
+ โ”‚ โ””โ”€โ”€ main.ts # Entry point
477
+ โ”œโ”€โ”€ dist/ # Build output
478
+ โ”œโ”€โ”€ index.html # Demo page
479
+ โ”œโ”€โ”€ package.json
480
+ โ”œโ”€โ”€ tsconfig.json
481
+ โ”œโ”€โ”€ vite.config.ts
482
+ โ””โ”€โ”€ README.md
483
+ ```
484
+
485
+ ## ๐Ÿ“ API Server Requirements
486
+
487
+ The component expects the API server to implement the following endpoints:
488
+
489
+ ### Search Endpoint
490
+
491
+ **POST** `/search`
492
+
493
+ Request:
494
+
495
+ ```json
496
+ {
497
+ "query": "search query",
498
+ "max_results": 10,
499
+ "filters": {}
500
+ }
501
+ ```
502
+
503
+ Response:
504
+
505
+ ```json
506
+ {
507
+ "results": [
508
+ {
509
+ "id": "result-1",
510
+ "title": "Result Title",
511
+ "snippet": "Result description...",
512
+ "url": "https://example.com",
513
+ "metadata": {}
514
+ }
515
+ ],
516
+ "total": 42
517
+ }
518
+ ```
519
+
520
+ ### Chat Endpoint (Streaming)
521
+
522
+ **POST** `/ask`
523
+
524
+ Request:
525
+
526
+ ```json
527
+ {
528
+ "query": "user message",
529
+ "generate_mode": "summarize",
530
+ "prev": [
531
+ {
532
+ "role": "user",
533
+ "content": "previous message",
534
+ "timestamp": 1234567890
535
+ }
536
+ ]
537
+ }
538
+ ```
539
+
540
+ Response: Streaming text chunks via ReadableStream
541
+
542
+ ## ๐Ÿงช Browser Support
543
+
544
+ - Chrome 90+
545
+ - Firefox 88+
546
+ - Safari 14+
547
+ - Edge 90+
548
+
549
+ ## ๐Ÿ“„ License
550
+
551
+ MIT License - see LICENSE file for details
552
+
553
+ ## ๐Ÿค Contributing
554
+
555
+ Contributions are welcome! Please feel free to submit a Pull Request.
556
+
557
+ ## ๐Ÿ› Bug Reports
558
+
559
+ If you discover any bugs, please create an issue on GitHub with:
560
+
561
+ - Description of the bug
562
+ - Steps to reproduce
563
+ - Expected behavior
564
+ - Actual behavior
565
+ - Browser and version
566
+
567
+ ## ๐Ÿ“ฎ Support
568
+
569
+ For questions and support, please open a GitHub issue.
570
+
571
+ ---
572
+
573
+ Built with TypeScript, Web Components, and โค๏ธ