getsyntux 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  https://github.com/user-attachments/assets/a930d35d-92ab-45f4-9f71-f7bc0380c4f1
10
10
 
11
11
  - ⚡ **Streamable** - display UI as you generate.
12
- - 🎨 **Custom Components** - maintain aesthetics and reusability.
12
+ - 🎨 **Custom Components** - use your own React components.
13
13
  - 💾 **Cacheable** - reuse generated UIs with new values.
14
14
 
15
15
  ⚠️ this library is still in **beta**. All npm versions are **stable**, but the API may change across versions.
@@ -61,7 +61,9 @@ $ npm i @ai-sdk/anthropic (if you're using Claude)
61
61
 
62
62
  ### Examples
63
63
 
64
- Generate a UI based on `valueToDisplay` (can be anything, including arrays):
64
+ #### Basic Example
65
+
66
+ Generate a simple UI with a hint:
65
67
 
66
68
  ```jsx
67
69
  import { GeneratedUI } from "@/lib/getsyntux/GeneratedUI";
@@ -76,7 +78,9 @@ export default function Home(){
76
78
  }
77
79
  ```
78
80
 
79
- Cache generated UI for different `value`s based on a user ID:
81
+ #### Caching
82
+
83
+ Cache generated UI based on a user ID:
80
84
 
81
85
  ```jsx
82
86
  const cache: Map<number, string> = new Map();
@@ -89,7 +93,9 @@ export default function Home(){
89
93
  }
90
94
  ```
91
95
 
92
- Use custom React components:
96
+ #### Custom components
97
+
98
+ Use your own components, or someone else's (a library):
93
99
 
94
100
  ```jsx
95
101
  import { CustomOne, CustomTwo } from '@/my_components'
@@ -103,6 +109,12 @@ export default function Home(){
103
109
  }
104
110
  ```
105
111
 
112
+ The component definitions (the `components` array above) can be generated automatically:
113
+
114
+ ```
115
+ $ npx getsyntux generate-defs ./path/to/component.tsx
116
+ ```
117
+
106
118
  See the [documentation](https://github.com/puffinsoft/syntux/wiki) for an in-depth explanation.
107
119
 
108
120
  ---
package/dist/client.mjs CHANGED
@@ -1,3 +1,3 @@
1
- "use client";import{readStreamableValue as N}from"@ai-sdk/rsc";import{useEffect as _,useRef as A,useState as I}from"react";import{Fragment as T}from"react";import{Fragment as C,jsx as d,jsxs as x}from"react/jsx-runtime";import{createElement as v}from"react";var S=(t,n)=>n==="$"?t:n.split(".").reduce((e,r)=>e==null?void 0:e[r],t),g=(t,n,e)=>e.startsWith("$item.")?(e=e.slice(6),S(n,e)):e==="$item"?n:S(t,e),R=(t,n,e)=>e&&("$bind"in e?g(t,n,e.$bind):(Object.keys(e).forEach(r=>{let a=e[r];typeof a=="object"&&(e[r]=R(t,n,a))}),e)),M=(t,n,e)=>typeof e=="object"?g(t,n,e.$bind):e,U=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]);function h(t){var u;let{id:n,componentMap:e,childrenMap:r,global:a,local:l,allowedComponents:i}=t,o=e[n];if(o.type==="TEXT")return d(C,{children:M(a,l,o.content)});let s=(u=o.props)==null?void 0:u.source;if(o.type==="__ForEach__"&&s){let f=g(a,l,s);if(!Array.isArray(f))return null;let p=r[o.id];return d(C,{children:p==null?void 0:p.map((E,P)=>d(T,{children:f.map(($,k)=>v(h,{...t,id:E,local:$,key:k}))},P))})}let m=i[o.type]||o.type,c=R(a,l,o.props);return typeof m=="string"&&U.has(m)?d(m,{...c}):x(m,{...c,children:[M(a,l,o.content),r[o.id]&&r[o.id].map((f,p)=>d(h,{...t,id:f},p))]})}var y=class{buffer="";schema={childrenMap:{},componentMap:{},root:null};addDelta(n){this.buffer+=n;let e=this.buffer.split(`
2
- `);e.length>1&&(e.slice(0,e.length-1).forEach(r=>this.handleLine(r)),this.buffer=e[e.length-1])}handleLine(n){try{let e=JSON.parse(n),{childrenMap:r,componentMap:a}=this.schema;a[e.id]=e,e.parentId===null?this.schema.root=e:(r[e.parentId]||(r[e.parentId]=[]),r[e.parentId].push(e.id))}catch{}}finish(){this.handleLine(this.buffer),this.buffer=""}};import{Fragment as w,jsx as b}from"react/jsx-runtime";function H({value:t,allowedComponents:n,inputStream:e,placeholder:r}){let[a,l]=I(""),[i,o]=I(),s=A(null);return _(()=>{s.current||(s.current=new y),(async()=>{for await(let c of N(e))l(u=>u+c),s.current&&c!==void 0&&(s.current.addDelta(c),o(s.current.schema))})()},[e]),b(w,{children:i!=null&&i.root?b(h,{id:i.root.id,componentMap:i.componentMap,childrenMap:i.childrenMap,allowedComponents:n,global:t,local:t}):b(w,{children:r})})}export{H as GeneratedClient,h as Renderer};
1
+ "use client";import{readStreamableValue as N}from"@ai-sdk/rsc";import{useEffect as _,useReducer as A,useRef as F}from"react";import{Fragment as k}from"react";import{Fragment as S,jsx as p,jsxs as x}from"react/jsx-runtime";import{createElement as v}from"react";var M=(t,n)=>n==="$"?t:n.split(".").reduce((e,r)=>e==null?void 0:e[r],t),h=(t,n,e)=>e.startsWith("$item.")?(e=e.slice(6),M(n,e)):e==="$item"?n:M(t,e),R=(t,n,e)=>e&&("$bind"in e?h(t,n,e.$bind):(Object.keys(e).forEach(r=>{let i=e[r];typeof i=="object"&&(e[r]=R(t,n,i))}),e)),C=(t,n,e)=>typeof e=="object"?h(t,n,e.$bind):e,T=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]);function d(t){var g;let{id:n,componentMap:e,childrenMap:r,global:i,local:a,allowedComponents:s}=t,o=e[n];if(o.type==="TEXT")return p(S,{children:C(i,a,o.content)});let c=(g=o.props)==null?void 0:g.source;if(o.type==="__ForEach__"&&c){let u=h(i,a,c);if(!Array.isArray(u))return null;let m=r[o.id];return p(S,{children:m==null?void 0:m.map((E,P)=>p(k,{children:u.map((I,$)=>v(d,{...t,id:E,local:I,key:$}))},P))})}let l=s[o.type]||o.type,b=R(i,a,o.props);return typeof l=="string"&&T.has(l)?p(l,{...b}):x(l,{...b,children:[C(i,a,o.content),r[o.id]&&r[o.id].map((u,m)=>p(d,{...t,id:u},m))]})}var f=class{buffer="";schema={childrenMap:{},componentMap:{},root:null};addDelta(n){this.buffer+=n;let e=this.buffer.split(`
2
+ `);return e.length>1?(e.slice(0,e.length-1).forEach(r=>this.handleLine(r)),this.buffer=e[e.length-1],!0):!1}handleLine(n){try{let e=JSON.parse(n),{childrenMap:r,componentMap:i}=this.schema;i[e.id]=e,e.parentId===null?this.schema.root=e:(r[e.parentId]||(r[e.parentId]=[]),r[e.parentId].push(e.id))}catch{}}finish(){this.handleLine(this.buffer),this.buffer=""}};import{Fragment as w,jsx as y}from"react/jsx-runtime";function K({value:t,allowedComponents:n,inputStream:e,placeholder:r}){var o;let[,i]=A(c=>c+1,0),a=F(null);_(()=>{a.current||(a.current=new f),(async()=>{for await(let l of N(e))a.current&&l!==void 0&&a.current.addDelta(l)&&i()})().then(()=>{a.current.finish(),i()})},[e]);let s=(o=a==null?void 0:a.current)==null?void 0:o.schema;return y(w,{children:s!=null&&s.root?y(d,{id:s.root.id,componentMap:s.componentMap,childrenMap:s.childrenMap,allowedComponents:n,global:t,local:t}):y(w,{children:r})})}export{K as GeneratedClient,d as Renderer};
3
3
  //# sourceMappingURL=client.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/GeneratedClient.tsx","../src/client/Renderer.tsx","../src/ResponseParser.ts"],"sourcesContent":["\"use client\";\r\n\r\nimport { StreamableValue, readStreamableValue } from '@ai-sdk/rsc';\r\nimport React, { JSX, useEffect, useRef, useState } from 'react';\r\nimport { Renderer } from './Renderer';\r\nimport { ResponseParser } from '../ResponseParser';\r\nimport { UISchema } from '../types';\r\n\r\n/**\r\n * Client wrapper for Renderer that handles streaming and parsing with server.\r\n */\r\nexport function GeneratedClient({\r\n value,\r\n allowedComponents,\r\n inputStream,\r\n placeholder\r\n}: {\r\n value: any,\r\n allowedComponents: Record<string, React.ComponentType<any> | string>,\r\n inputStream: StreamableValue<string>,\r\n placeholder?: JSX.Element\r\n}) {\r\n const [message, setMessage] = useState('');\r\n const [schema, setUISchema] = useState<UISchema | null>();\r\n const parser = useRef<ResponseParser | null>(null);\r\n\r\n useEffect(() => {\r\n if (!parser.current) {\r\n parser.current = new ResponseParser();\r\n }\r\n\r\n const parseStream = async () => {\r\n for await (const delta of readStreamableValue(inputStream)) {\r\n setMessage((prev) => prev + delta);\r\n if (parser.current && delta !== undefined) {\r\n parser.current.addDelta(delta);\r\n setUISchema(parser.current.schema);\r\n }\r\n }\r\n };\r\n\r\n parseStream();\r\n }, [inputStream]);\r\n\r\n return (\r\n <>\r\n {schema?.root ?\r\n <Renderer id={schema.root.id} componentMap={schema.componentMap} childrenMap={schema.childrenMap} allowedComponents={allowedComponents} global={value} local={value} />\r\n : <>{placeholder}</>}\r\n </>\r\n )\r\n}\r\n","\"use client\";\r\n\r\nimport { ComponentType, Fragment } from 'react'\r\nimport { ChildrenMap, ComponentMap, SchemaNode } from '../types';\r\n\r\n/**\r\n * lightweight implementation of lodash.get\r\n */\r\nconst resolvePath = (obj: any, path: string) => {\r\n if (path === '$') return obj;\r\n return path.split('.').reduce((acc, curr) => acc?.[curr], obj)\r\n}\r\n\r\n/**\r\n * parses binding protocol and performs property lookup w/ scope resolution\r\n */\r\nconst get = (global: any, local: any, path: string) => {\r\n if (path.startsWith(\"$item.\")) {\r\n path = path.slice(6)\r\n return resolvePath(local, path);\r\n } else {\r\n if (path === \"$item\") return local;\r\n return resolvePath(global, path);\r\n }\r\n}\r\n\r\n/**\r\n * recursively parses props for bindings, replacing with true values\r\n */\r\nconst resolveProps = (global: any, local: any, props: any) => {\r\n if (!props) return props;\r\n if (\"$bind\" in props) return get(global, local, props.$bind); // $bind may be falsy value\r\n Object.keys(props).forEach((key) => {\r\n const val = props[key];\r\n if (typeof val === \"object\") {\r\n props[key] = resolveProps(global, local, val);\r\n }\r\n })\r\n return props;\r\n}\r\n\r\n/**\r\n * output node.content, with check for $bind\r\n*/\r\nconst renderContent = (global: any, local: any, content: any) => {\r\n if (typeof content === \"object\") {\r\n return get(global, local, content.$bind);\r\n } else {\r\n return content;\r\n }\r\n}\r\n\r\nconst voidElements = new Set([\r\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\r\n 'link', 'meta', 'param', 'source', 'track', 'wbr'\r\n])\r\n\r\nexport interface RendererProps {\r\n id: string;\r\n componentMap: ComponentMap;\r\n childrenMap: ChildrenMap;\r\n allowedComponents: Record<string, ComponentType<any> | string>;\r\n global: any;\r\n local: any;\r\n}\r\n\r\n/**\r\n * Renders a UISchema recursively, in accordance to the spec.\r\n */\r\nexport function Renderer(props: RendererProps) {\r\n const {\r\n id, componentMap, childrenMap, global, local, allowedComponents\r\n } = props;\r\n const element = componentMap[id];\r\n\r\n if (element.type === \"TEXT\") return <>{renderContent(global, local, element.content)}</>\r\n\r\n const sourceArrPath = element.props?.source;\r\n if (element.type === '__ForEach__' && sourceArrPath) {\r\n const sourceArr = get(global, local, sourceArrPath)\r\n if (!Array.isArray(sourceArr)) return null;\r\n\r\n const childrenArr = childrenMap[element.id];\r\n return <>{childrenArr?.map((childId: string, index: number) => <Fragment key={index}>\r\n {sourceArr.map((item: any, index1: number) => <Renderer {...props} id={childId} local={item} key={index1} />)}\r\n </Fragment>)}</>\r\n }\r\n\r\n const Component = allowedComponents[element.type] || element.type;\r\n const componentProps = resolveProps(global, local, element.props);\r\n if(typeof Component === \"string\" && voidElements.has(Component)){\r\n return <Component {...componentProps} />\r\n }\r\n\r\n return <Component {...componentProps}>\r\n {renderContent(global, local, element.content)}\r\n {childrenMap[element.id] && childrenMap[element.id].map((childId: string, index: number) => {\r\n return <Renderer\r\n key={index}\r\n {...props}\r\n id={childId}\r\n />\r\n })}\r\n </Component>\r\n}\r\n","import { SchemaNode, UISchema } from \"./types\";\r\n\r\n/**\r\n * Utility class for parsing UISchema from stream.\r\n */\r\nexport class ResponseParser {\r\n buffer = \"\"; // unflushed existing deltas w/o newline\r\n \r\n // schema assembled thus far\r\n schema: UISchema = {\r\n childrenMap: {},\r\n componentMap: {},\r\n root: null\r\n }\r\n\r\n /**\r\n * Update schema with latest data chunk.\r\n * \r\n * Handles multiline input gracefully; can be used to load entire schemas from cache.\r\n * @param delta delta from stream.\r\n */\r\n addDelta(delta: string) {\r\n this.buffer += delta;\r\n const split = this.buffer.split(\"\\n\")\r\n if (split.length > 1) {\r\n split.slice(0, split.length - 1).forEach((line) => this.handleLine(line));\r\n this.buffer = split[split.length - 1];\r\n }\r\n }\r\n\r\n /**\r\n * Parses a single line (full JSON object) and updates schema.\r\n * Generally should not be used when streaming data.\r\n */\r\n handleLine(line: string) {\r\n try {\r\n const node: SchemaNode = JSON.parse(line);\r\n\r\n const { childrenMap, componentMap } = this.schema;\r\n\r\n componentMap[node.id] = node;\r\n if (node.parentId === null) {\r\n this.schema.root = node;\r\n } else {\r\n if (!childrenMap[node.parentId]) childrenMap[node.parentId] = []\r\n childrenMap[node.parentId].push(node.id)\r\n }\r\n } catch (err) { /* probably markdown or generation inconsistency */ }\r\n }\r\n\r\n /**\r\n * Clears the buffer and handles any remaining information within.\r\n */\r\n finish(){\r\n this.handleLine(this.buffer);\r\n this.buffer = \"\";\r\n }\r\n}"],"mappings":"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","names":["readStreamableValue","useEffect","useRef","useState","Fragment","jsx","jsxs","createElement","resolvePath","obj","path","acc","curr","get","global","local","resolveProps","props","key","val","renderContent","content","voidElements","Renderer","_a","id","componentMap","childrenMap","allowedComponents","element","sourceArrPath","sourceArr","childrenArr","childId","index","item","index1","Component","componentProps","ResponseParser","delta","split","line","node","childrenMap","componentMap","Fragment","jsx","GeneratedClient","value","allowedComponents","inputStream","placeholder","message","setMessage","useState","schema","setUISchema","parser","useRef","useEffect","ResponseParser","delta","readStreamableValue","prev","Renderer"]}
