vanilla-agent 1.9.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,7 +26,6 @@ import {
26
26
  initAgentWidget,
27
27
  createAgentExperience,
28
28
  markdownPostprocessor,
29
- directivePostprocessor,
30
29
  DEFAULT_WIDGET_CONFIG
31
30
  } from 'vanilla-agent';
32
31
 
@@ -61,13 +60,10 @@ const controller = initAgentWidget({
61
60
  }
62
61
  });
63
62
 
63
+ // Runtime theme update
64
64
  document.querySelector('#dark-mode')?.addEventListener('click', () => {
65
65
  controller.update({ theme: { surface: '#0f172a', primary: '#f8fafc' } });
66
66
  });
67
- controller.update({
68
- postprocessMessage: ({ text, streaming }) =>
69
- streaming ? markdownPostprocessor(text) : directivePostprocessor(text)
70
- });
71
67
  ```
72
68
 
73
69
  ### Initialization options
@@ -238,10 +234,62 @@ The client simply sends messages to the proxy, which constructs the full Travrse
238
234
  - Enforce security and cost controls centrally
239
235
  - Support multiple flows for different use cases
240
236
 
241
- ### Directive postprocessor
237
+ ### Dynamic Forms (Recommended)
238
+
239
+ For rendering AI-generated forms, use the **component middleware** approach with the `DynamicForm` component. This allows the AI to create contextually appropriate forms with any fields:
240
+
241
+ ```typescript
242
+ import { componentRegistry, initAgentWidget } from "vanilla-agent";
243
+ import { DynamicForm } from "./components"; // Your DynamicForm component
244
+
245
+ // Register the component
246
+ componentRegistry.register("DynamicForm", DynamicForm);
247
+
248
+ initAgentWidget({
249
+ target: "#app",
250
+ config: {
251
+ apiUrl: "/api/chat/dispatch-directive",
252
+ parserType: "json",
253
+ enableComponentStreaming: true,
254
+ formEndpoint: "/form",
255
+ // Optional: customize form appearance
256
+ formStyles: {
257
+ borderRadius: "16px",
258
+ borderWidth: "1px",
259
+ borderColor: "#e5e7eb",
260
+ padding: "1.5rem",
261
+ titleFontSize: "1.25rem",
262
+ buttonBorderRadius: "9999px"
263
+ }
264
+ }
265
+ });
266
+ ```
267
+
268
+ The AI responds with JSON like:
269
+
270
+ ```json
271
+ {
272
+ "text": "Please fill out this form:",
273
+ "component": "DynamicForm",
274
+ "props": {
275
+ "title": "Contact Us",
276
+ "fields": [
277
+ { "label": "Name", "type": "text", "required": true },
278
+ { "label": "Email", "type": "email", "required": true }
279
+ ],
280
+ "submit_text": "Submit"
281
+ }
282
+ }
283
+ ```
284
+
285
+ See `examples/embedded-app/json.html` for a full working example.
286
+
287
+ ### Directive postprocessor (Deprecated)
288
+
289
+ > **⚠️ Deprecated:** The `directivePostprocessor` approach is deprecated in favor of the component middleware with `DynamicForm`. The old approach only supports predefined form templates ("init" and "followup"), while the new approach allows AI-generated forms with any fields.
242
290
 
243
291
  `directivePostprocessor` looks for either `<Form type="init" />` tokens or
244
- `<Directive>{"component":"form","type":"init"}</Directive>` blocks and swaps them for placeholders that the widget upgrades into interactive UI (forms, cards, etc.). See `examples/embedded-app/json.html` for a full working example that submits to the proxy’s `/form` endpoint and posts a follow-up message back into the chat.
292
+ `<Directive>{"component":"form","type":"init"}</Directive>` blocks and swaps them for placeholders that the widget upgrades into interactive UI. This approach is limited to the predefined form templates in `formDefinitions`.
245
293
 
246
294
  ### Script tag installation
247
295
 
@@ -357,7 +405,254 @@ The script build exposes a `window.AgentWidget` global with `initAgentWidget()`
357
405
  - `window.AgentWidget.createJsonStreamParser()` - JSON parser using schema-stream
358
406
  - `window.AgentWidget.createXmlParser()` - XML parser
359
407
  - `window.AgentWidget.markdownPostprocessor()` - Markdown postprocessor
360
- - `window.AgentWidget.directivePostprocessor()` - Directive postprocessor
408
+ - `window.AgentWidget.directivePostprocessor()` - Directive postprocessor *(deprecated)*
409
+ - `window.AgentWidget.componentRegistry` - Component registry for custom components
410
+
411
+ ### React Framework Integration
412
+
413
+ The widget is fully compatible with React frameworks. Use the ESM imports to integrate it as a client component.
414
+
415
+ #### Framework Compatibility
416
+
417
+ | Framework | Compatible | Implementation Notes |
418
+ |-----------|------------|---------------------|
419
+ | **Vite** | ✅ Yes | No special requirements - works out of the box |
420
+ | **Create React App** | ✅ Yes | No special requirements - works out of the box |
421
+ | **Next.js** | ✅ Yes | Requires `'use client'` directive (App Router) |
422
+ | **Remix** | ✅ Yes | Use dynamic import or `useEffect` guard for SSR |
423
+ | **Gatsby** | ✅ Yes | Use in `wrapRootElement` or check `typeof window !== 'undefined'` |
424
+ | **Astro** | ✅ Yes | Use `client:load` or `client:only="react"` directive |
425
+
426
+ #### Quick Start with Vite or Create React App
427
+
428
+ For client-side-only React frameworks (Vite, CRA), create a component:
429
+
430
+ ```typescript
431
+ // src/components/ChatWidget.tsx
432
+ import { useEffect } from 'react';
433
+ import 'vanilla-agent/widget.css';
434
+ import { initAgentWidget, markdownPostprocessor } from 'vanilla-agent';
435
+ import type { AgentWidgetInitHandle } from 'vanilla-agent';
436
+
437
+ export function ChatWidget() {
438
+ useEffect(() => {
439
+ let handle: AgentWidgetInitHandle | null = null;
440
+
441
+ handle = initAgentWidget({
442
+ target: 'body',
443
+ config: {
444
+ apiUrl: "/api/chat/dispatch",
445
+ theme: {
446
+ primary: "#111827",
447
+ accent: "#1d4ed8",
448
+ },
449
+ launcher: {
450
+ enabled: true,
451
+ title: "Chat Assistant",
452
+ subtitle: "Here to help you get answers fast"
453
+ },
454
+ postprocessMessage: ({ text }) => markdownPostprocessor(text)
455
+ }
456
+ });
457
+
458
+ // Cleanup on unmount
459
+ return () => {
460
+ if (handle) {
461
+ handle.destroy();
462
+ }
463
+ };
464
+ }, []);
465
+
466
+ return null; // Widget injects itself into the DOM
467
+ }
468
+ ```
469
+
470
+ Then use it in your app:
471
+
472
+ ```typescript
473
+ // src/App.tsx
474
+ import { ChatWidget } from './components/ChatWidget';
475
+
476
+ function App() {
477
+ return (
478
+ <div>
479
+ {/* Your app content */}
480
+ <ChatWidget />
481
+ </div>
482
+ );
483
+ }
484
+
485
+ export default App;
486
+ ```
487
+
488
+ #### Next.js Integration
489
+
490
+ For Next.js App Router, add the `'use client'` directive:
491
+
492
+ ```typescript
493
+ // components/ChatWidget.tsx
494
+ 'use client';
495
+
496
+ import { useEffect } from 'react';
497
+ import 'vanilla-agent/widget.css';
498
+ import { initAgentWidget, markdownPostprocessor } from 'vanilla-agent';
499
+ import type { AgentWidgetInitHandle } from 'vanilla-agent';
500
+
501
+ export function ChatWidget() {
502
+ useEffect(() => {
503
+ let handle: AgentWidgetInitHandle | null = null;
504
+
505
+ handle = initAgentWidget({
506
+ target: 'body',
507
+ config: {
508
+ apiUrl: "/api/chat/dispatch",
509
+ launcher: {
510
+ enabled: true,
511
+ title: "Chat Assistant",
512
+ },
513
+ postprocessMessage: ({ text }) => markdownPostprocessor(text)
514
+ }
515
+ });
516
+
517
+ return () => {
518
+ if (handle) {
519
+ handle.destroy();
520
+ }
521
+ };
522
+ }, []);
523
+
524
+ return null;
525
+ }
526
+ ```
527
+
528
+ Use it in your layout or page:
529
+
530
+ ```typescript
531
+ // app/layout.tsx
532
+ import { ChatWidget } from '@/components/ChatWidget';
533
+
534
+ export default function RootLayout({ children }) {
535
+ return (
536
+ <html lang="en">
537
+ <body>
538
+ {children}
539
+ <ChatWidget />
540
+ </body>
541
+ </html>
542
+ );
543
+ }
544
+ ```
545
+
546
+ **Alternative: Dynamic Import (SSR-Safe)**
547
+
548
+ If you encounter SSR issues, use Next.js dynamic imports:
549
+
550
+ ```typescript
551
+ // app/layout.tsx
552
+ import dynamic from 'next/dynamic';
553
+
554
+ const ChatWidget = dynamic(
555
+ () => import('@/components/ChatWidget').then(mod => mod.ChatWidget),
556
+ { ssr: false }
557
+ );
558
+
559
+ export default function RootLayout({ children }) {
560
+ return (
561
+ <html lang="en">
562
+ <body>
563
+ {children}
564
+ <ChatWidget />
565
+ </body>
566
+ </html>
567
+ );
568
+ }
569
+ ```
570
+
571
+ #### Remix Integration
572
+
573
+ For Remix, guard the widget initialization with a client-side check:
574
+
575
+ ```typescript
576
+ // app/components/ChatWidget.tsx
577
+ import { useEffect, useState } from 'react';
578
+
579
+ export function ChatWidget() {
580
+ const [mounted, setMounted] = useState(false);
581
+
582
+ useEffect(() => {
583
+ setMounted(true);
584
+
585
+ // Dynamic import to avoid SSR issues
586
+ import('vanilla-agent/widget.css');
587
+ import('vanilla-agent').then(({ initAgentWidget, markdownPostprocessor }) => {
588
+ const handle = initAgentWidget({
589
+ target: 'body',
590
+ config: {
591
+ apiUrl: "/api/chat/dispatch",
592
+ launcher: { enabled: true },
593
+ postprocessMessage: ({ text }) => markdownPostprocessor(text)
594
+ }
595
+ });
596
+
597
+ return () => handle?.destroy();
598
+ });
599
+ }, []);
600
+
601
+ if (!mounted) return null;
602
+ return null;
603
+ }
604
+ ```
605
+
606
+ #### Gatsby Integration
607
+
608
+ Use Gatsby's `wrapRootElement` API:
609
+
610
+ ```typescript
611
+ // gatsby-browser.js
612
+ import { ChatWidget } from './src/components/ChatWidget';
613
+
614
+ export const wrapRootElement = ({ element }) => (
615
+ <>
616
+ {element}
617
+ <ChatWidget />
618
+ </>
619
+ );
620
+ ```
621
+
622
+ #### Astro Integration
623
+
624
+ Use Astro's client directives with React islands:
625
+
626
+ ```astro
627
+ ---
628
+ // src/components/ChatWidget.astro
629
+ import { ChatWidget } from './ChatWidget.tsx';
630
+ ---
631
+
632
+ <ChatWidget client:load />
633
+ ```
634
+
635
+ #### Using the Theme Configurator
636
+
637
+ For easy configuration generation, use the [Theme Configurator](https://github.com/becomevocal/chaty/tree/main/examples/embedded-app) which includes a "React (Client Component)" export option. It generates a complete React component with your custom theme, launcher settings, and all configuration options.
638
+
639
+ #### Installation
640
+
641
+ ```bash
642
+ npm install vanilla-agent
643
+ # or
644
+ pnpm add vanilla-agent
645
+ # or
646
+ yarn add vanilla-agent
647
+ ```
648
+
649
+ #### Key Considerations
650
+
651
+ 1. **CSS Import**: The CSS import (`import 'vanilla-agent/widget.css'`) works natively with all modern React build tools
652
+ 2. **Client-Side Only**: The widget manipulates the DOM, so it must run client-side only
653
+ 3. **Cleanup**: Always call `handle.destroy()` in the cleanup function to prevent memory leaks
654
+ 4. **API Routes**: Ensure your `apiUrl` points to a valid backend endpoint
655
+ 5. **TypeScript Support**: Full TypeScript definitions are included for all exports
361
656
 
362
657
  ### Using default configuration
363
658