@assistant-ui/react-generative-ui 0.0.2 → 0.0.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"buildPresentParameters.js","names":[],"sources":["../src/buildPresentParameters.ts"],"sourcesContent":["import { toJSONSchema } from \"assistant-stream\";\nimport type { JSONSchema7, JSONSchema7Definition } from \"json-schema\";\nimport { TYPE_KEY } from \"./constants\";\nimport type { GenerativeUILibrary } from \"./types\";\n\n/**\n * Builds the JSON schema for the `present` tool from a {@link GenerativeUILibrary}.\n *\n * The model produces a node `{ $type, ...props }` where `$type` selects a\n * component and the rest are its props. The schema is a flat object: `$type` is\n * an enum of the component names, every component's props are merged into one\n * optional bag, and `children` recurses via `$defs` so the tree can nest.\n *\n * It is intentionally flat rather than a per-`$type` discriminated union. Tool /\n * function-call schemas (OpenAI and others) require the top-level parameters to\n * be a plain object and reject a top-level `oneOf`/`anyOf`/`enum`. So props can't\n * be refined per `$type` at the root; the model is guided instead by `$type`'s\n * description (which lists each component) and each prop's own description. The\n * renderer validates nothing here — an unknown `$type` or stray prop is handled\n * at render time — so a looser schema only costs the model a hint, not safety.\n */\nexport function buildPresentParameters(\n library: GenerativeUILibrary,\n): JSONSchema7 {\n const names = Object.keys(library);\n\n // Merge every component's props into one optional bag. `$type`/`children` are\n // framework-reserved, so drop any author-declared copies. On a name clash the\n // first component's schema wins — props are an advisory hint here, not a\n // strict per-component contract.\n const props: Record<string, JSONSchema7Definition> = {};\n for (const name of names) {\n const propsSchema = toJSONSchema(library[name]!.properties);\n if (propsSchema.type !== \"object\") {\n throw new Error(\n `[@assistant-ui/react-generative-ui] Component \"${name}\": ` +\n \"`properties` must be an object schema (e.g. `z.object({ ... })`).\",\n );\n }\n for (const [key, schema] of Object.entries(propsSchema.properties ?? {})) {\n if (key === TYPE_KEY || key === \"children\") continue;\n if (!(key in props)) {\n props[key] = schema;\n } else if (process.env[\"NODE_ENV\"] !== \"production\") {\n // eslint-disable-next-line no-console\n console.warn(\n `[@assistant-ui/react-generative-ui] Prop \"${key}\" is declared by more ` +\n \"than one component; the first component's schema is kept and the rest \" +\n \"are ignored. Rename or align the type to avoid an ambiguous schema.\",\n );\n }\n }\n }\n\n // Carry each component's description on the `$type` enum, since there are no\n // per-branch schemas to hang them on anymore.\n const typeDescription =\n names.length > 0\n ? `The component to render. ${names\n .map((name) => `\"${name}\": ${library[name]!.description}`)\n .join(\"; \")}`\n : \"The component to render.\";\n\n const node: JSONSchema7 = {\n type: \"object\",\n properties: {\n [TYPE_KEY]: { type: \"string\", enum: names, description: typeDescription },\n ...props,\n children: { $ref: \"#/$defs/children\" },\n },\n required: [TYPE_KEY],\n };\n\n const children: JSONSchema7 = {\n description: \"Nested generative UI rendered inside this component.\",\n anyOf: [\n { type: \"string\" },\n { $ref: \"#/$defs/node\" },\n {\n type: \"array\",\n items: { anyOf: [{ type: \"string\" }, { $ref: \"#/$defs/node\" }] },\n },\n ],\n };\n\n return {\n ...node,\n $defs: { node, children },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,uBACd,SACa;CACb,MAAM,QAAQ,OAAO,KAAK,OAAO;CAMjC,MAAM,QAA+C,CAAC;CACtD,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,cAAc,aAAa,QAAQ,MAAO,UAAU;EAC1D,IAAI,YAAY,SAAS,UACvB,MAAM,IAAI,MACR,kDAAkD,KAAK,yEAEzD;EAEF,KAAK,MAAM,CAAC,KAAK,WAAW,OAAO,QAAQ,YAAY,cAAc,CAAC,CAAC,GAAG;GACxE,IAAI,QAAA,WAAoB,QAAQ,YAAY;GAC5C,IAAI,EAAE,OAAO,QACX,MAAM,OAAO;QACR,IAAI,QAAQ,IAAI,gBAAgB,cAErC,QAAQ,KACN,6CAA6C,IAAI,gKAGnD;EAEJ;CACF;CAIA,MAAM,kBACJ,MAAM,SAAS,IACX,4BAA4B,MACzB,KAAK,SAAS,IAAI,KAAK,KAAK,QAAQ,MAAO,aAAa,EACxD,KAAK,IAAI,MACZ;CAEN,MAAM,OAAoB;EACxB,MAAM;EACN,YAAY;IACT,WAAW;IAAE,MAAM;IAAU,MAAM;IAAO,aAAa;GAAgB;GACxE,GAAG;GACH,UAAU,EAAE,MAAM,mBAAmB;EACvC;EACA,UAAU,CAAC,QAAQ;CACrB;CAEA,MAAM,WAAwB;EAC5B,aAAa;EACb,OAAO;GACL,EAAE,MAAM,SAAS;GACjB,EAAE,MAAM,eAAe;GACvB;IACE,MAAM;IACN,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,SAAS,GAAG,EAAE,MAAM,eAAe,CAAC,EAAE;GACjE;EACF;CACF;CAEA,OAAO;EACL,GAAG;EACH,OAAO;GAAE;GAAM;EAAS;CAC1B;AACF"}
1
+ {"version":3,"file":"buildPresentParameters.js","names":[],"sources":["../src/buildPresentParameters.ts"],"sourcesContent":["import { toJSONSchema } from \"assistant-stream\";\nimport type { JSONSchema7, JSONSchema7Definition } from \"json-schema\";\nimport { TYPE_KEY } from \"./constants\";\nimport type { GenerativeUILibrary } from \"./types\";\n\n/**\n * Builds the JSON schema for the `present` tool from a {@link GenerativeUILibrary}.\n *\n * The model produces a node `{ $type, ...props }` where `$type` selects a\n * component and the rest are its props. The schema is a flat object: `$type` is\n * an enum of the component names, every component's props are merged into one\n * optional bag, and `children` recurses via `$defs` so the tree can nest.\n *\n * It is intentionally flat rather than a per-`$type` discriminated union. Tool /\n * function-call schemas (OpenAI and others) require the top-level parameters to\n * be a plain object and reject a top-level `oneOf`/`anyOf`/`enum`. So props can't\n * be refined per `$type` at the root; the model is guided instead by `$type`'s\n * description (which lists each component) and each prop's own description. The\n * renderer validates nothing here — an unknown `$type` or stray prop is handled\n * at render time — so a looser schema only costs the model a hint, not safety.\n */\nexport function buildPresentParameters(\n library: GenerativeUILibrary,\n): JSONSchema7 {\n const names = Object.keys(library);\n\n // Merge every component's props into one optional bag. `$type`/`children` are\n // framework-reserved, so drop any author-declared copies. On a name clash the\n // first component's schema wins — props are an advisory hint here, not a\n // strict per-component contract.\n const props: Record<string, JSONSchema7Definition> = {};\n for (const name of names) {\n const propsSchema = toJSONSchema(library[name]!.properties);\n if (propsSchema.type !== \"object\") {\n throw new Error(\n `[@assistant-ui/react-generative-ui] Component \"${name}\": ` +\n \"`properties` must be an object schema (e.g. `z.object({ ... })`).\",\n );\n }\n for (const [key, schema] of Object.entries(propsSchema.properties ?? {})) {\n if (key === TYPE_KEY || key === \"children\") continue;\n if (!(key in props)) {\n props[key] = schema;\n } else if (process.env[\"NODE_ENV\"] !== \"production\") {\n // eslint-disable-next-line no-console\n console.warn(\n `[@assistant-ui/react-generative-ui] Prop \"${key}\" is declared by more ` +\n \"than one component; the first component's schema is kept and the rest \" +\n \"are ignored. Rename or align the type to avoid an ambiguous schema.\",\n );\n }\n }\n }\n\n // Carry each component's description on the `$type` enum, since there are no\n // per-branch schemas to hang them on anymore.\n const typeDescription =\n names.length > 0\n ? `The component to render. ${names\n .map((name) => `\"${name}\": ${library[name]!.description}`)\n .join(\"; \")}`\n : \"The component to render.\";\n\n const node: JSONSchema7 = {\n type: \"object\",\n properties: {\n [TYPE_KEY]: { type: \"string\", enum: names, description: typeDescription },\n ...props,\n children: { $ref: \"#/$defs/children\" },\n },\n required: [TYPE_KEY],\n };\n\n const children: JSONSchema7 = {\n description: \"Nested generative UI rendered inside this component.\",\n anyOf: [\n { type: \"string\" },\n { $ref: \"#/$defs/node\" },\n {\n type: \"array\",\n items: { anyOf: [{ type: \"string\" }, { $ref: \"#/$defs/node\" }] },\n },\n ],\n };\n\n return {\n ...node,\n $defs: { node, children },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,uBACd,SACa;CACb,MAAM,QAAQ,OAAO,KAAK,OAAO;CAMjC,MAAM,QAA+C,CAAC;CACtD,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,cAAc,aAAa,QAAQ,KAAK,CAAE,UAAU;EAC1D,IAAI,YAAY,SAAS,UACvB,MAAM,IAAI,MACR,kDAAkD,KAAK,yEAEzD;EAEF,KAAK,MAAM,CAAC,KAAK,WAAW,OAAO,QAAQ,YAAY,cAAc,CAAC,CAAC,GAAG;GACxE,IAAI,QAAA,WAAoB,QAAQ,YAAY;GAC5C,IAAI,EAAE,OAAO,QACX,MAAM,OAAO;QACR,IAAI,QAAQ,IAAI,gBAAgB,cAErC,QAAQ,KACN,6CAA6C,IAAI,gKAGnD;EAEJ;CACF;CAIA,MAAM,kBACJ,MAAM,SAAS,IACX,4BAA4B,MACzB,KAAK,SAAS,IAAI,KAAK,KAAK,QAAQ,KAAK,CAAE,aAAa,CAAC,CACzD,KAAK,IAAI,MACZ;CAEN,MAAM,OAAoB;EACxB,MAAM;EACN,YAAY;IACT,WAAW;IAAE,MAAM;IAAU,MAAM;IAAO,aAAa;GAAgB;GACxE,GAAG;GACH,UAAU,EAAE,MAAM,mBAAmB;EACvC;EACA,UAAU,CAAC,QAAQ;CACrB;CAEA,MAAM,WAAwB;EAC5B,aAAa;EACb,OAAO;GACL,EAAE,MAAM,SAAS;GACjB,EAAE,MAAM,eAAe;GACvB;IACE,MAAM;IACN,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,SAAS,GAAG,EAAE,MAAM,eAAe,CAAC,EAAE;GACjE;EACF;CACF;CAEA,OAAO;EACL,GAAG;EACH,OAAO;GAAE;GAAM;EAAS;CAC1B;AACF"}
@@ -1 +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"}
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,CAAC,CAAC,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,CAAC,CAChC,KAAK,CAAC,KAAK,WAAW,WAAW,KAAK,KAAK,CAAC,CAAC,CAC7C,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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react-generative-ui",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Generative UI components for assistant-ui",
5
5
  "keywords": [
6
6
  "generative-ui",
@@ -38,7 +38,7 @@
38
38
  ],
39
39
  "sideEffects": false,
40
40
  "dependencies": {
41
- "assistant-stream": "^0.3.20"
41
+ "assistant-stream": "^0.3.21"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "@assistant-ui/react": "^0.14.14",
@@ -53,13 +53,13 @@
53
53
  },
54
54
  "devDependencies": {
55
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",
56
+ "@types/react": "^19.2.17",
57
+ "react": "^19.2.7",
58
+ "react-dom": "^19.2.7",
59
+ "vitest": "^4.1.8",
60
60
  "zod": "^4.4.3",
61
- "@assistant-ui/x-buildutils": "0.0.11",
62
- "@assistant-ui/react": "0.14.14"
61
+ "@assistant-ui/react": "0.14.15",
62
+ "@assistant-ui/x-buildutils": "0.0.12"
63
63
  },
64
64
  "publishConfig": {
65
65
  "access": "public",