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 +303 -8
- package/dist/index.cjs +46 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +581 -1
- package/dist/index.d.ts +581 -1
- package/dist/index.global.js +69 -30
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +46 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +141 -12
- package/src/components/composer-builder.ts +366 -0
- package/src/components/forms.ts +1 -0
- package/src/components/header-builder.ts +454 -0
- package/src/components/header-layouts.ts +303 -0
- package/src/components/message-bubble.ts +251 -34
- package/src/components/panel.ts +46 -684
- package/src/components/registry.ts +87 -0
- package/src/defaults.ts +49 -1
- package/src/index.ts +64 -2
- package/src/plugins/registry.ts +1 -0
- package/src/plugins/types.ts +1 -0
- package/src/runtime/init.ts +26 -0
- package/src/types.ts +381 -0
- package/src/ui.ts +521 -40
- package/src/utils/component-middleware.ts +137 -0
- package/src/utils/component-parser.ts +119 -0
- package/src/utils/constants.ts +1 -0
- package/src/utils/dom.ts +1 -0
- package/src/utils/formatting.ts +33 -8
- package/src/utils/positioning.ts +1 -0
- package/src/utils/theme.ts +1 -0
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
|
-
###
|
|
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
|
|
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
|
|