1
+ {"version":3,"sources":["../src/client/GeneratedClient.tsx","../src/client/Renderer.tsx","../src/ResponseParser.ts"],"sourcesContent":["\"use client\";\r\n\r\nimport { StreamableValue, readStreamableValue } from '@ai-sdk/rsc';\r\nimport React, { JSX, useEffect, useReducer, useRef, useState } from 'react';\r\nimport { Renderer } from './Renderer';\r\nimport { ResponseParser } from '../ResponseParser';\r\n\r\n/**\r\n * Client wrapper for Renderer that handles streaming and parsing with server.\r\n */\r\nexport function GeneratedClient({\r\n value,\r\n allowedComponents,\r\n inputStream,\r\n placeholder\r\n}: {\r\n value: any,\r\n allowedComponents: Record<string, React.ComponentType<any> | string>,\r\n inputStream: StreamableValue<string>,\r\n placeholder?: JSX.Element\r\n}) {\r\n const [, forceUpdate] = useReducer(x => x + 1, 0);\r\n const parser = useRef<ResponseParser | null>(null);\r\n\r\n useEffect(() => {\r\n if (!parser.current) {\r\n parser.current = new ResponseParser();\r\n }\r\n\r\n const parseStream = async () => {\r\n for await (const delta of readStreamableValue(inputStream)) {\r\n if (parser.current && delta !== undefined) {\r\n if(parser.current.addDelta(delta)){\r\n forceUpdate();\r\n }\r\n }\r\n }\r\n };\r\n\r\n parseStream().then(() => {\r\n parser.current.finish();\r\n forceUpdate();\r\n });\r\n }, [inputStream]);\r\n\r\n const schema = parser?.current?.schema;\r\n\r\n return (\r\n <>\r\n {schema?.root ?\r\n <Renderer id={schema.root.id} componentMap={schema.componentMap} childrenMap={schema.childrenMap} allowedComponents={allowedComponents} global={value} local={value} />\r\n : <>{placeholder}</>}\r\n </>\r\n )\r\n}\r\n","\"use client\";\r\n\r\nimport { ComponentType, Fragment } from 'react'\r\nimport { ChildrenMap, ComponentMap, SchemaNode } from '../types';\r\n\r\n/**\r\n * lightweight implementation of lodash.get\r\n */\r\nconst resolvePath = (obj: any, path: string) => {\r\n if (path === '$') return obj;\r\n return path.split('.').reduce((acc, curr) => acc?.[curr], obj)\r\n}\r\n\r\n/**\r\n * parses binding protocol and performs property lookup w/ scope resolution\r\n */\r\nconst get = (global: any, local: any, path: string) => {\r\n if (path.startsWith(\"$item.\")) {\r\n path = path.slice(6)\r\n return resolvePath(local, path);\r\n } else {\r\n if (path === \"$item\") return local;\r\n return resolvePath(global, path);\r\n }\r\n}\r\n\r\n/**\r\n * recursively parses props for bindings, replacing with true values\r\n */\r\nconst resolveProps = (global: any, local: any, props: any) => {\r\n if (!props) return props;\r\n if (\"$bind\" in props) return get(global, local, props.$bind); // $bind may be falsy value\r\n Object.keys(props).forEach((key) => {\r\n const val = props[key];\r\n if (typeof val === \"object\") {\r\n props[key] = resolveProps(global, local, val);\r\n }\r\n })\r\n return props;\r\n}\r\n\r\n/**\r\n * output node.content, with check for $bind\r\n*/\r\nconst renderContent = (global: any, local: any, content: any) => {\r\n if (typeof content === \"object\") {\r\n return get(global, local, content.$bind);\r\n } else {\r\n return content;\r\n }\r\n}\r\n\r\nconst voidElements = new Set([\r\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\r\n 'link', 'meta', 'param', 'source', 'track', 'wbr'\r\n])\r\n\r\nexport interface RendererProps {\r\n id: string;\r\n componentMap: ComponentMap;\r\n childrenMap: ChildrenMap;\r\n allowedComponents: Record<string, ComponentType<any> | string>;\r\n global: any;\r\n local: any;\r\n}\r\n\r\n/**\r\n * Renders a UISchema recursively, in accordance to the spec.\r\n */\r\nexport function Renderer(props: RendererProps) {\r\n const {\r\n id, componentMap, childrenMap, global, local, allowedComponents\r\n } = props;\r\n const element = componentMap[id];\r\n\r\n if (element.type === \"TEXT\") return <>{renderContent(global, local, element.content)}</>\r\n\r\n const sourceArrPath = element.props?.source;\r\n if (element.type === '__ForEach__' && sourceArrPath) {\r\n const sourceArr = get(global, local, sourceArrPath)\r\n if (!Array.isArray(sourceArr)) return null;\r\n\r\n const childrenArr = childrenMap[element.id];\r\n return <>{childrenArr?.map((childId: string, index: number) => <Fragment key={index}>\r\n {sourceArr.map((item: any, index1: number) => <Renderer {...props} id={childId} local={item} key={index1} />)}\r\n </Fragment>)}</>\r\n }\r\n\r\n const Component = allowedComponents[element.type] || element.type;\r\n const componentProps = resolveProps(global, local, element.props);\r\n if(typeof Component === \"string\" && voidElements.has(Component)){\r\n return <Component {...componentProps} />\r\n }\r\n\r\n return <Component {...componentProps}>\r\n {renderContent(global, local, element.content)}\r\n {childrenMap[element.id] && childrenMap[element.id].map((childId: string, index: number) => {\r\n return <Renderer\r\n key={index}\r\n {...props}\r\n id={childId}\r\n />\r\n })}\r\n </Component>\r\n}\r\n","import { SchemaNode, UISchema } from \"./types\";\r\n\r\n/**\r\n * Utility class for parsing UISchema from stream.\r\n */\r\nexport class ResponseParser {\r\n buffer = \"\"; // unflushed existing deltas w/o newline\r\n \r\n // schema assembled thus far\r\n schema: UISchema = {\r\n childrenMap: {},\r\n componentMap: {},\r\n root: null\r\n }\r\n\r\n /**\r\n * Update schema with latest data chunk.\r\n * \r\n * Handles multiline input gracefully; can be used to load entire schemas from cache.\r\n * \r\n * @param delta delta from stream.\r\n * @returns true if update is warranted, false otherwise.\r\n */\r\n addDelta(delta: string) {\r\n this.buffer += delta;\r\n const split = this.buffer.split(\"\\n\")\r\n if (split.length > 1) {\r\n split.slice(0, split.length - 1).forEach((line) => this.handleLine(line));\r\n this.buffer = split[split.length - 1];\r\n return true;\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Parses a single line (full JSON object) and updates schema.\r\n * Generally should not be used when streaming data.\r\n */\r\n handleLine(line: string) {\r\n try {\r\n const node: SchemaNode = JSON.parse(line);\r\n\r\n const { childrenMap, componentMap } = this.schema;\r\n\r\n componentMap[node.id] = node;\r\n if (node.parentId === null) {\r\n this.schema.root = node;\r\n } else {\r\n if (!childrenMap[node.parentId]) childrenMap[node.parentId] = []\r\n childrenMap[node.parentId].push(node.id)\r\n }\r\n } catch (err) { /* probably markdown or generation inconsistency */ }\r\n }\r\n\r\n /**\r\n * Clears the buffer and handles any remaining information within.\r\n */\r\n finish(){\r\n this.handleLine(this.buffer);\r\n this.buffer = \"\";\r\n }\r\n}"],"mappings":"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","names":["readStreamableValue","useEffect","useReducer","useRef","Fragment","jsx","jsxs","createElement","resolvePath","obj","path","acc","curr","get","global","local","resolveProps","props","key","val","renderContent","content","voidElements","Renderer","_a","id","componentMap","childrenMap","allowedComponents","element","sourceArrPath","sourceArr","childrenArr","childId","index","item","index1","Component","componentProps","ResponseParser","delta","split","line","node","childrenMap","componentMap","Fragment","jsx","GeneratedClient","value","allowedComponents","inputStream","placeholder","_a","forceUpdate","useReducer","x","parser","useRef","useEffect","ResponseParser","delta","readStreamableValue","schema","Renderer"]}
package/dist/index.d.mts CHANGED
@@ -13,6 +13,7 @@ interface GeneratedContentProps {
13
13
  placeholder?: JSX.Element;
14
14
  cached?: string;
15
15
  onGenerate?: (arg0: string) => void;
16
+ skeletonize?: boolean;
16
17
  }
