@assistant-ui/react-generative-ui 0.0.2
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 +21 -0
- package/README.md +140 -0
- package/dist/JSONGenerativeUI.client.d.ts +23 -0
- package/dist/JSONGenerativeUI.client.d.ts.map +1 -0
- package/dist/JSONGenerativeUI.client.js +48 -0
- package/dist/JSONGenerativeUI.client.js.map +1 -0
- package/dist/JSONGenerativeUI.server.d.ts +25 -0
- package/dist/JSONGenerativeUI.server.d.ts.map +1 -0
- package/dist/JSONGenerativeUI.server.js +32 -0
- package/dist/JSONGenerativeUI.server.js.map +1 -0
- package/dist/JSONGenerativeUI.shared.d.ts +53 -0
- package/dist/JSONGenerativeUI.shared.d.ts.map +1 -0
- package/dist/JSONGenerativeUI.shared.js +31 -0
- package/dist/JSONGenerativeUI.shared.js.map +1 -0
- package/dist/buildPresentParameters.d.ts +24 -0
- package/dist/buildPresentParameters.d.ts.map +1 -0
- package/dist/buildPresentParameters.js +68 -0
- package/dist/buildPresentParameters.js.map +1 -0
- package/dist/constants.d.ts +14 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +15 -0
- package/dist/constants.js.map +1 -0
- package/dist/defineGenerativeComponents.d.ts +33 -0
- package/dist/defineGenerativeComponents.d.ts.map +1 -0
- package/dist/defineGenerativeComponents.js +34 -0
- package/dist/defineGenerativeComponents.js.map +1 -0
- package/dist/generativeUIToJSX.d.ts +16 -0
- package/dist/generativeUIToJSX.d.ts.map +1 -0
- package/dist/generativeUIToJSX.js +46 -0
- package/dist/generativeUIToJSX.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +7 -0
- package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts +141 -0
- package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts.map +1 -0
- package/dist/renderGenerativeUI.d.ts +17 -0
- package/dist/renderGenerativeUI.d.ts.map +1 -0
- package/dist/renderGenerativeUI.js +85 -0
- package/dist/renderGenerativeUI.js.map +1 -0
- package/dist/types.d.ts +94 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +0 -0
- package/package.json +82 -0
- package/src/JSONGenerativeUI.client.tsx +66 -0
- package/src/JSONGenerativeUI.server.tsx +39 -0
- package/src/JSONGenerativeUI.shared.ts +74 -0
- package/src/JSONGenerativeUI.test.tsx +89 -0
- package/src/buildPresentParameters.ts +90 -0
- package/src/constants.ts +10 -0
- package/src/defineGenerativeComponents.ts +38 -0
- package/src/generativeUIToJSX.test.ts +64 -0
- package/src/generativeUIToJSX.ts +63 -0
- package/src/index.ts +26 -0
- package/src/renderGenerativeUI.test.tsx +204 -0
- package/src/renderGenerativeUI.tsx +135 -0
- package/src/types.ts +104 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/generativeUIToJSX.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Serializes a generative-UI node to a JSX-like string for display — the
|
|
4
|
+
* "view source" of a model-produced tree. The wire form
|
|
5
|
+
* `{ $type: "Weather", id: "x" }` becomes `<Weather id="x" />`, and nested
|
|
6
|
+
* `children` render between tags: `<Card title="Hi"><Text>hello</Text></Card>`.
|
|
7
|
+
*
|
|
8
|
+
* It is a faithful textual rendering, not a parser: text children are emitted
|
|
9
|
+
* verbatim (not HTML/JSX-escaped), so the result is meant to be shown, not
|
|
10
|
+
* re-parsed. Returns `""` for nodes that aren't renderable (no `$type` yet,
|
|
11
|
+
* `null`, booleans).
|
|
12
|
+
*/
|
|
13
|
+
declare function generativeUIToJSX(node: unknown): string;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { generativeUIToJSX };
|
|
16
|
+
//# sourceMappingURL=generativeUIToJSX.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generativeUIToJSX.d.ts","names":[],"sources":["../src/generativeUIToJSX.ts"],"mappings":";;AAaA;;;;AAA+C;;;;;;iBAA/B,iBAAA,CAAkB,IAAa"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { TYPE_KEY } from "./constants.js";
|
|
2
|
+
//#region src/generativeUIToJSX.ts
|
|
3
|
+
/**
|
|
4
|
+
* Serializes a generative-UI node to a JSX-like string for display — the
|
|
5
|
+
* "view source" of a model-produced tree. The wire form
|
|
6
|
+
* `{ $type: "Weather", id: "x" }` becomes `<Weather id="x" />`, and nested
|
|
7
|
+
* `children` render between tags: `<Card title="Hi"><Text>hello</Text></Card>`.
|
|
8
|
+
*
|
|
9
|
+
* It is a faithful textual rendering, not a parser: text children are emitted
|
|
10
|
+
* verbatim (not HTML/JSX-escaped), so the result is meant to be shown, not
|
|
11
|
+
* re-parsed. Returns `""` for nodes that aren't renderable (no `$type` yet,
|
|
12
|
+
* `null`, booleans).
|
|
13
|
+
*/
|
|
14
|
+
function generativeUIToJSX(node) {
|
|
15
|
+
return toJSX(node, 0);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The deepest tree we serialize. Input is model-produced, so a runaway/
|
|
19
|
+
* adversarial response could nest arbitrarily deep and overflow the stack — past
|
|
20
|
+
* this depth (far beyond any real UI) we stop. Mirrors the renderer's bound.
|
|
21
|
+
*/
|
|
22
|
+
const MAX_DEPTH = 64;
|
|
23
|
+
function toJSX(node, depth) {
|
|
24
|
+
if (depth > MAX_DEPTH) return "";
|
|
25
|
+
if (node == null || typeof node === "boolean") return "";
|
|
26
|
+
if (typeof node === "string") return node;
|
|
27
|
+
if (typeof node === "number") return String(node);
|
|
28
|
+
if (Array.isArray(node)) return node.map((child) => toJSX(child, depth + 1)).join("");
|
|
29
|
+
if (typeof node !== "object") return "";
|
|
30
|
+
const { [TYPE_KEY]: type, children, ...props } = node;
|
|
31
|
+
if (typeof type !== "string") return "";
|
|
32
|
+
const attrs = Object.entries(props).map(([key, value]) => formatAttr(key, value)).join("");
|
|
33
|
+
const inner = children === void 0 ? "" : toJSX(children, depth + 1);
|
|
34
|
+
return inner === "" ? `<${type}${attrs} />` : `<${type}${attrs}>${inner}</${type}>`;
|
|
35
|
+
}
|
|
36
|
+
/** Formats one prop as a JSX attribute (`id="x"`, `count={3}`, `open`, …). */
|
|
37
|
+
function formatAttr(key, value) {
|
|
38
|
+
if (value === void 0) return "";
|
|
39
|
+
if (value === true) return ` ${key}`;
|
|
40
|
+
if (typeof value === "string") return /["\n]/.test(value) ? ` ${key}={${JSON.stringify(value)}}` : ` ${key}="${value}"`;
|
|
41
|
+
return ` ${key}={${JSON.stringify(value)}}`;
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { generativeUIToJSX };
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=generativeUIToJSX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generativeUIToJSX.js","names":[],"sources":["../src/generativeUIToJSX.ts"],"sourcesContent":["import { TYPE_KEY } from \"./constants\";\n\n/**\n * Serializes a generative-UI node to a JSX-like string for display — the\n * \"view source\" of a model-produced tree. The wire form\n * `{ $type: \"Weather\", id: \"x\" }` becomes `<Weather id=\"x\" />`, and nested\n * `children` render between tags: `<Card title=\"Hi\"><Text>hello</Text></Card>`.\n *\n * It is a faithful textual rendering, not a parser: text children are emitted\n * verbatim (not HTML/JSX-escaped), so the result is meant to be shown, not\n * re-parsed. Returns `\"\"` for nodes that aren't renderable (no `$type` yet,\n * `null`, booleans).\n */\nexport function generativeUIToJSX(node: unknown): string {\n return toJSX(node, 0);\n}\n\n/**\n * The deepest tree we serialize. Input is model-produced, so a runaway/\n * adversarial response could nest arbitrarily deep and overflow the stack — past\n * this depth (far beyond any real UI) we stop. Mirrors the renderer's bound.\n */\nconst MAX_DEPTH = 64;\n\nfunction toJSX(node: unknown, depth: number): string {\n if (depth > MAX_DEPTH) return \"\";\n if (node == null || typeof node === \"boolean\") return \"\";\n if (typeof node === \"string\") return node;\n if (typeof node === \"number\") return String(node);\n if (Array.isArray(node))\n return node.map((child) => toJSX(child, depth + 1)).join(\"\");\n if (typeof node !== \"object\") return \"\";\n\n const {\n [TYPE_KEY]: type,\n children,\n ...props\n } = node as Record<string, unknown>;\n if (typeof type !== \"string\") return \"\";\n\n const attrs = Object.entries(props)\n .map(([key, value]) => formatAttr(key, value))\n .join(\"\");\n const inner = children === undefined ? \"\" : toJSX(children, depth + 1);\n\n return inner === \"\"\n ? `<${type}${attrs} />`\n : `<${type}${attrs}>${inner}</${type}>`;\n}\n\n/** Formats one prop as a JSX attribute (`id=\"x\"`, `count={3}`, `open`, …). */\nfunction formatAttr(key: string, value: unknown): string {\n if (value === undefined) return \"\";\n if (value === true) return ` ${key}`;\n if (typeof value === \"string\") {\n // Plain double-quoted form when safe; expression form when a quote or\n // newline would break the attribute.\n return /[\"\\n]/.test(value)\n ? ` ${key}={${JSON.stringify(value)}}`\n : ` ${key}=\"${value}\"`;\n }\n return ` ${key}={${JSON.stringify(value)}}`;\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,kBAAkB,MAAuB;CACvD,OAAO,MAAM,MAAM,CAAC;AACtB;;;;;;AAOA,MAAM,YAAY;AAElB,SAAS,MAAM,MAAe,OAAuB;CACnD,IAAI,QAAQ,WAAW,OAAO;CAC9B,IAAI,QAAQ,QAAQ,OAAO,SAAS,WAAW,OAAO;CACtD,IAAI,OAAO,SAAS,UAAU,OAAO;CACrC,IAAI,OAAO,SAAS,UAAU,OAAO,OAAO,IAAI;CAChD,IAAI,MAAM,QAAQ,IAAI,GACpB,OAAO,KAAK,KAAK,UAAU,MAAM,OAAO,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE;CAC7D,IAAI,OAAO,SAAS,UAAU,OAAO;CAErC,MAAM,GACH,WAAW,MACZ,UACA,GAAG,UACD;CACJ,IAAI,OAAO,SAAS,UAAU,OAAO;CAErC,MAAM,QAAQ,OAAO,QAAQ,KAAK,EAC/B,KAAK,CAAC,KAAK,WAAW,WAAW,KAAK,KAAK,CAAC,EAC5C,KAAK,EAAE;CACV,MAAM,QAAQ,aAAa,KAAA,IAAY,KAAK,MAAM,UAAU,QAAQ,CAAC;CAErE,OAAO,UAAU,KACb,IAAI,OAAO,MAAM,OACjB,IAAI,OAAO,MAAM,GAAG,MAAM,IAAI,KAAK;AACzC;;AAGA,SAAS,WAAW,KAAa,OAAwB;CACvD,IAAI,UAAU,KAAA,GAAW,OAAO;CAChC,IAAI,UAAU,MAAM,OAAO,IAAI;CAC/B,IAAI,OAAO,UAAU,UAGnB,OAAO,QAAQ,KAAK,KAAK,IACrB,IAAI,IAAI,IAAI,KAAK,UAAU,KAAK,EAAE,KAClC,IAAI,IAAI,IAAI,MAAM;CAExB,OAAO,IAAI,IAAI,IAAI,KAAK,UAAU,KAAK,EAAE;AAC3C"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { GenerativeUIComponent, GenerativeUIElement, GenerativeUILibrary, GenerativeUINode, GenerativeUIProps, GenerativeUIRenderContext, GenerativeUIStatus } from "./types.js";
|
|
2
|
+
import { JSONGenerativeUIOptions, PresentTool, PresentToolOptions, PromptUserTool } from "./JSONGenerativeUI.shared.js";
|
|
3
|
+
import { buildPresentParameters } from "./buildPresentParameters.js";
|
|
4
|
+
import { TYPE_KEY } from "./constants.js";
|
|
5
|
+
import { defineGenerativeComponents } from "./defineGenerativeComponents.js";
|
|
6
|
+
import { generativeUIToJSX } from "./generativeUIToJSX.js";
|
|
7
|
+
import { renderGenerativeUI } from "./renderGenerativeUI.js";
|
|
8
|
+
import { JSONGenerativeUI } from "@assistant-ui/react-generative-ui/internal-json";
|
|
9
|
+
export { type GenerativeUIComponent, type GenerativeUIElement, type GenerativeUILibrary, type GenerativeUINode, type GenerativeUIProps, type GenerativeUIRenderContext, type GenerativeUIStatus, JSONGenerativeUI, type JSONGenerativeUIOptions, type PresentTool, type PresentToolOptions, type PromptUserTool, TYPE_KEY, buildPresentParameters, defineGenerativeComponents, generativeUIToJSX, renderGenerativeUI };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { TYPE_KEY } from "./constants.js";
|
|
2
|
+
import { buildPresentParameters } from "./buildPresentParameters.js";
|
|
3
|
+
import { renderGenerativeUI } from "./renderGenerativeUI.js";
|
|
4
|
+
import { defineGenerativeComponents } from "./defineGenerativeComponents.js";
|
|
5
|
+
import { generativeUIToJSX } from "./generativeUIToJSX.js";
|
|
6
|
+
import { JSONGenerativeUI } from "@assistant-ui/react-generative-ui/internal-json";
|
|
7
|
+
export { JSONGenerativeUI, TYPE_KEY, buildPresentParameters, defineGenerativeComponents, generativeUIToJSX, renderGenerativeUI };
|
package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
//#region ../../node_modules/.pnpm/@types+json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts
|
|
2
|
+
// ==================================================================================================
|
|
3
|
+
// JSON Schema Draft 07
|
|
4
|
+
// ==================================================================================================
|
|
5
|
+
// https://tools.ietf.org/html/draft-handrews-json-schema-validation-01
|
|
6
|
+
// --------------------------------------------------------------------------------------------------
|
|
7
|
+
/**
|
|
8
|
+
* Primitive type
|
|
9
|
+
* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.1.1
|
|
10
|
+
*/
|
|
11
|
+
type JSONSchema7TypeName = "string" //
|
|
12
|
+
| "number" | "integer" | "boolean" | "object" | "array" | "null";
|
|
13
|
+
/**
|
|
14
|
+
* Primitive type
|
|
15
|
+
* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.1.1
|
|
16
|
+
*/
|
|
17
|
+
type JSONSchema7Type = string //
|
|
18
|
+
| number | boolean | JSONSchema7Object | JSONSchema7Array | null;
|
|
19
|
+
// Workaround for infinite type recursion
|
|
20
|
+
interface JSONSchema7Object {
|
|
21
|
+
[key: string]: JSONSchema7Type;
|
|
22
|
+
}
|
|
23
|
+
// Workaround for infinite type recursion
|
|
24
|
+
// https://github.com/Microsoft/TypeScript/issues/3496#issuecomment-128553540
|
|
25
|
+
interface JSONSchema7Array extends Array<JSONSchema7Type> {}
|
|
26
|
+
/**
|
|
27
|
+
* Meta schema
|
|
28
|
+
*
|
|
29
|
+
* Recommended values:
|
|
30
|
+
* - 'http://json-schema.org/schema#'
|
|
31
|
+
* - 'http://json-schema.org/hyper-schema#'
|
|
32
|
+
* - 'http://json-schema.org/draft-07/schema#'
|
|
33
|
+
* - 'http://json-schema.org/draft-07/hyper-schema#'
|
|
34
|
+
*
|
|
35
|
+
* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-5
|
|
36
|
+
*/
|
|
37
|
+
type JSONSchema7Version = string;
|
|
38
|
+
/**
|
|
39
|
+
* JSON Schema v7
|
|
40
|
+
* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01
|
|
41
|
+
*/
|
|
42
|
+
type JSONSchema7Definition = JSONSchema7 | boolean;
|
|
43
|
+
interface JSONSchema7 {
|
|
44
|
+
$id?: string | undefined;
|
|
45
|
+
$ref?: string | undefined;
|
|
46
|
+
$schema?: JSONSchema7Version | undefined;
|
|
47
|
+
$comment?: string | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* @see https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-00#section-8.2.4
|
|
50
|
+
* @see https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-00#appendix-A
|
|
51
|
+
*/
|
|
52
|
+
$defs?: {
|
|
53
|
+
[key: string]: JSONSchema7Definition;
|
|
54
|
+
} | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.1
|
|
57
|
+
*/
|
|
58
|
+
type?: JSONSchema7TypeName | JSONSchema7TypeName[] | undefined;
|
|
59
|
+
enum?: JSONSchema7Type[] | undefined;
|
|
60
|
+
const?: JSONSchema7Type | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.2
|
|
63
|
+
*/
|
|
64
|
+
multipleOf?: number | undefined;
|
|
65
|
+
maximum?: number | undefined;
|
|
66
|
+
exclusiveMaximum?: number | undefined;
|
|
67
|
+
minimum?: number | undefined;
|
|
68
|
+
exclusiveMinimum?: number | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.3
|
|
71
|
+
*/
|
|
72
|
+
maxLength?: number | undefined;
|
|
73
|
+
minLength?: number | undefined;
|
|
74
|
+
pattern?: string | undefined;
|
|
75
|
+
/**
|
|
76
|
+
* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.4
|
|
77
|
+
*/
|
|
78
|
+
items?: JSONSchema7Definition | JSONSchema7Definition[] | undefined;
|
|
79
|
+
additionalItems?: JSONSchema7Definition | undefined;
|
|
80
|
+
maxItems?: number | undefined;
|
|
81
|
+
minItems?: number | undefined;
|
|
82
|
+
uniqueItems?: boolean | undefined;
|
|
83
|
+
contains?: JSONSchema7Definition | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.5
|
|
86
|
+
*/
|
|
87
|
+
maxProperties?: number | undefined;
|
|
88
|
+
minProperties?: number | undefined;
|
|
89
|
+
required?: string[] | undefined;
|
|
90
|
+
properties?: {
|
|
91
|
+
[key: string]: JSONSchema7Definition;
|
|
92
|
+
} | undefined;
|
|
93
|
+
patternProperties?: {
|
|
94
|
+
[key: string]: JSONSchema7Definition;
|
|
95
|
+
} | undefined;
|
|
96
|
+
additionalProperties?: JSONSchema7Definition | undefined;
|
|
97
|
+
dependencies?: {
|
|
98
|
+
[key: string]: JSONSchema7Definition | string[];
|
|
99
|
+
} | undefined;
|
|
100
|
+
propertyNames?: JSONSchema7Definition | undefined;
|
|
101
|
+
/**
|
|
102
|
+
* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.6
|
|
103
|
+
*/
|
|
104
|
+
if?: JSONSchema7Definition | undefined;
|
|
105
|
+
then?: JSONSchema7Definition | undefined;
|
|
106
|
+
else?: JSONSchema7Definition | undefined;
|
|
107
|
+
/**
|
|
108
|
+
* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.7
|
|
109
|
+
*/
|
|
110
|
+
allOf?: JSONSchema7Definition[] | undefined;
|
|
111
|
+
anyOf?: JSONSchema7Definition[] | undefined;
|
|
112
|
+
oneOf?: JSONSchema7Definition[] | undefined;
|
|
113
|
+
not?: JSONSchema7Definition | undefined;
|
|
114
|
+
/**
|
|
115
|
+
* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-7
|
|
116
|
+
*/
|
|
117
|
+
format?: string | undefined;
|
|
118
|
+
/**
|
|
119
|
+
* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-8
|
|
120
|
+
*/
|
|
121
|
+
contentMediaType?: string | undefined;
|
|
122
|
+
contentEncoding?: string | undefined;
|
|
123
|
+
/**
|
|
124
|
+
* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-9
|
|
125
|
+
*/
|
|
126
|
+
definitions?: {
|
|
127
|
+
[key: string]: JSONSchema7Definition;
|
|
128
|
+
} | undefined;
|
|
129
|
+
/**
|
|
130
|
+
* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-10
|
|
131
|
+
*/
|
|
132
|
+
title?: string | undefined;
|
|
133
|
+
description?: string | undefined;
|
|
134
|
+
default?: JSONSchema7Type | undefined;
|
|
135
|
+
readOnly?: boolean | undefined;
|
|
136
|
+
writeOnly?: boolean | undefined;
|
|
137
|
+
examples?: JSONSchema7Type | undefined;
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
140
|
+
export { JSONSchema7 };
|
|
141
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":["JSONSchema4Object","JSONSchema4Array","key","JSONSchema4Type","Array","id","$ref","$schema","JSONSchema4Version","title","description","default","multipleOf","maximum","exclusiveMaximum","minimum","exclusiveMinimum","maxLength","minLength","pattern","additionalItems","JSONSchema4","items","maxItems","minItems","uniqueItems","maxProperties","minProperties","required","additionalProperties","definitions","k","properties","patternProperties","dependencies","enum","type","JSONSchema4TypeName","allOf","anyOf","oneOf","not","extends","format","JSONSchema6Object","JSONSchema6Array","JSONSchema6Type","JSONSchema6","$id","JSONSchema6Version","JSONSchema6Definition","contains","propertyNames","const","JSONSchema6TypeName","examples","JSONSchema7Object","JSONSchema7Array","JSONSchema7Type","JSONSchema7","JSONSchema7Version","$comment","$defs","JSONSchema7Definition","JSONSchema7TypeName","if","then","else","contentMediaType","contentEncoding","readOnly","writeOnly","valid","errors","ValidationError","property","message","instance","schema","ValidationResult","value","result"],"sources":["../../../../../../../../../node_modules/.pnpm/@types+json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts"],"x_google_ignoreList":[0],"mappings":";;;;;;;;;;KA+iBY,mBAAA;AAAA;;;;;KAaA,eAAA;AAAA,qBAIN,iBAAA,GACA,gBAAgB;AAAA;AAAA,UAIL,iBAAA;EAAA,CACZE,GAAAA,WAAc,eAAe;AAAA;AAAA;AAAA;AAAA,UAKjB,gBAAA,SAAyB,KAAK,CAAC,eAAA;;;;;;;;;;;;KAapC,kBAAA;;;;;KAMA,qBAAA,GAAwB,WAAW;AAAA,UAC9B,WAAA;EACb8C,GAAAA;EACA1C,IAAAA;EACAC,OAAAA,GAAU,kBAAA;EACVsD,QAAAA;;;;;EAMAC,KAAAA;IAAAA,CACK5D,GAAAA,WAAc,qBAAA;EAAA;;;;EAMnBkC,IAAAA,GAAO,mBAAA,GAAsB,mBAAA;EAC7BD,IAAAA,GAAO,eAAA;EACPkB,KAAAA,GAAQ,eAAA;;;;EAKRzC,UAAAA;EACAC,OAAAA;EACAC,gBAAAA;EACAC,OAAAA;EACAC,gBAAAA;;;;EAKAC,SAAAA;EACAC,SAAAA;EACAC,OAAAA;;;;EAKAG,KAAAA,GAAQ,qBAAA,GAAwB,qBAAA;EAChCF,eAAAA,GAAkB,qBAAA;EAClBG,QAAAA;EACAC,QAAAA;EACAC,WAAAA;EACA0B,QAAAA,GAAW,qBAAA;;;;EAKXzB,aAAAA;EACAC,aAAAA;EACAC,QAAAA;EACAI,UAAAA;IAAAA,CACK9B,GAAAA,WAAc,qBAAA;EAAA;EAEnB+B,iBAAAA;IAAAA,CACK/B,GAAAA,WAAc,qBAAA;EAAA;EAEnB2B,oBAAAA,GAAuB,qBAAA;EACvBK,YAAAA;IAAAA,CACKhC,GAAAA,WAAc,qBAAA;EAAA;EAEnBkD,aAAAA,GAAgB,qBAAA;;;;EAKhBa,EAAAA,GAAK,qBAAA;EACLC,IAAAA,GAAO,qBAAA;EACPC,IAAAA,GAAO,qBAAA;;;;EAKP7B,KAAAA,GAAQ,qBAAA;EACRC,KAAAA,GAAQ,qBAAA;EACRC,KAAAA,GAAQ,qBAAA;EACRC,GAAAA,GAAM,qBAAA;;;;EAKNE,MAAAA;;;;EAKAyB,gBAAAA;EACAC,eAAAA;;;;EAKAvC,WAAAA;IAAAA,CACK5B,GAAAA,WAAc,qBAAA;EAAA;;;;EAMnBO,KAAAA;EACAC,WAAAA;EACAC,OAAAA,GAAU,eAAA;EACV2D,QAAAA;EACAC,SAAAA;EACAhB,QAAAA,GAAW,eAAA;AAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { GenerativeUILibrary, GenerativeUIRenderContext } from "./types.js";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
//#region src/renderGenerativeUI.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Renders a generative-ui tree against a {@link GenerativeUILibrary}.
|
|
7
|
+
*
|
|
8
|
+
* The model emits each node as a flat object `{ $type, ...props }`. We first
|
|
9
|
+
* normalize that wire form into React-shaped elements (`{ type, props }`), then
|
|
10
|
+
* render: each `type` is looked up in the library and its `props` are passed
|
|
11
|
+
* to the component's `render(props, context)`, with `children` rendered
|
|
12
|
+
* recursively so components can nest.
|
|
13
|
+
*/
|
|
14
|
+
declare function renderGenerativeUI(node: unknown, library: GenerativeUILibrary, context?: GenerativeUIRenderContext): ReactNode;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { renderGenerativeUI };
|
|
17
|
+
//# sourceMappingURL=renderGenerativeUI.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderGenerativeUI.d.ts","names":[],"sources":["../src/renderGenerativeUI.tsx"],"mappings":";;;;;;AAoBA;;;;;;;iBAAgB,kBAAA,CACd,IAAA,WACA,OAAA,EAAS,mBAAA,EACT,OAAA,GAAS,yBAAA,GACR,SAAA"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { TYPE_KEY } from "./constants.js";
|
|
2
|
+
import { Fragment } from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/renderGenerativeUI.tsx
|
|
5
|
+
const DEFAULT_CONTEXT = { status: "done" };
|
|
6
|
+
/**
|
|
7
|
+
* Renders a generative-ui tree against a {@link GenerativeUILibrary}.
|
|
8
|
+
*
|
|
9
|
+
* The model emits each node as a flat object `{ $type, ...props }`. We first
|
|
10
|
+
* normalize that wire form into React-shaped elements (`{ type, props }`), then
|
|
11
|
+
* render: each `type` is looked up in the library and its `props` are passed
|
|
12
|
+
* to the component's `render(props, context)`, with `children` rendered
|
|
13
|
+
* recursively so components can nest.
|
|
14
|
+
*/
|
|
15
|
+
function renderGenerativeUI(node, library, context = DEFAULT_CONTEXT) {
|
|
16
|
+
return renderNode(normalizeNode(node), library, context);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The deepest tree we normalize. The input comes from the model, so a runaway
|
|
20
|
+
* or adversarial response could nest arbitrarily deep and overflow the stack;
|
|
21
|
+
* past this depth we stop (far beyond any real UI). Bounding normalization
|
|
22
|
+
* bounds rendering too, since it only walks the normalized tree.
|
|
23
|
+
*/
|
|
24
|
+
const MAX_DEPTH = 64;
|
|
25
|
+
/** Converts the flat wire form into a normalized {@link GenerativeUINode}. */
|
|
26
|
+
function normalizeNode(node, depth = 0) {
|
|
27
|
+
if (depth > MAX_DEPTH) return null;
|
|
28
|
+
if (node == null || typeof node === "boolean") return null;
|
|
29
|
+
if (typeof node === "string" || typeof node === "number") return node;
|
|
30
|
+
if (Array.isArray(node)) return node.map((child) => normalizeNode(child, depth + 1));
|
|
31
|
+
if (typeof node !== "object") return null;
|
|
32
|
+
const { [TYPE_KEY]: type, ...props } = node;
|
|
33
|
+
if (typeof type !== "string") return null;
|
|
34
|
+
if ("children" in props) props["children"] = normalizeNode(props["children"], depth + 1);
|
|
35
|
+
return {
|
|
36
|
+
type,
|
|
37
|
+
props
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function renderNode(node, library, context) {
|
|
41
|
+
if (node == null || typeof node === "boolean") return null;
|
|
42
|
+
if (typeof node === "string" || typeof node === "number") return node;
|
|
43
|
+
if (Array.isArray(node)) return node.map((child, index) => /* @__PURE__ */ jsx(Fragment, { children: renderNode(child, library, context) }, `${index}:${nodeKind(child)}`));
|
|
44
|
+
return renderElement(node, library, context);
|
|
45
|
+
}
|
|
46
|
+
function renderElement(element, library, context) {
|
|
47
|
+
const entry = library[element.type];
|
|
48
|
+
if (!entry) {
|
|
49
|
+
reportUnknownComponent(element.type, Object.keys(library));
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
if (!entry.streamProperties && context.status === "streaming") return null;
|
|
53
|
+
const { children, ...rest } = element.props;
|
|
54
|
+
const props = {
|
|
55
|
+
...rest,
|
|
56
|
+
$status: context.status
|
|
57
|
+
};
|
|
58
|
+
if (children !== void 0) props["children"] = renderNode(children, library, context);
|
|
59
|
+
return /* @__PURE__ */ jsx(GenerativeUIComponentRenderer, {
|
|
60
|
+
render: entry.render,
|
|
61
|
+
props
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Mounts a single node's `render` on its own fiber so the function may use
|
|
66
|
+
* hooks and hold state independently of its siblings and parent.
|
|
67
|
+
*/
|
|
68
|
+
function GenerativeUIComponentRenderer({ render, props }) {
|
|
69
|
+
return render(props);
|
|
70
|
+
}
|
|
71
|
+
/** A coarse kind tag for a child, used in its list key so a node changing kind
|
|
72
|
+
* at a given index forces a remount rather than a wrong-fiber reuse. */
|
|
73
|
+
function nodeKind(node) {
|
|
74
|
+
if (node == null || typeof node === "boolean") return "";
|
|
75
|
+
if (typeof node === "string" || typeof node === "number") return "#text";
|
|
76
|
+
if (Array.isArray(node)) return "#array";
|
|
77
|
+
return node.type;
|
|
78
|
+
}
|
|
79
|
+
function reportUnknownComponent(type, available) {
|
|
80
|
+
if (process.env["NODE_ENV"] !== "production") console.error(`[@assistant-ui/react-generative-ui] Unknown component "${type}". Available components: ${available.join(", ") || "(none)"}.`);
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
export { renderGenerativeUI };
|
|
84
|
+
|
|
85
|
+
//# sourceMappingURL=renderGenerativeUI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderGenerativeUI.js","names":[],"sources":["../src/renderGenerativeUI.tsx"],"sourcesContent":["import { Fragment, type ReactNode } from \"react\";\nimport { TYPE_KEY } from \"./constants\";\nimport type {\n GenerativeUIElement,\n GenerativeUILibrary,\n GenerativeUINode,\n GenerativeUIRenderContext,\n} from \"./types\";\n\nconst DEFAULT_CONTEXT: GenerativeUIRenderContext = { status: \"done\" };\n\n/**\n * Renders a generative-ui tree against a {@link GenerativeUILibrary}.\n *\n * The model emits each node as a flat object `{ $type, ...props }`. We first\n * normalize that wire form into React-shaped elements (`{ type, props }`), then\n * render: each `type` is looked up in the library and its `props` are passed\n * to the component's `render(props, context)`, with `children` rendered\n * recursively so components can nest.\n */\nexport function renderGenerativeUI(\n node: unknown,\n library: GenerativeUILibrary,\n context: GenerativeUIRenderContext = DEFAULT_CONTEXT,\n): ReactNode {\n return renderNode(normalizeNode(node), library, context);\n}\n\n/**\n * The deepest tree we normalize. The input comes from the model, so a runaway\n * or adversarial response could nest arbitrarily deep and overflow the stack;\n * past this depth we stop (far beyond any real UI). Bounding normalization\n * bounds rendering too, since it only walks the normalized tree.\n */\nconst MAX_DEPTH = 64;\n\n/** Converts the flat wire form into a normalized {@link GenerativeUINode}. */\nfunction normalizeNode(node: unknown, depth = 0): GenerativeUINode {\n if (depth > MAX_DEPTH) return null;\n if (node == null || typeof node === \"boolean\") return null;\n if (typeof node === \"string\" || typeof node === \"number\") return node;\n if (Array.isArray(node))\n return node.map((child) => normalizeNode(child, depth + 1));\n if (typeof node !== \"object\") return null;\n\n const { [TYPE_KEY]: type, ...props } = node as Record<string, unknown>;\n // Args stream in incrementally; a node whose `$type` has not arrived yet is\n // not an error, it just isn't renderable.\n if (typeof type !== \"string\") return null;\n\n if (\"children\" in props) {\n props[\"children\"] = normalizeNode(props[\"children\"], depth + 1);\n }\n return { type, props } as GenerativeUIElement;\n}\n\nfunction renderNode(\n node: GenerativeUINode,\n library: GenerativeUILibrary,\n context: GenerativeUIRenderContext,\n): ReactNode {\n if (node == null || typeof node === \"boolean\") return null;\n if (typeof node === \"string\" || typeof node === \"number\") return node;\n if (Array.isArray(node)) {\n // The wire format has no per-node id, so the key is positional. Pairing the\n // index with the node's kind means that when the model splices or reorders\n // `children` and the kind at an index changes, the key changes and React\n // remounts instead of handing a streaming node's hook state to a different\n // component.\n return node.map((child, index) => (\n <Fragment key={`${index}:${nodeKind(child)}`}>\n {renderNode(child, library, context)}\n </Fragment>\n ));\n }\n return renderElement(node, library, context);\n}\n\nfunction renderElement(\n element: GenerativeUIElement,\n library: GenerativeUILibrary,\n context: GenerativeUIRenderContext,\n): ReactNode {\n const entry = library[element.type];\n if (!entry) {\n reportUnknownComponent(element.type, Object.keys(library));\n return null;\n }\n\n // Components that opt out of prop streaming wait until their props are\n // complete rather than rendering from a partial parse.\n if (!entry.streamProperties && context.status === \"streaming\") return null;\n\n // Inject the framework props last so the model can never override them.\n const { children, ...rest } = element.props;\n const props: Record<string, unknown> = { ...rest, $status: context.status };\n if (children !== undefined) {\n props[\"children\"] = renderNode(children, library, context);\n }\n\n return <GenerativeUIComponentRenderer render={entry.render} props={props} />;\n}\n\n/**\n * Mounts a single node's `render` on its own fiber so the function may use\n * hooks and hold state independently of its siblings and parent.\n */\nfunction GenerativeUIComponentRenderer({\n render,\n props,\n}: {\n render: (props: any) => ReactNode;\n props: Record<string, unknown>;\n}): ReactNode {\n return render(props);\n}\n\n/** A coarse kind tag for a child, used in its list key so a node changing kind\n * at a given index forces a remount rather than a wrong-fiber reuse. */\nfunction nodeKind(node: GenerativeUINode): string {\n if (node == null || typeof node === \"boolean\") return \"\";\n if (typeof node === \"string\" || typeof node === \"number\") return \"#text\";\n if (Array.isArray(node)) return \"#array\";\n return node.type;\n}\n\nfunction reportUnknownComponent(type: string, available: string[]): void {\n if (process.env[\"NODE_ENV\"] !== \"production\") {\n // eslint-disable-next-line no-console\n console.error(\n `[@assistant-ui/react-generative-ui] Unknown component \"${type}\". ` +\n `Available components: ${available.join(\", \") || \"(none)\"}.`,\n );\n }\n}\n"],"mappings":";;;;AASA,MAAM,kBAA6C,EAAE,QAAQ,OAAO;;;;;;;;;;AAWpE,SAAgB,mBACd,MACA,SACA,UAAqC,iBAC1B;CACX,OAAO,WAAW,cAAc,IAAI,GAAG,SAAS,OAAO;AACzD;;;;;;;AAQA,MAAM,YAAY;;AAGlB,SAAS,cAAc,MAAe,QAAQ,GAAqB;CACjE,IAAI,QAAQ,WAAW,OAAO;CAC9B,IAAI,QAAQ,QAAQ,OAAO,SAAS,WAAW,OAAO;CACtD,IAAI,OAAO,SAAS,YAAY,OAAO,SAAS,UAAU,OAAO;CACjE,IAAI,MAAM,QAAQ,IAAI,GACpB,OAAO,KAAK,KAAK,UAAU,cAAc,OAAO,QAAQ,CAAC,CAAC;CAC5D,IAAI,OAAO,SAAS,UAAU,OAAO;CAErC,MAAM,GAAG,WAAW,MAAM,GAAG,UAAU;CAGvC,IAAI,OAAO,SAAS,UAAU,OAAO;CAErC,IAAI,cAAc,OAChB,MAAM,cAAc,cAAc,MAAM,aAAa,QAAQ,CAAC;CAEhE,OAAO;EAAE;EAAM;CAAM;AACvB;AAEA,SAAS,WACP,MACA,SACA,SACW;CACX,IAAI,QAAQ,QAAQ,OAAO,SAAS,WAAW,OAAO;CACtD,IAAI,OAAO,SAAS,YAAY,OAAO,SAAS,UAAU,OAAO;CACjE,IAAI,MAAM,QAAQ,IAAI,GAMpB,OAAO,KAAK,KAAK,OAAO,UACtB,oBAAC,UAAD,EAAA,UACG,WAAW,OAAO,SAAS,OAAO,EAC3B,GAFK,GAAG,MAAM,GAAG,SAAS,KAAK,GAE/B,CACX;CAEH,OAAO,cAAc,MAAM,SAAS,OAAO;AAC7C;AAEA,SAAS,cACP,SACA,SACA,SACW;CACX,MAAM,QAAQ,QAAQ,QAAQ;CAC9B,IAAI,CAAC,OAAO;EACV,uBAAuB,QAAQ,MAAM,OAAO,KAAK,OAAO,CAAC;EACzD,OAAO;CACT;CAIA,IAAI,CAAC,MAAM,oBAAoB,QAAQ,WAAW,aAAa,OAAO;CAGtE,MAAM,EAAE,UAAU,GAAG,SAAS,QAAQ;CACtC,MAAM,QAAiC;EAAE,GAAG;EAAM,SAAS,QAAQ;CAAO;CAC1E,IAAI,aAAa,KAAA,GACf,MAAM,cAAc,WAAW,UAAU,SAAS,OAAO;CAG3D,OAAO,oBAAC,+BAAD;EAA+B,QAAQ,MAAM;EAAe;CAAQ,CAAA;AAC7E;;;;;AAMA,SAAS,8BAA8B,EACrC,QACA,SAIY;CACZ,OAAO,OAAO,KAAK;AACrB;;;AAIA,SAAS,SAAS,MAAgC;CAChD,IAAI,QAAQ,QAAQ,OAAO,SAAS,WAAW,OAAO;CACtD,IAAI,OAAO,SAAS,YAAY,OAAO,SAAS,UAAU,OAAO;CACjE,IAAI,MAAM,QAAQ,IAAI,GAAG,OAAO;CAChC,OAAO,KAAK;AACd;AAEA,SAAS,uBAAuB,MAAc,WAA2B;CACvE,IAAI,QAAQ,IAAI,gBAAgB,cAE9B,QAAQ,MACN,0DAA0D,KAAK,2BACpC,UAAU,KAAK,IAAI,KAAK,SAAS,EAC9D;AAEJ"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { ZodType } from "zod";
|
|
3
|
+
|
|
4
|
+
//#region src/types.d.ts
|
|
5
|
+
/** Whether a node's props are still streaming in or have fully arrived. */
|
|
6
|
+
type GenerativeUIStatus = "streaming" | "done";
|
|
7
|
+
/** The render context threaded through {@link renderGenerativeUI}. */
|
|
8
|
+
type GenerativeUIRenderContext = {
|
|
9
|
+
/** Whether the tool call's arguments are still streaming or are complete. */status: GenerativeUIStatus;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Props a component's `render` receives: its model props, rendered `children`,
|
|
13
|
+
* and the injected `$status`. `$status` is the discriminant — when it is
|
|
14
|
+
* `"done"`, `P` is complete; while `"streaming"`, `P` is partial. It is named
|
|
15
|
+
* `$status` (not `status`) so it never collides with a real `status` prop, the
|
|
16
|
+
* same reservation as `$type`.
|
|
17
|
+
*/
|
|
18
|
+
type StreamingRenderProps<P> = (Partial<P> & {
|
|
19
|
+
children?: ReactNode;
|
|
20
|
+
$status: "streaming";
|
|
21
|
+
}) | (P & {
|
|
22
|
+
children?: ReactNode;
|
|
23
|
+
$status: "done";
|
|
24
|
+
});
|
|
25
|
+
/** Props for a component that only renders once complete — `$status` is always `"done"`. */
|
|
26
|
+
type StaticRenderProps<P> = P & {
|
|
27
|
+
children?: ReactNode;
|
|
28
|
+
$status: "done";
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* A component the model is allowed to render, with the schema for its props.
|
|
32
|
+
*
|
|
33
|
+
* Components opt into prop streaming with `streamProperties`: they render as
|
|
34
|
+
* props arrive, so `render` sees `Partial<P>` while `$status` is `"streaming"`
|
|
35
|
+
* and the full `P` once it is `"done"`. By default a component opts out and is
|
|
36
|
+
* only rendered once its props are complete.
|
|
37
|
+
*
|
|
38
|
+
* `render` is a function of `props`, not a React component. Direct hook use is
|
|
39
|
+
* fine (each node is mounted on its own fiber), but a given node must keep its
|
|
40
|
+
* `type` across renders for hook identity to be stable.
|
|
41
|
+
*/
|
|
42
|
+
type GenerativeUIComponent<P = any> = {
|
|
43
|
+
/** Natural-language description shown to the model when selecting the component. */description: string; /** Schema for the props the model must provide. Drives the tool parameters. */
|
|
44
|
+
properties: ZodType<P>;
|
|
45
|
+
/**
|
|
46
|
+
* Render from partially-streamed props. Widened to `boolean | undefined`
|
|
47
|
+
* so a non-literal value (e.g. a variable) still resolves to this branch
|
|
48
|
+
* cleanly rather than matching neither; the strict `false | undefined`
|
|
49
|
+
* branch below is the only one that promises complete props.
|
|
50
|
+
*/
|
|
51
|
+
streamProperties: boolean | undefined;
|
|
52
|
+
render: (props: StreamingRenderProps<P>) => ReactNode;
|
|
53
|
+
} | {
|
|
54
|
+
description: string;
|
|
55
|
+
properties: ZodType<P>; /** Render only once props are complete (the default). */
|
|
56
|
+
streamProperties?: false | undefined;
|
|
57
|
+
render: (props: StaticRenderProps<P>) => ReactNode;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* The consumer-provided allowlist of components the model is permitted to
|
|
61
|
+
* render. Keys are the `type` values referenced in the generative-ui tree
|
|
62
|
+
* (e.g. `"Card"`, `"Button"`); values describe each component.
|
|
63
|
+
*
|
|
64
|
+
* This registry is the security boundary — any `type` not present is rejected.
|
|
65
|
+
*/
|
|
66
|
+
type GenerativeUILibrary = Record<string, GenerativeUIComponent>;
|
|
67
|
+
/**
|
|
68
|
+
* A component invocation — mirrors React's `ReactElement`.
|
|
69
|
+
*
|
|
70
|
+
* `type` selects a component from the {@link GenerativeUILibrary}; `props` are
|
|
71
|
+
* passed to it. The `children` prop is special: it is itself a renderable
|
|
72
|
+
* {@link GenerativeUINode}, drawn as generative UI rather than passed as data.
|
|
73
|
+
*
|
|
74
|
+
* On the wire the model emits the flattened form `{ $type, ...props }`, where
|
|
75
|
+
* `$type` names the component so a real `type` prop never collides. This is the
|
|
76
|
+
* normalized shape the renderer works with, the same way React normalizes
|
|
77
|
+
* `createElement` arguments into an element.
|
|
78
|
+
*/
|
|
79
|
+
type GenerativeUIElement = {
|
|
80
|
+
type: string;
|
|
81
|
+
props: GenerativeUIProps;
|
|
82
|
+
};
|
|
83
|
+
/** Props passed to a component — mirrors a React component's props. */
|
|
84
|
+
type GenerativeUIProps = {
|
|
85
|
+
children?: GenerativeUINode;
|
|
86
|
+
} & Record<string, unknown>;
|
|
87
|
+
/**
|
|
88
|
+
* Anything renderable as generative UI — mirrors React's `ReactNode`: an
|
|
89
|
+
* element, primitive text, or a list of nodes.
|
|
90
|
+
*/
|
|
91
|
+
type GenerativeUINode = GenerativeUIElement | string | number | boolean | null | undefined | GenerativeUINode[];
|
|
92
|
+
//#endregion
|
|
93
|
+
export { GenerativeUIComponent, GenerativeUIElement, GenerativeUILibrary, GenerativeUINode, GenerativeUIProps, GenerativeUIRenderContext, GenerativeUIStatus };
|
|
94
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;KAIY,kBAAA;AAAZ;AAAA,KAGY,yBAAA;+EAEV,MAAA,EAAQ,kBAAkB;AAAA;AAF5B;;;;AAE4B;AAC1B;;AAHF,KAYK,oBAAA,OACA,OAAA,CAAQ,CAAA;EAAO,QAAA,GAAW,SAAA;EAAW,OAAA;AAAA,MACrC,CAAA;EAAM,QAAA,GAAW,SAAA;EAAW,OAAA;AAAA;;KAG5B,iBAAA,MAAuB,CAAA;EAAM,QAAA,GAAW,SAAS;EAAE,OAAA;AAAA;;;;;;;;AAHhB;AAAA;;;;KAiB5B,qBAAA;EAdgB,oFAiBtB,WAAA,UAjBuC;EAmBvC,UAAA,EAAY,OAAA,CAAQ,CAAA;EAnBqC;AAAA;AAc/D;;;;EAYM,gBAAA;EACA,MAAA,GAAS,KAAA,EAAO,oBAAA,CAAqB,CAAA,MAAO,SAAA;AAAA;EAG5C,WAAA;EACA,UAAA,EAAY,OAAA,CAAQ,CAAA,GAAR;EAEZ,gBAAA;EACA,MAAA,GAAS,KAAA,EAAO,iBAAA,CAAkB,CAAA,MAAO,SAAA;AAAA;;;;;;;;KAUnC,mBAAA,GAAsB,MAAM,SAAS,qBAAA;;;;;;;;;;;;;KAcrC,mBAAA;EACV,IAAA;EACA,KAAA,EAAO,iBAAiB;AAAA;AA1B8B;AAAA,KA8B5C,iBAAA;EACV,QAAA,GAAW,gBAAA;AAAA,IACT,MAAM;;AAtB4D;AActE;;KAcY,gBAAA,GACR,mBAAA,kDAMA,gBAAgB"}
|
package/dist/types.js
ADDED
|
File without changes
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@assistant-ui/react-generative-ui",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "Generative UI components for assistant-ui",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"generative-ui",
|
|
7
|
+
"assistant-ui",
|
|
8
|
+
"react",
|
|
9
|
+
"ai",
|
|
10
|
+
"chat",
|
|
11
|
+
"rendering"
|
|
12
|
+
],
|
|
13
|
+
"author": "AgentbaseAI Inc.",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"default": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./internal-json": {
|
|
22
|
+
"react-server": {
|
|
23
|
+
"types": "./dist/JSONGenerativeUI.server.d.ts",
|
|
24
|
+
"default": "./dist/JSONGenerativeUI.server.js"
|
|
25
|
+
},
|
|
26
|
+
"default": {
|
|
27
|
+
"types": "./dist/JSONGenerativeUI.client.d.ts",
|
|
28
|
+
"default": "./dist/JSONGenerativeUI.client.js"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"main": "./dist/index.js",
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"files": [
|
|
35
|
+
"dist",
|
|
36
|
+
"src",
|
|
37
|
+
"README.md"
|
|
38
|
+
],
|
|
39
|
+
"sideEffects": false,
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"assistant-stream": "^0.3.20"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@assistant-ui/react": "^0.14.14",
|
|
45
|
+
"@types/react": "*",
|
|
46
|
+
"react": "^18 || ^19",
|
|
47
|
+
"zod": "^4.0.0"
|
|
48
|
+
},
|
|
49
|
+
"peerDependenciesMeta": {
|
|
50
|
+
"@types/react": {
|
|
51
|
+
"optional": true
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@types/json-schema": "^7.0.15",
|
|
56
|
+
"@types/react": "^19.2.15",
|
|
57
|
+
"react": "^19.2.6",
|
|
58
|
+
"react-dom": "^19.2.6",
|
|
59
|
+
"vitest": "^4.1.7",
|
|
60
|
+
"zod": "^4.4.3",
|
|
61
|
+
"@assistant-ui/x-buildutils": "0.0.11",
|
|
62
|
+
"@assistant-ui/react": "0.14.14"
|
|
63
|
+
},
|
|
64
|
+
"publishConfig": {
|
|
65
|
+
"access": "public",
|
|
66
|
+
"provenance": true
|
|
67
|
+
},
|
|
68
|
+
"homepage": "https://www.assistant-ui.com/",
|
|
69
|
+
"repository": {
|
|
70
|
+
"type": "git",
|
|
71
|
+
"url": "git+https://github.com/assistant-ui/assistant-ui.git",
|
|
72
|
+
"directory": "packages/react-generative-ui"
|
|
73
|
+
},
|
|
74
|
+
"bugs": {
|
|
75
|
+
"url": "https://github.com/assistant-ui/assistant-ui/issues"
|
|
76
|
+
},
|
|
77
|
+
"scripts": {
|
|
78
|
+
"build": "aui-build",
|
|
79
|
+
"test": "vitest run",
|
|
80
|
+
"test:watch": "vitest"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { buildPresentParameters } from "./buildPresentParameters";
|
|
3
|
+
import {
|
|
4
|
+
presentToolBase,
|
|
5
|
+
promptUserToolBase,
|
|
6
|
+
type JSONGenerativeUIOptions,
|
|
7
|
+
type PresentParameters,
|
|
8
|
+
type PresentTool,
|
|
9
|
+
type PresentToolOptions,
|
|
10
|
+
type PromptUserTool,
|
|
11
|
+
} from "./JSONGenerativeUI.shared";
|
|
12
|
+
import { renderGenerativeUI } from "./renderGenerativeUI";
|
|
13
|
+
import type { GenerativeUILibrary, GenerativeUIStatus } from "./types";
|
|
14
|
+
|
|
15
|
+
/** Maps a tool-call part status to the generative-UI streaming status. Only a
|
|
16
|
+
* `complete` call has fully-arrived args; `running` and `incomplete`
|
|
17
|
+
* (aborted/errored, so args may be partial) both render as `"streaming"` so a
|
|
18
|
+
* non-streaming component is never handed partial props. */
|
|
19
|
+
function uiStatus(status: { type: string }): GenerativeUIStatus {
|
|
20
|
+
return status.type === "complete" ? "done" : "streaming";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Client build of {@link JSONGenerativeUI}, resolved through the package's
|
|
25
|
+
* `default` export condition (browser and SSR).
|
|
26
|
+
*
|
|
27
|
+
* `present` is a frontend tool that renders the model's `{ $type, ...props }`
|
|
28
|
+
* tree against the library and resolves immediately. `prompt_user` is a
|
|
29
|
+
* human-in-the-loop tool: the model pauses and the rendered UI supplies the
|
|
30
|
+
* result. Both draw the tree the same way, so they share one `render`.
|
|
31
|
+
*/
|
|
32
|
+
export class JSONGenerativeUI {
|
|
33
|
+
private readonly library: GenerativeUILibrary;
|
|
34
|
+
private readonly parameters: PresentParameters;
|
|
35
|
+
|
|
36
|
+
constructor(options: JSONGenerativeUIOptions) {
|
|
37
|
+
this.library = options.library;
|
|
38
|
+
this.parameters = buildPresentParameters(options.library);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private readonly render = ({
|
|
42
|
+
args,
|
|
43
|
+
status,
|
|
44
|
+
}: {
|
|
45
|
+
args: unknown;
|
|
46
|
+
status: { type: string };
|
|
47
|
+
}): ReactNode =>
|
|
48
|
+
renderGenerativeUI(args, this.library, { status: uiStatus(status) });
|
|
49
|
+
|
|
50
|
+
present(options?: PresentToolOptions): PresentTool {
|
|
51
|
+
return {
|
|
52
|
+
...presentToolBase(this.parameters, options),
|
|
53
|
+
unstable_backendDefault: { parameters: true },
|
|
54
|
+
execute: async () => ({}),
|
|
55
|
+
render: this.render,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
promptUser(): PromptUserTool {
|
|
60
|
+
return {
|
|
61
|
+
...promptUserToolBase(this.parameters),
|
|
62
|
+
unstable_backendDefault: { parameters: true },
|
|
63
|
+
render: this.render,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|