@dudousxd/nestjs-agent-react 0.13.0 → 0.15.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 +136 -2
- package/dist/genui-json-render.cjs +93 -0
- package/dist/genui-json-render.cjs.map +1 -0
- package/dist/genui-json-render.d.cts +34 -0
- package/dist/genui-json-render.d.ts +34 -0
- package/dist/genui-json-render.js +67 -0
- package/dist/genui-json-render.js.map +1 -0
- package/dist/genui.cjs +450 -0
- package/dist/genui.cjs.map +1 -0
- package/dist/genui.d.cts +62 -0
- package/dist/genui.d.ts +62 -0
- package/dist/genui.js +420 -0
- package/dist/genui.js.map +1 -0
- package/dist/index.cjs +970 -432
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +343 -609
- package/dist/index.d.ts +343 -609
- package/dist/index.js +931 -399
- package/dist/index.js.map +1 -1
- package/dist/markdown.d.cts +2 -2
- package/dist/markdown.d.ts +2 -2
- package/dist/model-gHqjNjLJ.d.cts +561 -0
- package/dist/model-gHqjNjLJ.d.ts +561 -0
- package/dist/types-DFD-soWU.d.cts +86 -0
- package/dist/types-DFD-soWU.d.ts +86 -0
- package/package.json +30 -3
package/README.md
CHANGED
|
@@ -111,7 +111,7 @@ function Chat() {
|
|
|
111
111
|
| On the instance | What it is |
|
|
112
112
|
|---|---|
|
|
113
113
|
| `items` | The mounted window, oldest first. Each item carries `blocks`, `text`, `usage`, `timestamp`, `isStreaming`, `isLastAssistant`, and its action machines. |
|
|
114
|
-
| `items[i].blocks` | `{ kind: 'text' \| 'reasoning' \| 'tools' \| 'sources' \| 'elicitation' \| 'ui' }`. A `ui` block is a component the server pushed (`{ id, component, props, version }`) — look `component` up in your own registry. A `tools` block is a run of CONSECUTIVE tool parts — any other part between two calls (including a `step-start` marker) ends the run. A `reasoning` block carries `isOpen`/`toggle`, open while it streams. A `sources` block appears only under `sources: true`, an `elicitation` block only under `onAnswer` — see below. |
|
|
114
|
+
| `items[i].blocks` | `{ kind: 'text' \| 'reasoning' \| 'tools' \| 'sources' \| 'elicitation' \| 'ui' }`. A `ui` block is a component the server pushed (`{ id, component, props, version, toolCallId }`) — draw it with `<GenerativeUI>` (below) or look `component` up in your own registry. A `tools` block is a run of CONSECUTIVE tool parts — any other part between two calls (including a `step-start` marker) ends the run. A `reasoning` block carries `isOpen`/`toggle`, open while it streams. A `sources` block appears only under `sources: true`, an `elicitation` block only under `onAnswer` — see below. |
|
|
115
115
|
| `items[i].blocks[n]` (`tools`) | Also carries `calls`: the same parts, each with `{ toolCallId, name, toolKind, parentId, children, approval, isAwaitingApproval, approve, reject, error }`, and `roots`: the same calls as a tree (a call nested under another by the stream's `parentId` sits in its parent's `children`). `approval` is `{ approver, expiresAt, reason, status, remember, decidedBy, decidedVia, decisionReason }` when the runner said who has to decide, else `null` — `status` is `pending` / `approved` / `rejected` / `expired`. |
|
|
116
116
|
| `items[i].copy` | `{ available, copied, copy() }` — `copied` flashes for `copyResetMs` (default 1500). |
|
|
117
117
|
| `items[i].edit` | `{ available, isEditing, draft, canSave, start(), cancel(), setDraft(), save(), getTextareaProps() }`. The prop-getter focuses with the caret at the end, saves on Enter, cancels on Escape. |
|
|
@@ -348,6 +348,51 @@ type-ahead narrows the list it received; it never re-derives which skills apply.
|
|
|
348
348
|
The shadcn `ChatComposer` wires all of this for you: pass `autocompleteSources` and it renders the
|
|
349
349
|
menu (`ChatCommandPalette`, grown into the combobox's listbox) above the composer card.
|
|
350
350
|
|
|
351
|
+
### Generative UI (`/genui`)
|
|
352
|
+
|
|
353
|
+
`@dudousxd/nestjs-agent-react/genui` draws server-pushed components (`ui` frames, persisted `ui[]`)
|
|
354
|
+
with YOUR renderers. Headless: it adds no element and no style — only what your registry renders.
|
|
355
|
+
|
|
356
|
+
```tsx
|
|
357
|
+
import { GenerativeUI, type GenuiRegistry } from '@dudousxd/nestjs-agent-react/genui';
|
|
358
|
+
import { catalog } from './catalog'; // optional: a @dudousxd/nestjs-agent-genui catalog
|
|
359
|
+
|
|
360
|
+
const registry: GenuiRegistry = { DataTable: MyTable, Chart: MyChart, Card: MyCard, Text: MyText };
|
|
361
|
+
|
|
362
|
+
<MessageItem
|
|
363
|
+
message={message}
|
|
364
|
+
renderUi={(block) => (
|
|
365
|
+
<GenerativeUI
|
|
366
|
+
part={block}
|
|
367
|
+
registry={registry}
|
|
368
|
+
catalog={catalog}
|
|
369
|
+
resolveComponent={(name, version) => loadTenantComponent(name, version)}
|
|
370
|
+
fallback={({ reason, item }) => <UnknownComponent name={item.component} reason={reason} />}
|
|
371
|
+
loading={<Skeleton />}
|
|
372
|
+
/>
|
|
373
|
+
)}
|
|
374
|
+
/>;
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
| Prop | |
|
|
378
|
+
|---|---|
|
|
379
|
+
| `part` | A transcript `ui` block, a `data-ui` message part, or a stored `{ id, component, props, version? }`. |
|
|
380
|
+
| `registry` | Component name → your renderer. It receives the props spread (and `children` as a tree layout node). |
|
|
381
|
+
| `catalog?` | Validate props before drawing (`catalog.validateSync` when it can, so no placeholder flashes). Components the catalog does not know render unvalidated. |
|
|
382
|
+
| `resolveComponent?(name, version)` | For components the registry lacks, e.g. a tenant's own, at the exact version a message was rendered with. Sync or async, cached per resolver/name/version. |
|
|
383
|
+
| `fallback?` | A node, or `({ reason: 'unknown' \| 'invalid' \| 'error', item, issues?, error? }) => node`. Default: nothing. |
|
|
384
|
+
| `loading?`, `onError?` | While a resolver or async validation is pending; a renderer threw (each item has its own error boundary). |
|
|
385
|
+
|
|
386
|
+
A `genui:tree` frame (tree mode) is drawn node by node through the same registry, catalog and
|
|
387
|
+
fallbacks, each node in its own boundary; put a `genui:tree` entry in the registry to take over.
|
|
388
|
+
`useGenerativeUI(part, options)` returns the state (`ready` with `Component`/`props`, `loading`,
|
|
389
|
+
`problem`) for your own chrome; wrap it in `<GenerativeUIScope>` when it may draw a tree.
|
|
390
|
+
|
|
391
|
+
**json-render** (optional peer `@json-render/react` >= 0.21):
|
|
392
|
+
`@dudousxd/nestjs-agent-react/genui/json-render` renders tree frames through json-render's
|
|
393
|
+
`Renderer` — `registry[GENUI_TREE_COMPONENT] = jsonRenderTree(toJsonRenderRegistry(registry))` —
|
|
394
|
+
plus `JsonRenderTree` and `treeToJsonRenderSpec`.
|
|
395
|
+
|
|
351
396
|
### Designed components, as copy-in source
|
|
352
397
|
|
|
353
398
|
`MessageList`/`MessageItem`/`ChatInput` are deliberately minimal. For a styled, accessible chat
|
|
@@ -423,12 +468,101 @@ disables itself while busy never noticed; a double-submitted one, and StrictMode
|
|
|
423
468
|
effect, did.
|
|
424
469
|
|
|
425
470
|
`chat` is the AI SDK v7 `useChat` return value (`messages`, `status`, `sendMessage`, `stop`, …) spread
|
|
426
|
-
together with the extras: `runId`/`activeRunId`, `
|
|
471
|
+
together with the extras: `runId`/`activeRunId`, `connection`, `getThreadId`, `backend` (the `AgentBackend`; also
|
|
472
|
+
under its old name `client`), thread list/CRUD
|
|
427
473
|
(`threads`, `loadThreads`, `loadThread`, `deleteThread`, `forkThread`, `renameThread`, `promoteThread`,
|
|
428
474
|
`truncateFromMessage`), `quota`/`loadQuota`, `cancel`, HITL `approve`/`reject` and `answer`/`skip`, and `regenerate`.
|
|
429
475
|
`MyToolCard`'s `part` prop above types as the exported `AnyToolUIPart` (`ToolUIPart | DynamicToolUIPart`
|
|
430
476
|
— `MessageItem` uses the same union for its own `renderToolPart` callback).
|
|
431
477
|
|
|
478
|
+
### Your own backend (`AgentBackend`)
|
|
479
|
+
|
|
480
|
+
Everything the hooks ask of a server goes through one interface, `AgentBackend`: start a turn
|
|
481
|
+
(`openChatStream`), attach to a streaming run (`resumeChatStream`), cancel, list/get/update/delete
|
|
482
|
+
threads — required — plus optional fork/promote/truncate, approve/reject/answer/skip, upload, tools,
|
|
483
|
+
skills, quota and message feedback. `AgentClient` (fetch over this library's routes) is the default.
|
|
484
|
+
An app with its own client, auth scheme or server implements the interface and passes it in:
|
|
485
|
+
|
|
486
|
+
```ts
|
|
487
|
+
import type { AgentBackend } from '@dudousxd/nestjs-agent-react';
|
|
488
|
+
|
|
489
|
+
const backend: AgentBackend = {
|
|
490
|
+
async openChatStream({ body, headers, signal }) {
|
|
491
|
+
const res = await fetch('/api/chat', {
|
|
492
|
+
method: 'POST',
|
|
493
|
+
credentials: 'include',
|
|
494
|
+
headers: { 'content-type': 'application/json', ...csrfHeader(), ...headers },
|
|
495
|
+
body: JSON.stringify(body),
|
|
496
|
+
signal,
|
|
497
|
+
});
|
|
498
|
+
if (!res.ok || !res.body) throw new Error(`chat failed: ${res.status}`);
|
|
499
|
+
return { body: res.body }; // SSE bytes in docs/stream-protocol.md framing
|
|
500
|
+
},
|
|
501
|
+
async resumeChatStream({ runId, after, signal }) {
|
|
502
|
+
const res = await fetch(`/api/chat/${runId}/stream${after ? `?after=${after}` : ''}`, {
|
|
503
|
+
credentials: 'include',
|
|
504
|
+
signal,
|
|
505
|
+
});
|
|
506
|
+
return res.status === 404 ? null : { body: res.body! };
|
|
507
|
+
},
|
|
508
|
+
cancelStream: (runId) => api.chat.cancel(runId),
|
|
509
|
+
listThreads: () => api.threads.list(),
|
|
510
|
+
getThread: (id) => api.threads.get(id),
|
|
511
|
+
updateThread: (id, patch) => api.threads.update(id, patch),
|
|
512
|
+
deleteThread: (id) => api.threads.remove(id),
|
|
513
|
+
setMessageFeedback: (id, input) => api.messages.feedback(id, input),
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
const chat = useAgentChat({ backend }); // chat.backend === backend, typed as yours
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
Calling a hook method whose optional backend member is missing throws
|
|
520
|
+
`AgentBackendUnsupportedError`. The body a backend receives is `{ message, threadId?, agent?,
|
|
521
|
+
attachments?, pageContext?, regenerate? }`; the SSE it returns and the REST shapes are in
|
|
522
|
+
[docs/stream-protocol.md](../../docs/stream-protocol.md).
|
|
523
|
+
|
|
524
|
+
**Cookie session + CSRF with the default client.** `credentials` and `getHeaders` are all it takes —
|
|
525
|
+
`getHeaders` runs per request, so a rotated token is picked up:
|
|
526
|
+
|
|
527
|
+
```ts
|
|
528
|
+
const readCookie = (name: string) =>
|
|
529
|
+
decodeURIComponent(document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`))?.[1] ?? '');
|
|
530
|
+
|
|
531
|
+
useAgentChat({
|
|
532
|
+
baseUrl: '/api',
|
|
533
|
+
credentials: 'include', // 'same-origin' (the fetch default) is enough when the API is same-origin
|
|
534
|
+
getHeaders: () => ({ 'X-XSRF-TOKEN': readCookie('XSRF-TOKEN') }),
|
|
535
|
+
});
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### Reconnecting a dropped stream
|
|
539
|
+
|
|
540
|
+
A server that numbers its frames (SSE `id:`, as this library's does) can be resumed: when the
|
|
541
|
+
connection drops mid-run, the transport re-attaches with `GET <base>/chat/:runId/stream?after=<last
|
|
542
|
+
id>`, backing off 0.5s, 1s, 2s… (`reconnect: { maxAttempts, baseDelayMs, maxDelayMs }`, or `false`).
|
|
543
|
+
Meanwhile `chat.status` is `'reconnecting'` (a busy status to `useChatTranscript`) and
|
|
544
|
+
`chat.connection` says which attempt it is on. If the run ended while the client was away (the
|
|
545
|
+
resume answers 404), the hook reloads the thread so the full answer replaces the partial one. After
|
|
546
|
+
the last failed attempt the turn ends with an error.
|
|
547
|
+
|
|
548
|
+
### Thread list and message feedback
|
|
549
|
+
|
|
550
|
+
```tsx
|
|
551
|
+
import { useMessageFeedback, useThreads } from '@dudousxd/nestjs-agent-react';
|
|
552
|
+
|
|
553
|
+
const { threads, isLoading, rename, remove, refresh } = useThreads({ backend: chat.backend });
|
|
554
|
+
const feedback = useMessageFeedback({ backend: chat.backend, threadId: chat.getThreadId });
|
|
555
|
+
|
|
556
|
+
<button aria-pressed={feedback.feedbackOf(message)?.value === 'up'}
|
|
557
|
+
onClick={() => feedback.toggle(message, 'up')}>Helpful</button>
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
`useThreads` refreshes itself when a chat on the same backend creates a thread or settles a run,
|
|
561
|
+
and patches a streamed title in place; `rename`/`remove` are optimistic and roll back on failure.
|
|
562
|
+
`useMessageFeedback` reads a replayed message's rating from `message.metadata.feedback`, rates
|
|
563
|
+
through `POST <base>/messages/:id/feedback`, and maps a message streamed in this session to the row
|
|
564
|
+
its run persisted (the live message carries `metadata.runId`).
|
|
565
|
+
|
|
432
566
|
### The transport, standalone
|
|
433
567
|
|
|
434
568
|
`AgentChatTransport` is a plain AI SDK v7 `ChatTransport` — wire it straight into `useChat` for the SSE
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/genui/json-render.tsx
|
|
22
|
+
var json_render_exports = {};
|
|
23
|
+
__export(json_render_exports, {
|
|
24
|
+
JsonRenderTree: () => JsonRenderTree,
|
|
25
|
+
jsonRenderTree: () => jsonRenderTree,
|
|
26
|
+
toJsonRenderRegistry: () => toJsonRenderRegistry,
|
|
27
|
+
treeToJsonRenderSpec: () => treeToJsonRenderSpec
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(json_render_exports);
|
|
30
|
+
var import_react = require("@json-render/react");
|
|
31
|
+
var import_react2 = require("react");
|
|
32
|
+
function treeToJsonRenderSpec(root) {
|
|
33
|
+
const elements = {};
|
|
34
|
+
let next = 0;
|
|
35
|
+
const visit = /* @__PURE__ */ __name((node) => {
|
|
36
|
+
const id = `el-${next++}`;
|
|
37
|
+
const entry = {
|
|
38
|
+
type: node.type,
|
|
39
|
+
props: node.props ?? {},
|
|
40
|
+
children: []
|
|
41
|
+
};
|
|
42
|
+
elements[id] = entry;
|
|
43
|
+
for (const child of node.children ?? []) entry.children.push(visit(child));
|
|
44
|
+
return id;
|
|
45
|
+
}, "visit");
|
|
46
|
+
return {
|
|
47
|
+
root: visit(root),
|
|
48
|
+
elements
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
__name(treeToJsonRenderSpec, "treeToJsonRenderSpec");
|
|
52
|
+
function toJsonRenderRegistry(registry) {
|
|
53
|
+
return Object.fromEntries(Object.entries(registry).map(([name, Renderer2]) => [
|
|
54
|
+
name,
|
|
55
|
+
({ element, children }) => /* @__PURE__ */ React.createElement(Renderer2, element.props ?? {}, children)
|
|
56
|
+
]));
|
|
57
|
+
}
|
|
58
|
+
__name(toJsonRenderRegistry, "toJsonRenderRegistry");
|
|
59
|
+
function JsonRenderTree({ root, registry, initialState }) {
|
|
60
|
+
const spec = (0, import_react2.useMemo)(() => root === void 0 ? null : treeToJsonRenderSpec(root), [
|
|
61
|
+
root
|
|
62
|
+
]);
|
|
63
|
+
if (spec === null) return null;
|
|
64
|
+
return /* @__PURE__ */ React.createElement(import_react.JSONUIProvider, {
|
|
65
|
+
registry,
|
|
66
|
+
...initialState !== void 0 ? {
|
|
67
|
+
initialState
|
|
68
|
+
} : {}
|
|
69
|
+
}, /* @__PURE__ */ React.createElement(import_react.Renderer, {
|
|
70
|
+
spec,
|
|
71
|
+
registry
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
__name(JsonRenderTree, "JsonRenderTree");
|
|
75
|
+
function jsonRenderTree(registry) {
|
|
76
|
+
return /* @__PURE__ */ __name(function JsonRenderTreeFrame({ root }) {
|
|
77
|
+
return /* @__PURE__ */ React.createElement(JsonRenderTree, {
|
|
78
|
+
registry,
|
|
79
|
+
...root !== void 0 ? {
|
|
80
|
+
root
|
|
81
|
+
} : {}
|
|
82
|
+
});
|
|
83
|
+
}, "JsonRenderTreeFrame");
|
|
84
|
+
}
|
|
85
|
+
__name(jsonRenderTree, "jsonRenderTree");
|
|
86
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
87
|
+
0 && (module.exports = {
|
|
88
|
+
JsonRenderTree,
|
|
89
|
+
jsonRenderTree,
|
|
90
|
+
toJsonRenderRegistry,
|
|
91
|
+
treeToJsonRenderSpec
|
|
92
|
+
});
|
|
93
|
+
//# sourceMappingURL=genui-json-render.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/genui/json-render.tsx"],"sourcesContent":["/**\n * Optional json-render adapter for tree frames (`@dudousxd/nestjs-agent-react/genui/json-render`).\n * Needs the optional peer `@json-render/react` (>= 0.21). Use it when your app already renders\n * json-render specs and wants tree frames to go through the same `Renderer` (its state, actions and\n * visibility machinery); otherwise `<GenerativeUI>` renders trees natively.\n *\n * ```tsx\n * const registry = { ...myComponents, [GENUI_TREE_COMPONENT]: jsonRenderTree(myJsonRenderRegistry) };\n * ```\n */\nimport { type ComponentRegistry, JSONUIProvider, Renderer } from '@json-render/react';\nimport { type ReactNode, useMemo } from 'react';\nimport type { GenerativeUIElement, GenuiRegistry, GenuiRenderer } from './types.js';\n\n/** A json-render flat spec: `{ root, elements: { [id]: { type, props, children } } }`. */\nexport interface JsonRenderFlatSpec {\n root: string;\n elements: Record<string, { type: string; props: Record<string, unknown>; children: string[] }>;\n}\n\n/** A tree frame's `root` as a json-render flat spec (ids `el-0`, `el-1`, … depth first). */\nexport function treeToJsonRenderSpec(root: GenerativeUIElement): JsonRenderFlatSpec {\n const elements: JsonRenderFlatSpec['elements'] = {};\n let next = 0;\n const visit = (node: GenerativeUIElement): string => {\n const id = `el-${next++}`;\n const entry = { type: node.type, props: node.props ?? {}, children: [] as string[] };\n elements[id] = entry;\n for (const child of node.children ?? []) entry.children.push(visit(child));\n return id;\n };\n return { root: visit(root), elements };\n}\n\n/**\n * Adapt a genui registry (renderers taking props + `children`) to a json-render registry (renderers\n * taking `{ element, children }`), so one set of components serves both.\n */\nexport function toJsonRenderRegistry(registry: GenuiRegistry): ComponentRegistry {\n return Object.fromEntries(\n Object.entries(registry).map(([name, Renderer]) => [\n name,\n ({ element, children }: { element: { props?: unknown }; children?: ReactNode }) => (\n <Renderer {...((element.props ?? {}) as Record<string, unknown>)}>{children}</Renderer>\n ),\n ]),\n );\n}\n\nexport interface JsonRenderTreeProps {\n root?: GenerativeUIElement;\n registry: ComponentRegistry;\n /** json-render state the spec's `$state` expressions read. */\n initialState?: Record<string, unknown>;\n}\n\n/** Render a tree frame's `root` with json-render. */\nexport function JsonRenderTree({ root, registry, initialState }: JsonRenderTreeProps) {\n const spec = useMemo(() => (root === undefined ? null : treeToJsonRenderSpec(root)), [root]);\n if (spec === null) return null;\n return (\n <JSONUIProvider registry={registry} {...(initialState !== undefined ? { initialState } : {})}>\n <Renderer spec={spec as never} registry={registry} />\n </JSONUIProvider>\n );\n}\n\n/** A registry entry for `genui:tree` that renders through json-render with `registry`. */\nexport function jsonRenderTree(registry: ComponentRegistry): GenuiRenderer<{\n root?: GenerativeUIElement;\n}> {\n return function JsonRenderTreeFrame({ root }: { root?: GenerativeUIElement }) {\n return <JsonRenderTree registry={registry} {...(root !== undefined ? { root } : {})} />;\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;AAUA,mBAAiE;AACjE,IAAAA,gBAAwC;AAUjC,SAASC,qBAAqBC,MAAyB;AAC5D,QAAMC,WAA2C,CAAC;AAClD,MAAIC,OAAO;AACX,QAAMC,QAAQ,wBAACC,SAAAA;AACb,UAAMC,KAAK,MAAMH,MAAAA;AACjB,UAAMI,QAAQ;MAAEC,MAAMH,KAAKG;MAAMC,OAAOJ,KAAKI,SAAS,CAAC;MAAGC,UAAU,CAAA;IAAe;AACnFR,aAASI,EAAAA,IAAMC;AACf,eAAWI,SAASN,KAAKK,YAAY,CAAA,EAAIH,OAAMG,SAASE,KAAKR,MAAMO,KAAAA,CAAAA;AACnE,WAAOL;EACT,GANc;AAOd,SAAO;IAAEL,MAAMG,MAAMH,IAAAA;IAAOC;EAAS;AACvC;AAXgBF;AAiBT,SAASa,qBAAqBC,UAAuB;AAC1D,SAAOC,OAAOC,YACZD,OAAOE,QAAQH,QAAAA,EAAUI,IAAI,CAAC,CAACC,MAAMC,SAAAA,MAAc;IACjDD;IACA,CAAC,EAAEE,SAASX,SAAQ,MAClB,sBAAA,cAACU,WAAeC,QAAQZ,SAAS,CAAC,GAAiCC,QAAAA;GAEtE,CAAA;AAEL;AATgBG;AAmBT,SAASS,eAAe,EAAErB,MAAMa,UAAUS,aAAY,GAAuB;AAClF,QAAMC,WAAOC,uBAAQ,MAAOxB,SAASyB,SAAY,OAAO1B,qBAAqBC,IAAAA,GAAQ;IAACA;GAAK;AAC3F,MAAIuB,SAAS,KAAM,QAAO;AAC1B,SACE,sBAAA,cAACG,6BAAAA;IAAeb;IAAqB,GAAIS,iBAAiBG,SAAY;MAAEH;IAAa,IAAI,CAAC;KACxF,sBAAA,cAACH,uBAAAA;IAASI;IAAqBV;;AAGrC;AARgBQ;AAWT,SAASM,eAAed,UAA2B;AAGxD,SAAO,gCAASe,oBAAoB,EAAE5B,KAAI,GAAkC;AAC1E,WAAO,sBAAA,cAACqB,gBAAAA;MAAeR;MAAqB,GAAIb,SAASyB,SAAY;QAAEzB;MAAK,IAAI,CAAC;;EACnF,GAFO;AAGT;AANgB2B;","names":["import_react","treeToJsonRenderSpec","root","elements","next","visit","node","id","entry","type","props","children","child","push","toJsonRenderRegistry","registry","Object","fromEntries","entries","map","name","Renderer","element","JsonRenderTree","initialState","spec","useMemo","undefined","JSONUIProvider","jsonRenderTree","JsonRenderTreeFrame"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ComponentRegistry } from '@json-render/react';
|
|
3
|
+
import { c as GenerativeUIElement, i as GenuiRenderer, h as GenuiRegistry } from './types-DFD-soWU.cjs';
|
|
4
|
+
|
|
5
|
+
/** A json-render flat spec: `{ root, elements: { [id]: { type, props, children } } }`. */
|
|
6
|
+
interface JsonRenderFlatSpec {
|
|
7
|
+
root: string;
|
|
8
|
+
elements: Record<string, {
|
|
9
|
+
type: string;
|
|
10
|
+
props: Record<string, unknown>;
|
|
11
|
+
children: string[];
|
|
12
|
+
}>;
|
|
13
|
+
}
|
|
14
|
+
/** A tree frame's `root` as a json-render flat spec (ids `el-0`, `el-1`, … depth first). */
|
|
15
|
+
declare function treeToJsonRenderSpec(root: GenerativeUIElement): JsonRenderFlatSpec;
|
|
16
|
+
/**
|
|
17
|
+
* Adapt a genui registry (renderers taking props + `children`) to a json-render registry (renderers
|
|
18
|
+
* taking `{ element, children }`), so one set of components serves both.
|
|
19
|
+
*/
|
|
20
|
+
declare function toJsonRenderRegistry(registry: GenuiRegistry): ComponentRegistry;
|
|
21
|
+
interface JsonRenderTreeProps {
|
|
22
|
+
root?: GenerativeUIElement;
|
|
23
|
+
registry: ComponentRegistry;
|
|
24
|
+
/** json-render state the spec's `$state` expressions read. */
|
|
25
|
+
initialState?: Record<string, unknown>;
|
|
26
|
+
}
|
|
27
|
+
/** Render a tree frame's `root` with json-render. */
|
|
28
|
+
declare function JsonRenderTree({ root, registry, initialState }: JsonRenderTreeProps): React.JSX.Element | null;
|
|
29
|
+
/** A registry entry for `genui:tree` that renders through json-render with `registry`. */
|
|
30
|
+
declare function jsonRenderTree(registry: ComponentRegistry): GenuiRenderer<{
|
|
31
|
+
root?: GenerativeUIElement;
|
|
32
|
+
}>;
|
|
33
|
+
|
|
34
|
+
export { type JsonRenderFlatSpec, JsonRenderTree, type JsonRenderTreeProps, jsonRenderTree, toJsonRenderRegistry, treeToJsonRenderSpec };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ComponentRegistry } from '@json-render/react';
|
|
3
|
+
import { c as GenerativeUIElement, i as GenuiRenderer, h as GenuiRegistry } from './types-DFD-soWU.js';
|
|
4
|
+
|
|
5
|
+
/** A json-render flat spec: `{ root, elements: { [id]: { type, props, children } } }`. */
|
|
6
|
+
interface JsonRenderFlatSpec {
|
|
7
|
+
root: string;
|
|
8
|
+
elements: Record<string, {
|
|
9
|
+
type: string;
|
|
10
|
+
props: Record<string, unknown>;
|
|
11
|
+
children: string[];
|
|
12
|
+
}>;
|
|
13
|
+
}
|
|
14
|
+
/** A tree frame's `root` as a json-render flat spec (ids `el-0`, `el-1`, … depth first). */
|
|
15
|
+
declare function treeToJsonRenderSpec(root: GenerativeUIElement): JsonRenderFlatSpec;
|
|
16
|
+
/**
|
|
17
|
+
* Adapt a genui registry (renderers taking props + `children`) to a json-render registry (renderers
|
|
18
|
+
* taking `{ element, children }`), so one set of components serves both.
|
|
19
|
+
*/
|
|
20
|
+
declare function toJsonRenderRegistry(registry: GenuiRegistry): ComponentRegistry;
|
|
21
|
+
interface JsonRenderTreeProps {
|
|
22
|
+
root?: GenerativeUIElement;
|
|
23
|
+
registry: ComponentRegistry;
|
|
24
|
+
/** json-render state the spec's `$state` expressions read. */
|
|
25
|
+
initialState?: Record<string, unknown>;
|
|
26
|
+
}
|
|
27
|
+
/** Render a tree frame's `root` with json-render. */
|
|
28
|
+
declare function JsonRenderTree({ root, registry, initialState }: JsonRenderTreeProps): React.JSX.Element | null;
|
|
29
|
+
/** A registry entry for `genui:tree` that renders through json-render with `registry`. */
|
|
30
|
+
declare function jsonRenderTree(registry: ComponentRegistry): GenuiRenderer<{
|
|
31
|
+
root?: GenerativeUIElement;
|
|
32
|
+
}>;
|
|
33
|
+
|
|
34
|
+
export { type JsonRenderFlatSpec, JsonRenderTree, type JsonRenderTreeProps, jsonRenderTree, toJsonRenderRegistry, treeToJsonRenderSpec };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
4
|
+
// src/genui/json-render.tsx
|
|
5
|
+
import { JSONUIProvider, Renderer } from "@json-render/react";
|
|
6
|
+
import { useMemo } from "react";
|
|
7
|
+
function treeToJsonRenderSpec(root) {
|
|
8
|
+
const elements = {};
|
|
9
|
+
let next = 0;
|
|
10
|
+
const visit = /* @__PURE__ */ __name((node) => {
|
|
11
|
+
const id = `el-${next++}`;
|
|
12
|
+
const entry = {
|
|
13
|
+
type: node.type,
|
|
14
|
+
props: node.props ?? {},
|
|
15
|
+
children: []
|
|
16
|
+
};
|
|
17
|
+
elements[id] = entry;
|
|
18
|
+
for (const child of node.children ?? []) entry.children.push(visit(child));
|
|
19
|
+
return id;
|
|
20
|
+
}, "visit");
|
|
21
|
+
return {
|
|
22
|
+
root: visit(root),
|
|
23
|
+
elements
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
__name(treeToJsonRenderSpec, "treeToJsonRenderSpec");
|
|
27
|
+
function toJsonRenderRegistry(registry) {
|
|
28
|
+
return Object.fromEntries(Object.entries(registry).map(([name, Renderer2]) => [
|
|
29
|
+
name,
|
|
30
|
+
({ element, children }) => /* @__PURE__ */ React.createElement(Renderer2, element.props ?? {}, children)
|
|
31
|
+
]));
|
|
32
|
+
}
|
|
33
|
+
__name(toJsonRenderRegistry, "toJsonRenderRegistry");
|
|
34
|
+
function JsonRenderTree({ root, registry, initialState }) {
|
|
35
|
+
const spec = useMemo(() => root === void 0 ? null : treeToJsonRenderSpec(root), [
|
|
36
|
+
root
|
|
37
|
+
]);
|
|
38
|
+
if (spec === null) return null;
|
|
39
|
+
return /* @__PURE__ */ React.createElement(JSONUIProvider, {
|
|
40
|
+
registry,
|
|
41
|
+
...initialState !== void 0 ? {
|
|
42
|
+
initialState
|
|
43
|
+
} : {}
|
|
44
|
+
}, /* @__PURE__ */ React.createElement(Renderer, {
|
|
45
|
+
spec,
|
|
46
|
+
registry
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
49
|
+
__name(JsonRenderTree, "JsonRenderTree");
|
|
50
|
+
function jsonRenderTree(registry) {
|
|
51
|
+
return /* @__PURE__ */ __name(function JsonRenderTreeFrame({ root }) {
|
|
52
|
+
return /* @__PURE__ */ React.createElement(JsonRenderTree, {
|
|
53
|
+
registry,
|
|
54
|
+
...root !== void 0 ? {
|
|
55
|
+
root
|
|
56
|
+
} : {}
|
|
57
|
+
});
|
|
58
|
+
}, "JsonRenderTreeFrame");
|
|
59
|
+
}
|
|
60
|
+
__name(jsonRenderTree, "jsonRenderTree");
|
|
61
|
+
export {
|
|
62
|
+
JsonRenderTree,
|
|
63
|
+
jsonRenderTree,
|
|
64
|
+
toJsonRenderRegistry,
|
|
65
|
+
treeToJsonRenderSpec
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=genui-json-render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/genui/json-render.tsx"],"sourcesContent":["/**\n * Optional json-render adapter for tree frames (`@dudousxd/nestjs-agent-react/genui/json-render`).\n * Needs the optional peer `@json-render/react` (>= 0.21). Use it when your app already renders\n * json-render specs and wants tree frames to go through the same `Renderer` (its state, actions and\n * visibility machinery); otherwise `<GenerativeUI>` renders trees natively.\n *\n * ```tsx\n * const registry = { ...myComponents, [GENUI_TREE_COMPONENT]: jsonRenderTree(myJsonRenderRegistry) };\n * ```\n */\nimport { type ComponentRegistry, JSONUIProvider, Renderer } from '@json-render/react';\nimport { type ReactNode, useMemo } from 'react';\nimport type { GenerativeUIElement, GenuiRegistry, GenuiRenderer } from './types.js';\n\n/** A json-render flat spec: `{ root, elements: { [id]: { type, props, children } } }`. */\nexport interface JsonRenderFlatSpec {\n root: string;\n elements: Record<string, { type: string; props: Record<string, unknown>; children: string[] }>;\n}\n\n/** A tree frame's `root` as a json-render flat spec (ids `el-0`, `el-1`, … depth first). */\nexport function treeToJsonRenderSpec(root: GenerativeUIElement): JsonRenderFlatSpec {\n const elements: JsonRenderFlatSpec['elements'] = {};\n let next = 0;\n const visit = (node: GenerativeUIElement): string => {\n const id = `el-${next++}`;\n const entry = { type: node.type, props: node.props ?? {}, children: [] as string[] };\n elements[id] = entry;\n for (const child of node.children ?? []) entry.children.push(visit(child));\n return id;\n };\n return { root: visit(root), elements };\n}\n\n/**\n * Adapt a genui registry (renderers taking props + `children`) to a json-render registry (renderers\n * taking `{ element, children }`), so one set of components serves both.\n */\nexport function toJsonRenderRegistry(registry: GenuiRegistry): ComponentRegistry {\n return Object.fromEntries(\n Object.entries(registry).map(([name, Renderer]) => [\n name,\n ({ element, children }: { element: { props?: unknown }; children?: ReactNode }) => (\n <Renderer {...((element.props ?? {}) as Record<string, unknown>)}>{children}</Renderer>\n ),\n ]),\n );\n}\n\nexport interface JsonRenderTreeProps {\n root?: GenerativeUIElement;\n registry: ComponentRegistry;\n /** json-render state the spec's `$state` expressions read. */\n initialState?: Record<string, unknown>;\n}\n\n/** Render a tree frame's `root` with json-render. */\nexport function JsonRenderTree({ root, registry, initialState }: JsonRenderTreeProps) {\n const spec = useMemo(() => (root === undefined ? null : treeToJsonRenderSpec(root)), [root]);\n if (spec === null) return null;\n return (\n <JSONUIProvider registry={registry} {...(initialState !== undefined ? { initialState } : {})}>\n <Renderer spec={spec as never} registry={registry} />\n </JSONUIProvider>\n );\n}\n\n/** A registry entry for `genui:tree` that renders through json-render with `registry`. */\nexport function jsonRenderTree(registry: ComponentRegistry): GenuiRenderer<{\n root?: GenerativeUIElement;\n}> {\n return function JsonRenderTreeFrame({ root }: { root?: GenerativeUIElement }) {\n return <JsonRenderTree registry={registry} {...(root !== undefined ? { root } : {})} />;\n };\n}\n"],"mappings":";;;;AAUA,SAAiCA,gBAAgBC,gBAAgB;AACjE,SAAyBC,eAAe;AAUjC,SAASC,qBAAqBC,MAAyB;AAC5D,QAAMC,WAA2C,CAAC;AAClD,MAAIC,OAAO;AACX,QAAMC,QAAQ,wBAACC,SAAAA;AACb,UAAMC,KAAK,MAAMH,MAAAA;AACjB,UAAMI,QAAQ;MAAEC,MAAMH,KAAKG;MAAMC,OAAOJ,KAAKI,SAAS,CAAC;MAAGC,UAAU,CAAA;IAAe;AACnFR,aAASI,EAAAA,IAAMC;AACf,eAAWI,SAASN,KAAKK,YAAY,CAAA,EAAIH,OAAMG,SAASE,KAAKR,MAAMO,KAAAA,CAAAA;AACnE,WAAOL;EACT,GANc;AAOd,SAAO;IAAEL,MAAMG,MAAMH,IAAAA;IAAOC;EAAS;AACvC;AAXgBF;AAiBT,SAASa,qBAAqBC,UAAuB;AAC1D,SAAOC,OAAOC,YACZD,OAAOE,QAAQH,QAAAA,EAAUI,IAAI,CAAC,CAACC,MAAMC,SAAAA,MAAc;IACjDD;IACA,CAAC,EAAEE,SAASX,SAAQ,MAClB,sBAAA,cAACU,WAAeC,QAAQZ,SAAS,CAAC,GAAiCC,QAAAA;GAEtE,CAAA;AAEL;AATgBG;AAmBT,SAASS,eAAe,EAAErB,MAAMa,UAAUS,aAAY,GAAuB;AAClF,QAAMC,OAAOC,QAAQ,MAAOxB,SAASyB,SAAY,OAAO1B,qBAAqBC,IAAAA,GAAQ;IAACA;GAAK;AAC3F,MAAIuB,SAAS,KAAM,QAAO;AAC1B,SACE,sBAAA,cAACG,gBAAAA;IAAeb;IAAqB,GAAIS,iBAAiBG,SAAY;MAAEH;IAAa,IAAI,CAAC;KACxF,sBAAA,cAACH,UAAAA;IAASI;IAAqBV;;AAGrC;AARgBQ;AAWT,SAASM,eAAed,UAA2B;AAGxD,SAAO,gCAASe,oBAAoB,EAAE5B,KAAI,GAAkC;AAC1E,WAAO,sBAAA,cAACqB,gBAAAA;MAAeR;MAAqB,GAAIb,SAASyB,SAAY;QAAEzB;MAAK,IAAI,CAAC;;EACnF,GAFO;AAGT;AANgB2B;","names":["JSONUIProvider","Renderer","useMemo","treeToJsonRenderSpec","root","elements","next","visit","node","id","entry","type","props","children","child","push","toJsonRenderRegistry","registry","Object","fromEntries","entries","map","name","Renderer","element","JsonRenderTree","initialState","spec","useMemo","undefined","JSONUIProvider","jsonRenderTree","JsonRenderTreeFrame"]}
|