17
18
 
18
19
  /**
@@ -23,7 +24,14 @@ declare function generateComponentMap(allowedComponents: (SyntuxComponent$1 | st
23
24
  /**
24
25
  * Creates LLM input in accordance to the spec
25
26
  */
26
- declare function constructInput({ value, components, hint }: GeneratedContentProps): string;
27
+ declare function constructInput({ value, skeletonize, components, hint }: GeneratedContentProps): string;
28
+ /**
29
+ * generates a skeleton of the input value, ideal for large arrays or untrusted input.
30
+ * see the FAQ for more information: https://github.com/puffinsoft/syntux/wiki/FAQ#handling-untrusted-input--large-arrays.
31
+ *
32
+ * *important*: assumes arrays are non-polymorphic
33
+ */
34
+ declare function create_skeleton(input: any): any;
27
35
 
28
36
  /**
29
37
  * Utility class for parsing UISchema from stream.
@@ -35,9 +43,11 @@ declare class ResponseParser {
35
43
  * Update schema with latest data chunk.
36
44
  *
37
45
  * Handles multiline input gracefully; can be used to load entire schemas from cache.
46
+ *
38
47
  * @param delta delta from stream.
48
+ * @returns true if update is warranted, false otherwise.
39
49
  */
40
- addDelta(delta: string): void;
50
+ addDelta(delta: string): boolean;
41
51
  /**
42
52
  * Parses a single line (full JSON object) and updates schema.
43
53
  * Generally should not be used when streaming data.
@@ -49,4 +59,4 @@ declare class ResponseParser {
49
59
  finish(): void;
50
60
  }
51
61
 
52
- export { ResponseParser, SyntuxComponent$1 as SyntuxComponent, UISchema, constructInput, generateComponentMap };
62
+ export { ResponseParser, SyntuxComponent$1 as SyntuxComponent, UISchema, constructInput, create_skeleton, generateComponentMap };
package/dist/index.mjs CHANGED
@@ -1,7 +1,9 @@
1
- function l(p){return p.reduce((n,t)=>typeof t=="string"?(n[t]=t,n):(n[t.name]=t.component,n),{})}function h({value:p,components:n,hint:t}){let o=(n==null?void 0:n.map(e=>typeof e=="string"?e:e.name).join(","))||"",r=n==null?void 0:n.filter(e=>typeof e!="string"),s=(r==null?void 0:r.map(e=>e.context?`${e.name} [props: ${e.props}, details: ${e.context}]`:`${e.name} [props: ${e.props}]`).join(","))||"",a=t,f=JSON.stringify(p);return`<AllowedComponents>${o}</AllowedComponents>
2
- <ComponentContext>${s}</ComponentContext>
3
- <UserContext>${a||""}</UserContext>
1
+ function h(n){return n.reduce((t,e)=>typeof e=="string"?(t[e]=e,t):(t[e.name]=e.component,t),{})}function d({value:n,skeletonize:t=!1,components:e,hint:o}){let s=(e==null?void 0:e.map(r=>typeof r=="string"?r:r.name).join(","))||"",i=e==null?void 0:e.filter(r=>typeof r!="string"),a=(i==null?void 0:i.map(r=>r.context?`${r.name} [props: ${r.props}, details: ${r.context}]`:`${r.name} [props: ${r.props}]`).join(","))||"",l=o,u=JSON.stringify(t?f(n):n);return`<AllowedComponents>${s}</AllowedComponents>
2
+ <ComponentContext>${a}</ComponentContext>
3
+ <UserContext>${l||""}</UserContext>
4
+ <IsSkeleton>${t.toString()}</IsSkeleton>
4
5
  <Value>
5
- ${f}</Value>`}var i=class{buffer="";schema={childrenMap:{},componentMap:{},root:null};addDelta(n){this.buffer+=n;let t=this.buffer.split(`
6
- `);t.length>1&&(t.slice(0,t.length-1).forEach(o=>this.handleLine(o)),this.buffer=t[t.length-1])}handleLine(n){try{let t=JSON.parse(n),{childrenMap:o,componentMap:r}=this.schema;r[t.id]=t,t.parentId===null?this.schema.root=t:(o[t.parentId]||(o[t.parentId]=[]),o[t.parentId].push(t.id))}catch{}}finish(){this.handleLine(this.buffer),this.buffer=""}};export{i as ResponseParser,h as constructInput,l as generateComponentMap};
6
+ ${u}
7
+ </Value>`}function f(n){return n===null?"null":typeof n!="object"?typeof n:Array.isArray(n)?n.length==0?"null":[f(n[0])]:Object.entries(n).reduce((t,[e,o])=>(t[e]=f(o),t),{})}var p=class{buffer="";schema={childrenMap:{},componentMap:{},root:null};addDelta(t){this.buffer+=t;let e=this.buffer.split(`
8
+ `);return e.length>1?(e.slice(0,e.length-1).forEach(o=>this.handleLine(o)),this.buffer=e[e.length-1],!0):!1}handleLine(t){try{let e=JSON.parse(t),{childrenMap:o,componentMap:s}=this.schema;s[e.id]=e,e.parentId===null?this.schema.root=e:(o[e.parentId]||(o[e.parentId]=[]),o[e.parentId].push(e.id))}catch{}}finish(){this.handleLine(this.buffer),this.buffer=""}};export{p as ResponseParser,d as constructInput,f as create_skeleton,h as generateComponentMap};
7
9
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/util.ts","../src/ResponseParser.ts"],"sourcesContent":["import { GeneratedContentProps } from \"./templates/GeneratedUI\";\r\nimport { SyntuxComponent } from \"./types\";\r\n\r\n/**\r\n * Converts a list of components into a dictionary for fast-retrieval\r\n * during rendering.\r\n */\r\nexport function generateComponentMap(allowedComponents: (SyntuxComponent | string)[]){\r\n return allowedComponents.reduce((acc: Record<string, React.ComponentType<any> | string>, curr: SyntuxComponent | string) => {\r\n if(typeof curr === \"string\"){\r\n acc[curr] = curr;\r\n return acc;\r\n }\r\n\r\n acc[curr.name] = curr.component;\r\n return acc;\r\n }, {})\r\n}\r\n\r\n/**\r\n * Creates LLM input in accordance to the spec\r\n */\r\nexport function constructInput({\r\n value, components, hint\r\n} : GeneratedContentProps){\r\n const allowedComponents = components?.map((item: SyntuxComponent | string) => {\r\n if(typeof item === \"string\") return item;\r\n return item.name;\r\n }).join(',') || \"\"\r\n\r\n const customComponents = components?.filter((item): item is SyntuxComponent => typeof item !== \"string\");\r\n const componentContext = customComponents?.map((item) => {\r\n if(!item.context){\r\n return `${item.name} [props: ${item.props}]`\r\n } else {\r\n return `${item.name} [props: ${item.props}, details: ${item.context}]`\r\n }\r\n }).join(',') || \"\"\r\n\r\n const userContext = hint;\r\n const inputValue = JSON.stringify(value)\r\n\r\n return `<AllowedComponents>${allowedComponents}</AllowedComponents>\\n<ComponentContext>${componentContext}</ComponentContext>\\n<UserContext>${userContext || \"\"}</UserContext>\\n<Value>\\n${inputValue}</Value>`\r\n}","import { SchemaNode, UISchema } from \"./types\";\r\n\r\n/**\r\n * Utility class for parsing UISchema from stream.\r\n */\r\nexport class ResponseParser {\r\n buffer = \"\"; // unflushed existing deltas w/o newline\r\n \r\n // schema assembled thus far\r\n schema: UISchema = {\r\n childrenMap: {},\r\n componentMap: {},\r\n root: null\r\n }\r\n\r\n /**\r\n * Update schema with latest data chunk.\r\n * \r\n * Handles multiline input gracefully; can be used to load entire schemas from cache.\r\n * @param delta delta from stream.\r\n */\r\n addDelta(delta: string) {\r\n this.buffer += delta;\r\n const split = this.buffer.split(\"\\n\")\r\n if (split.length > 1) {\r\n split.slice(0, split.length - 1).forEach((line) => this.handleLine(line));\r\n this.buffer = split[split.length - 1];\r\n }\r\n }\r\n\r\n /**\r\n * Parses a single line (full JSON object) and updates schema.\r\n * Generally should not be used when streaming data.\r\n */\r\n handleLine(line: string) {\r\n try {\r\n const node: SchemaNode = JSON.parse(line);\r\n\r\n const { childrenMap, componentMap } = this.schema;\r\n\r\n componentMap[node.id] = node;\r\n if (node.parentId === null) {\r\n this.schema.root = node;\r\n } else {\r\n if (!childrenMap[node.parentId]) childrenMap[node.parentId] = []\r\n childrenMap[node.parentId].push(node.id)\r\n }\r\n } catch (err) { /* probably markdown or generation inconsistency */ }\r\n }\r\n\r\n /**\r\n * Clears the buffer and handles any remaining information within.\r\n */\r\n finish(){\r\n this.handleLine(this.buffer);\r\n this.buffer = \"\";\r\n }\r\n}"],"mappings":"AAOO,SAASA,EAAqBC,EAAgD,CACjF,OAAOA,EAAkB,OAAO,CAACC,EAAwDC,IAClF,OAAOA,GAAS,UACfD,EAAIC,CAAI,EAAIA,EACLD,IAGXA,EAAIC,EAAK,IAAI,EAAIA,EAAK,UACfD,GACR,CAAC,CAAC,CACT,CAKO,SAASE,EAAe,CAC3B,MAAAC,EAAO,WAAAC,EAAY,KAAAC,CACvB,EAA0B,CACtB,IAAMN,GAAoBK,GAAA,YAAAA,EAAY,IAAKE,GACpC,OAAOA,GAAS,SAAiBA,EAC7BA,EAAK,MACb,KAAK,OAAQ,GAEVC,EAAmBH,GAAA,YAAAA,EAAY,OAAQE,GAAkC,OAAOA,GAAS,UACzFE,GAAmBD,GAAA,YAAAA,EAAkB,IAAKD,GACxCA,EAAK,QAGG,GAAGA,EAAK,IAAI,YAAYA,EAAK,KAAK,cAAcA,EAAK,OAAO,IAF5D,GAAGA,EAAK,IAAI,YAAYA,EAAK,KAAK,KAI/C,KAAK,OAAQ,GAEVG,EAAcJ,EACdK,EAAa,KAAK,UAAUP,CAAK,EAEvC,MAAO,sBAAsBJ,CAAiB;AAAA,oBAA2CS,CAAgB;AAAA,eAAqCC,GAAe,EAAE;AAAA;AAAA,EAA4BC,CAAU,UACzM,CCtCO,IAAMC,EAAN,KAAqB,CACxB,OAAS,GAGT,OAAmB,CACf,YAAa,CAAC,EACd,aAAc,CAAC,EACf,KAAM,IACV,EAQA,SAASC,EAAe,CACpB,KAAK,QAAUA,EACf,IAAMC,EAAQ,KAAK,OAAO,MAAM;AAAA,CAAI,EAChCA,EAAM,OAAS,IACfA,EAAM,MAAM,EAAGA,EAAM,OAAS,CAAC,EAAE,QAASC,GAAS,KAAK,WAAWA,CAAI,CAAC,EACxE,KAAK,OAASD,EAAMA,EAAM,OAAS,CAAC,EAE5C,CAMA,WAAWC,EAAc,CACrB,GAAI,CACA,IAAMC,EAAmB,KAAK,MAAMD,CAAI,EAElC,CAAE,YAAAE,EAAa,aAAAC,CAAa,EAAI,KAAK,OAE3CA,EAAaF,EAAK,EAAE,EAAIA,EACpBA,EAAK,WAAa,KAClB,KAAK,OAAO,KAAOA,GAEdC,EAAYD,EAAK,QAAQ,IAAGC,EAAYD,EAAK,QAAQ,EAAI,CAAC,GAC/DC,EAAYD,EAAK,QAAQ,EAAE,KAAKA,EAAK,EAAE,EAE/C,MAAc,CAAsD,CACxE,CAKA,QAAQ,CACJ,KAAK,WAAW,KAAK,MAAM,EAC3B,KAAK,OAAS,EAClB,CACJ","names":["generateComponentMap","allowedComponents","acc","curr","constructInput","value","components","hint","item","customComponents","componentContext","userContext","inputValue","ResponseParser","delta","split","line","node","childrenMap","componentMap"]}
1
+ {"version":3,"sources":["../src/util.ts","../src/ResponseParser.ts"],"sourcesContent":["import { GeneratedContentProps } from \"./templates/GeneratedUI\";\r\nimport { SyntuxComponent } from \"./types\";\r\n\r\n/**\r\n * Converts a list of components into a dictionary for fast-retrieval\r\n * during rendering.\r\n */\r\nexport function generateComponentMap(allowedComponents: (SyntuxComponent | string)[]) {\r\n return allowedComponents.reduce((acc: Record<string, React.ComponentType<any> | string>, curr: SyntuxComponent | string) => {\r\n if (typeof curr === \"string\") {\r\n acc[curr] = curr;\r\n return acc;\r\n }\r\n\r\n acc[curr.name] = curr.component;\r\n return acc;\r\n }, {})\r\n}\r\n\r\n/**\r\n * Creates LLM input in accordance to the spec\r\n */\r\nexport function constructInput({\r\n value, skeletonize = false, components, hint\r\n}: GeneratedContentProps) {\r\n const allowedComponents = components?.map((item: SyntuxComponent | string) => {\r\n if (typeof item === \"string\") return item;\r\n return item.name;\r\n }).join(',') || \"\"\r\n\r\n const customComponents = components?.filter((item): item is SyntuxComponent => typeof item !== \"string\");\r\n const componentContext = customComponents?.map((item) => {\r\n if (!item.context) {\r\n return `${item.name} [props: ${item.props}]`\r\n } else {\r\n return `${item.name} [props: ${item.props}, details: ${item.context}]`\r\n }\r\n }).join(',') || \"\"\r\n\r\n const userContext = hint;\r\n const inputValue = JSON.stringify(skeletonize ? create_skeleton(value) : value)\r\n\r\n return `<AllowedComponents>${allowedComponents}</AllowedComponents>\\n<ComponentContext>${componentContext}</ComponentContext>\\n<UserContext>${userContext || \"\"}</UserContext>\\n<IsSkeleton>${skeletonize.toString()}</IsSkeleton>\\n<Value>\\n${inputValue}\\n</Value>`\r\n}\r\n\r\n/**\r\n * generates a skeleton of the input value, ideal for large arrays or untrusted input.\r\n * see the FAQ for more information: https://github.com/puffinsoft/syntux/wiki/FAQ#handling-untrusted-input--large-arrays.\r\n * \r\n * *important*: assumes arrays are non-polymorphic\r\n */\r\nexport function create_skeleton(input: any) {\r\n if (input === null) return \"null\";\r\n\r\n if (typeof input !== \"object\") return typeof input;\r\n\r\n if (Array.isArray(input)) {\r\n if (input.length == 0) {\r\n return \"null\"; // ignore this field completely\r\n } else {\r\n return [create_skeleton(input[0])]\r\n }\r\n }\r\n return Object.entries(input).reduce((acc, [key, value]) => {\r\n acc[key] = create_skeleton(value);\r\n return acc;\r\n }, {})\r\n}","import { SchemaNode, UISchema } from \"./types\";\r\n\r\n/**\r\n * Utility class for parsing UISchema from stream.\r\n */\r\nexport class ResponseParser {\r\n buffer = \"\"; // unflushed existing deltas w/o newline\r\n \r\n // schema assembled thus far\r\n schema: UISchema = {\r\n childrenMap: {},\r\n componentMap: {},\r\n root: null\r\n }\r\n\r\n /**\r\n * Update schema with latest data chunk.\r\n * \r\n * Handles multiline input gracefully; can be used to load entire schemas from cache.\r\n * \r\n * @param delta delta from stream.\r\n * @returns true if update is warranted, false otherwise.\r\n */\r\n addDelta(delta: string) {\r\n this.buffer += delta;\r\n const split = this.buffer.split(\"\\n\")\r\n if (split.length > 1) {\r\n split.slice(0, split.length - 1).forEach((line) => this.handleLine(line));\r\n this.buffer = split[split.length - 1];\r\n return true;\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Parses a single line (full JSON object) and updates schema.\r\n * Generally should not be used when streaming data.\r\n */\r\n handleLine(line: string) {\r\n try {\r\n const node: SchemaNode = JSON.parse(line);\r\n\r\n const { childrenMap, componentMap } = this.schema;\r\n\r\n componentMap[node.id] = node;\r\n if (node.parentId === null) {\r\n this.schema.root = node;\r\n } else {\r\n if (!childrenMap[node.parentId]) childrenMap[node.parentId] = []\r\n childrenMap[node.parentId].push(node.id)\r\n }\r\n } catch (err) { /* probably markdown or generation inconsistency */ }\r\n }\r\n\r\n /**\r\n * Clears the buffer and handles any remaining information within.\r\n */\r\n finish(){\r\n this.handleLine(this.buffer);\r\n this.buffer = \"\";\r\n }\r\n}"],"mappings":"AAOO,SAASA,EAAqBC,EAAiD,CAClF,OAAOA,EAAkB,OAAO,CAACC,EAAwDC,IACjF,OAAOA,GAAS,UAChBD,EAAIC,CAAI,EAAIA,EACLD,IAGXA,EAAIC,EAAK,IAAI,EAAIA,EAAK,UACfD,GACR,CAAC,CAAC,CACT,CAKO,SAASE,EAAe,CAC3B,MAAAC,EAAO,YAAAC,EAAc,GAAO,WAAAC,EAAY,KAAAC,CAC5C,EAA0B,CACtB,IAAMP,GAAoBM,GAAA,YAAAA,EAAY,IAAKE,GACnC,OAAOA,GAAS,SAAiBA,EAC9BA,EAAK,MACb,KAAK,OAAQ,GAEVC,EAAmBH,GAAA,YAAAA,EAAY,OAAQE,GAAkC,OAAOA,GAAS,UACzFE,GAAmBD,GAAA,YAAAA,EAAkB,IAAKD,GACvCA,EAAK,QAGC,GAAGA,EAAK,IAAI,YAAYA,EAAK,KAAK,cAAcA,EAAK,OAAO,IAF5D,GAAGA,EAAK,IAAI,YAAYA,EAAK,KAAK,KAI9C,KAAK,OAAQ,GAEVG,EAAcJ,EACdK,EAAa,KAAK,UAAUP,EAAcQ,EAAgBT,CAAK,EAAIA,CAAK,EAE9E,MAAO,sBAAsBJ,CAAiB;AAAA,oBAA2CU,CAAgB;AAAA,eAAqCC,GAAe,EAAE;AAAA,cAA+BN,EAAY,SAAS,CAAC;AAAA;AAAA,EAA2BO,CAAU;AAAA,SAC7P,CAQO,SAASC,EAAgBC,EAAY,CACxC,OAAIA,IAAU,KAAa,OAEvB,OAAOA,GAAU,SAAiB,OAAOA,EAEzC,MAAM,QAAQA,CAAK,EACfA,EAAM,QAAU,EACT,OAEA,CAACD,EAAgBC,EAAM,CAAC,CAAC,CAAC,EAGlC,OAAO,QAAQA,CAAK,EAAE,OAAO,CAACb,EAAK,CAACc,EAAKX,CAAK,KACjDH,EAAIc,CAAG,EAAIF,EAAgBT,CAAK,EACzBH,GACR,CAAC,CAAC,CACT,CC9DO,IAAMe,EAAN,KAAqB,CACxB,OAAS,GAGT,OAAmB,CACf,YAAa,CAAC,EACd,aAAc,CAAC,EACf,KAAM,IACV,EAUA,SAASC,EAAe,CACpB,KAAK,QAAUA,EACf,IAAMC,EAAQ,KAAK,OAAO,MAAM;AAAA,CAAI,EACpC,OAAIA,EAAM,OAAS,GACfA,EAAM,MAAM,EAAGA,EAAM,OAAS,CAAC,EAAE,QAASC,GAAS,KAAK,WAAWA,CAAI,CAAC,EACxE,KAAK,OAASD,EAAMA,EAAM,OAAS,CAAC,EAC7B,IAEJ,EACX,CAMA,WAAWC,EAAc,CACrB,GAAI,CACA,IAAMC,EAAmB,KAAK,MAAMD,CAAI,EAElC,CAAE,YAAAE,EAAa,aAAAC,CAAa,EAAI,KAAK,OAE3CA,EAAaF,EAAK,EAAE,EAAIA,EACpBA,EAAK,WAAa,KAClB,KAAK,OAAO,KAAOA,GAEdC,EAAYD,EAAK,QAAQ,IAAGC,EAAYD,EAAK,QAAQ,EAAI,CAAC,GAC/DC,EAAYD,EAAK,QAAQ,EAAE,KAAKA,EAAK,EAAE,EAE/C,MAAc,CAAsD,CACxE,CAKA,QAAQ,CACJ,KAAK,WAAW,KAAK,MAAM,EAC3B,KAAK,OAAS,EAClB,CACJ","names":["generateComponentMap","allowedComponents","acc","curr","constructInput","value","skeletonize","components","hint","item","customComponents","componentContext","userContext","inputValue","create_skeleton","input","key","ResponseParser","delta","split","line","node","childrenMap","componentMap"]}
@@ -1 +1 @@
1
- {"inputs":{"src/types.ts":{"bytes":583,"imports":[],"format":"esm"},"src/util.ts":{"bytes":1656,"imports":[{"path":"./templates/GeneratedUI","kind":"import-statement","external":true},{"path":"./types","kind":"import-statement","external":true}],"format":"esm"},"src/ResponseParser.ts":{"bytes":1744,"imports":[{"path":"./types","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":84,"imports":[{"path":"src/types.ts","kind":"import-statement","original":"./types"},{"path":"src/util.ts","kind":"import-statement","original":"./util"},{"path":"src/ResponseParser.ts","kind":"import-statement","original":"./ResponseParser"}],"format":"esm"},"src/client/Renderer.tsx":{"bytes":3397,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"../types","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true}],"format":"esm"},"src/client/GeneratedClient.tsx":{"bytes":1552,"imports":[{"path":"@ai-sdk/rsc","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"src/client/Renderer.tsx","kind":"import-statement","original":"./Renderer"},{"path":"src/ResponseParser.ts","kind":"import-statement","original":"../ResponseParser"},{"path":"../types","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/client.ts":{"bytes":92,"imports":[{"path":"src/client/GeneratedClient.tsx","kind":"import-statement","original":"./client/GeneratedClient"},{"path":"src/client/Renderer.tsx","kind":"import-statement","original":"./client/Renderer"}],"format":"esm"},"src/bin/cli_util.mjs":{"bytes":115,"imports":[{"path":"chalk","kind":"import-statement","external":true}],"format":"esm"},"src/bin/commands/init.mjs":{"bytes":3700,"imports":[{"path":"commander","kind":"import-statement","external":true},{"path":"fs-extra","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"child_process","kind":"import-statement","external":true},{"path":"prompts","kind":"import-statement","external":true},{"path":"chalk","kind":"import-statement","external":true},{"path":"src/bin/cli_util.mjs","kind":"import-statement","original":"../cli_util.mjs"},{"path":"url","kind":"import-statement","external":true}],"format":"esm"},"src/bin/commands/generate.mjs":{"bytes":2933,"imports":[{"path":"commander","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"react-docgen-typescript","kind":"import-statement","external":true},{"path":"prompts","kind":"import-statement","external":true},{"path":"chalk","kind":"import-statement","external":true},{"path":"fs-extra","kind":"import-statement","external":true},{"path":"src/bin/cli_util.mjs","kind":"import-statement","original":"../cli_util.mjs"}],"format":"esm"},"src/bin/cli.mjs":{"bytes":482,"imports":[{"path":"commander","kind":"import-statement","external":true},{"path":"src/bin/commands/init.mjs","kind":"import-statement","original":"./commands/init.mjs"},{"path":"src/bin/commands/generate.mjs","kind":"import-statement","original":"./commands/generate.mjs"}],"format":"esm"}},"outputs":{"dist/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":5351},"dist/index.mjs":{"imports":[],"exports":["ResponseParser","constructInput","generateComponentMap"],"entryPoint":"src/index.ts","inputs":{"src/index.ts":{"bytesInOutput":0},"src/util.ts":{"bytesInOutput":578},"src/ResponseParser.ts":{"bytesInOutput":473}},"bytes":1126},"dist/client.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":11337},"dist/client.mjs":{"imports":[{"path":"@ai-sdk/rsc","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"exports":["GeneratedClient","Renderer"],"entryPoint":"src/client.ts","inputs":{"src/client/GeneratedClient.tsx":{"bytesInOutput":595},"src/client/Renderer.tsx":{"bytesInOutput":1153},"src/ResponseParser.ts":{"bytesInOutput":473},"src/client.ts":{"bytesInOutput":0}},"bytes":2278},"dist/bin/cli.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":12417},"dist/bin/cli.mjs":{"imports":[{"path":"commander","kind":"import-statement","external":true},{"path":"commander","kind":"import-statement","external":true},{"path":"fs-extra","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"child_process","kind":"import-statement","external":true},{"path":"prompts","kind":"import-statement","external":true},{"path":"chalk","kind":"import-statement","external":true},{"path":"chalk","kind":"import-statement","external":true},{"path":"url","kind":"import-statement","external":true},{"path":"commander","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"react-docgen-typescript","kind":"import-statement","external":true},{"path":"prompts","kind":"import-statement","external":true},{"path":"chalk","kind":"import-statement","external":true},{"path":"fs-extra","kind":"import-statement","external":true}],"exports":[],"entryPoint":"src/bin/cli.mjs","inputs":{"src/bin/cli.mjs":{"bytesInOutput":250},"src/bin/commands/init.mjs":{"bytesInOutput":1851},"src/bin/cli_util.mjs":{"bytesInOutput":78},"src/bin/commands/generate.mjs":{"bytesInOutput":1443}},"bytes":3643}}}
1
+ {"inputs":{"src/types.ts":{"bytes":583,"imports":[],"format":"esm"},"src/util.ts":{"bytes":2560,"imports":[{"path":"./templates/GeneratedUI","kind":"import-statement","external":true},{"path":"./types","kind":"import-statement","external":true}],"format":"esm"},"src/ResponseParser.ts":{"bytes":1865,"imports":[{"path":"./types","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":84,"imports":[{"path":"src/types.ts","kind":"import-statement","original":"./types"},{"path":"src/util.ts","kind":"import-statement","original":"./util"},{"path":"src/ResponseParser.ts","kind":"import-statement","original":"./ResponseParser"}],"format":"esm"},"src/client/Renderer.tsx":{"bytes":3397,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"../types","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true}],"format":"esm"},"src/client/GeneratedClient.tsx":{"bytes":1544,"imports":[{"path":"@ai-sdk/rsc","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"src/client/Renderer.tsx","kind":"import-statement","original":"./Renderer"},{"path":"src/ResponseParser.ts","kind":"import-statement","original":"../ResponseParser"},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/client.ts":{"bytes":92,"imports":[{"path":"src/client/GeneratedClient.tsx","kind":"import-statement","original":"./client/GeneratedClient"},{"path":"src/client/Renderer.tsx","kind":"import-statement","original":"./client/Renderer"}],"format":"esm"},"src/bin/cli_util.mjs":{"bytes":115,"imports":[{"path":"chalk","kind":"import-statement","external":true}],"format":"esm"},"src/bin/commands/init.mjs":{"bytes":3700,"imports":[{"path":"commander","kind":"import-statement","external":true},{"path":"fs-extra","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"child_process","kind":"import-statement","external":true},{"path":"prompts","kind":"import-statement","external":true},{"path":"chalk","kind":"import-statement","external":true},{"path":"src/bin/cli_util.mjs","kind":"import-statement","original":"../cli_util.mjs"},{"path":"url","kind":"import-statement","external":true}],"format":"esm"},"src/bin/commands/generate.mjs":{"bytes":2933,"imports":[{"path":"commander","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"react-docgen-typescript","kind":"import-statement","external":true},{"path":"prompts","kind":"import-statement","external":true},{"path":"chalk","kind":"import-statement","external":true},{"path":"fs-extra","kind":"import-statement","external":true},{"path":"src/bin/cli_util.mjs","kind":"import-statement","original":"../cli_util.mjs"}],"format":"esm"},"src/bin/cli.mjs":{"bytes":482,"imports":[{"path":"commander","kind":"import-statement","external":true},{"path":"src/bin/commands/init.mjs","kind":"import-statement","original":"./commands/init.mjs"},{"path":"src/bin/commands/generate.mjs","kind":"import-statement","original":"./commands/generate.mjs"}],"format":"esm"}},"outputs":{"dist/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":6856},"dist/index.mjs":{"imports":[],"exports":["ResponseParser","constructInput","create_skeleton","generateComponentMap"],"entryPoint":"src/index.ts","inputs":{"src/index.ts":{"bytesInOutput":0},"src/util.ts":{"bytesInOutput":809},"src/ResponseParser.ts":{"bytesInOutput":485}},"bytes":1390},"dist/client.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":11528},"dist/client.mjs":{"imports":[{"path":"@ai-sdk/rsc","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"exports":["GeneratedClient","Renderer"],"entryPoint":"src/client.ts","inputs":{"src/client/GeneratedClient.tsx":{"bytesInOutput":663},"src/client/Renderer.tsx":{"bytesInOutput":1153},"src/ResponseParser.ts":{"bytesInOutput":485},"src/client.ts":{"bytesInOutput":0}},"bytes":2358},"dist/bin/cli.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":12417},"dist/bin/cli.mjs":{"imports":[{"path":"commander","kind":"import-statement","external":true},{"path":"commander","kind":"import-statement","external":true},{"path":"fs-extra","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"child_process","kind":"import-statement","external":true},{"path":"prompts","kind":"import-statement","external":true},{"path":"chalk","kind":"import-statement","external":true},{"path":"chalk","kind":"import-statement","external":true},{"path":"url","kind":"import-statement","external":true},{"path":"commander","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"react-docgen-typescript","kind":"import-statement","external":true},{"path":"prompts","kind":"import-statement","external":true},{"path":"chalk","kind":"import-statement","external":true},{"path":"fs-extra","kind":"import-statement","external":true}],"exports":[],"entryPoint":"src/bin/cli.mjs","inputs":{"src/bin/cli.mjs":{"bytesInOutput":250},"src/bin/commands/init.mjs":{"bytesInOutput":1851},"src/bin/cli_util.mjs":{"bytesInOutput":78},"src/bin/commands/generate.mjs":{"bytesInOutput":1443}},"bytes":3643}}}
@@ -15,7 +15,13 @@ export interface GeneratedContentProps {
15
15
  hint?: string;
16
16
  placeholder?: JSX.Element;
17
17
  cached?: string;
18
- onGenerate?: (arg0: string) => void
18
+ /*
19
+ * ^ this is a string for two reasons:
20
+ * - it is easier to store
21
+ * - it is parsed then mutated at runtime. This avoids unintended side effects.
22
+ */
23
+ onGenerate?: (arg0: string) => void;
24
+ skeletonize?: boolean;
19
25
  }
20
26
 
21
27
  /**
@@ -27,6 +33,7 @@ export interface GeneratedContentProps {
27
33
  * @param placeholder A placeholder to show while awaiting streaming (NOT during streaming)
28
34
  * @param cached Schema returned from onGenerate, used for caching UI
29
35
  * @param onGenerate Callback which accepts a string, to be passed to `cached` to reuse same UI
36
+ * @param skeletonize Compresses value for large inputs (arrays) or untrusted input
30
37
  */
31
38
  export async function GeneratedUI(props: GeneratedContentProps) {
32
39
  const input = constructInput(props);
@@ -51,6 +51,7 @@ In the example above, card_1 is the template that repeats for every author. It s
51
51
  * `ComponentContext` defines the Typescript interface for custom components. Components are separated by a comma, in the format `ComponentName [props: { ... }, details: "..."]`. The `props` indicate what `props` it must accept, in Typescript format. The `details` is an optional field, and describes what the component does. DO NOT hallucinate props. Use the details to better your understanding of how to generate the UI.
52
52
  2. Parse Context: Read `UserContext` for specific design requests.
53
53
  3. Parse Data: Analyze `Value` to determine structure.
54
+ 4. Check Skeleton: Read `IsSkeleton`. If it is `true`, that means all the property values have been replaced by the *type* of the value. If it is `false`, then you are seeing the raw values of each property.
54
55
  </input_processing_rules>
55
56
 
56
57
  <output_formatting>
@@ -60,6 +61,7 @@ Input:
60
61
  <AllowedComponents>...</AllowedComponents>
61
62
  <ComponentContext>...</ComponentContext>
62
63
  <UserContext>...</UserContext>
64
+ <IsSkeleton>...</IsSkeleton>
63
65
  <Value>...</Value>
64
66
 
65
67
  Output:
@@ -57,6 +57,7 @@ In the example above, card_1 is the template that repeats for every author. It s
57
57
  * \`ComponentContext\` defines the Typescript interface for custom components. Components are separated by a comma, in the format \`ComponentName [props: { ... }, details: "..."]\`. The \`props\` indicate what \`props\` it must accept, in Typescript format. The \`details\` is an optional field, and describes what the component does. DO NOT hallucinate props. Use the details to better your understanding of how to generate the UI.
58
58
  2. Parse Context: Read \`UserContext\` for specific design requests.
59
59
  3. Parse Data: Analyze \`Value\` to determine structure.
60
+ 4. Check Skeleton: Read \`IsSkeleton\`. If it is \`true\`, that means all the property values have been replaced by the *type* of the value. If it is \`false\`, then you are seeing the raw values of each property. Use this information to bind properly.
60
61
  </input_processing_rules>
61
62
 
62
63
  <output_formatting>
@@ -66,6 +67,7 @@ Input:
66
67
  <AllowedComponents>...</AllowedComponents>
67
68
  <ComponentContext>...</ComponentContext>
68
69
  <UserContext>...</UserContext>
70
+ <IsSkeleton>...</IsSkeleton>
69
71
  <Value>...</Value>
70
72
 
71
73
  Output:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "getsyntux",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "The declarative generative-UI library.",
5
5
  "exports": {
6
6
  ".": {
@@ -31,14 +31,12 @@
31
31
  "@ai-sdk/rsc": "^2.0.3",
32
32
  "@types/node": "^25.0.2",
33
33
  "@types/react": "^19.2.7",
34
- "ai": "^6.0.3",
35
- "chalk": "^5.6.2",
36
34
  "commander": "^14.0.2",
37
35
  "fs-extra": "^11.3.3",
38
36
  "prompts": "^2.4.2",
37
+ "chalk": "^5.6.2",
39
38
  "react-docgen-typescript": "^2.4.0",
40
- "tsup": "^8.5.1",
41
- "typescript": "^5.9.3"
39
+ "ai": "^6.0.3"
42
40
  },
43
41
  "scripts": {
44
42
  "build": "tsup",
@@ -51,6 +49,8 @@
51
49
  "devDependencies": {
52
50
  "@ai-sdk/anthropic": "^3.0.1",
53
51
  "esbuild-plugin-preserve-directives": "^0.0.7",
54
- "tsx": "^4.21.0"
52
+ "tsup": "^8.5.1",
53
+ "tsx": "^4.21.0",
54
+ "typescript": "^5.9.3"
55
55
  }
56
56
  }