getsyntux 0.7.2 → 0.8.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 +111 -20
- package/dist/client.d.mts +4 -3
- package/dist/client.mjs +2 -2
- package/dist/client.mjs.map +1 -1
- package/dist/index.d.mts +7 -2
- package/dist/index.mjs +5 -5
- package/dist/index.mjs.map +1 -1
- package/dist/metafile-esm.json +1 -1
- package/dist/templates/GeneratedUI.tsx +19 -30
- package/dist/templates/RerenderHandler.tsx +49 -3
- package/dist/templates/spec.md +1 -0
- package/dist/templates/spec.ts +12 -0
- package/dist/types-0XgxLwqz.d.mts +45 -0
- package/package.json +1 -1
- package/dist/types-C9N85B_g.d.mts +0 -28
package/README.md
CHANGED
|
@@ -21,6 +21,7 @@ For instance, if you provide an array `value` with 10,000 items, it will cost yo
|
|
|
21
21
|
- ⚡ **Streamable** - display UI as you generate.
|
|
22
22
|
- 🎨 **Custom Components** - use your own React components.
|
|
23
23
|
- 💾 **Cacheable** - reuse generated UIs with new values.
|
|
24
|
+
- 🔄 **Reactive** - update the UI programmatically.
|
|
24
25
|
|
|
25
26
|
**How does it work?** *syntux* generates a JSON-DSL to represent the UI, known as the React Interface Schema. The specifics are in the FAQ [below](#faq).
|
|
26
27
|
|
|
@@ -96,13 +97,23 @@ export default function Home(){
|
|
|
96
97
|
Cache generated UI based on a user ID:
|
|
97
98
|
|
|
98
99
|
```jsx
|
|
99
|
-
const cache: Map<number, string> = new Map();
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
100
|
+
const cache: Map<number, string> = new Map(); // user id → UI schema
|
|
101
|
+
|
|
102
|
+
export default function Home() {
|
|
103
|
+
const userID = 10;
|
|
104
|
+
const valueToDisplay = { ... };
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<GeneratedUI
|
|
108
|
+
cached={cache.get(userID)}
|
|
109
|
+
onGenerate={(result) => {
|
|
110
|
+
cache.set(userID, result);
|
|
111
|
+
}}
|
|
112
|
+
|
|
113
|
+
model={anthropic("claude-sonnet-4-5")}
|
|
114
|
+
value={valueToDisplay}
|
|
115
|
+
/>
|
|
116
|
+
);
|
|
106
117
|
}
|
|
107
118
|
```
|
|
108
119
|
|
|
@@ -111,14 +122,28 @@ export default function Home(){
|
|
|
111
122
|
Use your own components, or someone else's (a library):
|
|
112
123
|
|
|
113
124
|
```jsx
|
|
114
|
-
import { CustomOne, CustomTwo } from
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
125
|
+
import { CustomOne, CustomTwo } from "@/my_components";
|
|
126
|
+
|
|
127
|
+
export default function Home() {
|
|
128
|
+
const valueToDisplay = { ... };
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<GeneratedUI
|
|
132
|
+
components={[{
|
|
133
|
+
name: "Button",
|
|
134
|
+
props: "{ color: string, text: string }",
|
|
135
|
+
component: CustomOne,
|
|
136
|
+
}, {
|
|
137
|
+
name: "Input",
|
|
138
|
+
props: "{ initial: string, disabled: boolean }",
|
|
139
|
+
component: CustomTwo,
|
|
140
|
+
context: "Creates an input field with an (initial) value. Can be disabled.",
|
|
141
|
+
}]}
|
|
142
|
+
|
|
143
|
+
model={anthropic("claude-sonnet-4-5")}
|
|
144
|
+
value={valueToDisplay}
|
|
145
|
+
/>
|
|
146
|
+
);
|
|
122
147
|
}
|
|
123
148
|
```
|
|
124
149
|
|
|
@@ -130,24 +155,90 @@ export default function Home(){
|
|
|
130
155
|
|
|
131
156
|
Make sure components are marked with `"use client"`.
|
|
132
157
|
|
|
133
|
-
#### Update value (
|
|
158
|
+
#### Update value (static)
|
|
134
159
|
|
|
135
160
|
Use the `useSyntux` hook to retrieve and update the `value` inside a custom component:
|
|
136
161
|
|
|
137
162
|
```jsx
|
|
138
163
|
"use client";
|
|
139
164
|
|
|
140
|
-
export default function CustomComponent(){
|
|
141
|
-
|
|
165
|
+
export default function CustomComponent() {
|
|
166
|
+
const { value, setValue } = useSyntux();
|
|
142
167
|
|
|
143
|
-
|
|
168
|
+
return (
|
|
169
|
+
<button
|
|
170
|
+
onClick={() => {
|
|
144
171
|
const newArr = [...value];
|
|
145
172
|
newArr.push({ ... });
|
|
146
173
|
setValue(newArr);
|
|
147
|
-
|
|
174
|
+
}}
|
|
175
|
+
>
|
|
176
|
+
Add value
|
|
177
|
+
</button>
|
|
178
|
+
);
|
|
148
179
|
}
|
|
149
180
|
```
|
|
150
181
|
|
|
182
|
+
#### Regenerate UI (dynamic)
|
|
183
|
+
|
|
184
|
+
Provide the `rerender` prop a server action.
|
|
185
|
+
|
|
186
|
+
**The server action is already provided.** However, you will need to configure it (i.e., add an API key etc,.)
|
|
187
|
+
|
|
188
|
+
```jsx
|
|
189
|
+
import { rerenderAction } from "@/lib/getsyntux/RerenderHandler"; // preinstalled
|
|
190
|
+
|
|
191
|
+
<GeneratedUI
|
|
192
|
+
model={anthropic("claude-sonnet-4-5")}
|
|
193
|
+
value={valueToDisplay}
|
|
194
|
+
rerender={rerenderAction}
|
|
195
|
+
/>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Inside a custom component, use the `useSyntux` hook and provide a hint to regenerate the UI:
|
|
199
|
+
|
|
200
|
+
```jsx
|
|
201
|
+
"use client";
|
|
202
|
+
|
|
203
|
+
export default function CustomComponent() {
|
|
204
|
+
const { value, setValue } = useSyntux();
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<button
|
|
208
|
+
onClick={() => {
|
|
209
|
+
setValue(value, {
|
|
210
|
+
regenerate: true, // if false, treated as static
|
|
211
|
+
hint: "Change the style to be more..."
|
|
212
|
+
})
|
|
213
|
+
}}
|
|
214
|
+
>
|
|
215
|
+
Update UI!
|
|
216
|
+
</button>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The new user interface will be streamed.
|
|
222
|
+
|
|
223
|
+
#### Customize animation
|
|
224
|
+
|
|
225
|
+
By default, new elements fade in from below when mounted.
|
|
226
|
+
|
|
227
|
+
This motion cannot yet be customized. However, the duration and offset can, using the `animate` property:
|
|
228
|
+
|
|
229
|
+
```jsx
|
|
230
|
+
<GeneratedUI
|
|
231
|
+
model={anthropic("claude-sonnet-4-5")}
|
|
232
|
+
value={valueToDisplay}
|
|
233
|
+
animate={{
|
|
234
|
+
offset: 10, // pixels
|
|
235
|
+
duration: 100 // ms
|
|
236
|
+
}}
|
|
237
|
+
/>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
In order to disable the animation, set `offset` to 0 or `duration` to 0.
|
|
241
|
+
|
|
151
242
|
---
|
|
152
243
|
|
|
153
244
|
### FAQ
|
package/dist/client.d.mts
CHANGED
|
@@ -2,18 +2,19 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
2
2
|
import { StreamableValue } from '@ai-sdk/rsc';
|
|
3
3
|
import * as react from 'react';
|
|
4
4
|
import react__default, { JSX, ComponentType } from 'react';
|
|
5
|
-
import { A as AnimateOptions, a as ComponentMap, C as ChildrenMap } from './types-
|
|
5
|
+
import { A as AnimateOptions, R as RerenderContext, a as ComponentMap, C as ChildrenMap, b as RerenderOptions } from './types-0XgxLwqz.mjs';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Client wrapper for Renderer that handles streaming and parsing with server.
|
|
9
9
|
*/
|
|
10
|
-
declare function GeneratedClient({ value, allowedComponents, inputStream, placeholder, errorFallback, animate }: {
|
|
10
|
+
declare function GeneratedClient({ value, allowedComponents, inputStream, placeholder, errorFallback, animate, rerender }: {
|
|
11
11
|
value: any;
|
|
12
12
|
allowedComponents: Record<string, react__default.ComponentType<any> | string>;
|
|
13
13
|
inputStream: StreamableValue<string>;
|
|
14
14
|
placeholder?: JSX.Element;
|
|
15
15
|
errorFallback?: JSX.Element;
|
|
16
16
|
animate?: AnimateOptions;
|
|
17
|
+
rerender: RerenderContext;
|
|
17
18
|
}): react_jsx_runtime.JSX.Element;
|
|
18
19
|
|
|
19
20
|
interface RendererProps {
|
|
@@ -32,7 +33,7 @@ declare function Renderer(props: RendererProps): react_jsx_runtime.JSX.Element;
|
|
|
32
33
|
|
|
33
34
|
type SyntuxContextType = {
|
|
34
35
|
value: any;
|
|
35
|
-
setValue: (
|
|
36
|
+
setValue: (value: any, options?: RerenderOptions) => Promise<null> | null;
|
|
36
37
|
};
|
|
37
38
|
declare const SyntuxContext: react.Context<SyntuxContextType>;
|
|
38
39
|
declare function useSyntux(): SyntuxContextType;
|
package/dist/client.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use client";import{readStreamableValue as
|
|
2
|
-
`);return e.length>1?(e.slice(0,e.length-1).forEach(
|
|
1
|
+
"use client";import{readStreamableValue as W}from"@ai-sdk/rsc";import{useEffect as J,useMemo as z,useReducer as B,useRef as K,useState as I}from"react";var R=class{buffer="";total="";schema={childrenMap:{},componentMap:{},root:null};addDelta(t){this.total+=t,this.buffer+=t;let e=this.buffer.split(`
|
|
2
|
+
`);return e.length>1?(e.slice(0,e.length-1).forEach(n=>this.handleLine(n)),this.buffer=e[e.length-1],!0):!1}handleLine(t){try{let e=JSON.parse(t),{childrenMap:n,componentMap:a}=this.schema;a[e.id]=e,e.parentId===null?this.schema.root=e:(n[e.parentId]||(n[e.parentId]=[]),n[e.parentId].push(e.id))}catch{}}finish(){this.handleLine(this.buffer),this.buffer=""}};import{Fragment as D,useEffect as G,useState as Y}from"react";import{Fragment as F,jsx as h}from"react/jsx-runtime";import{createElement as j}from"react";var T=(r,t)=>t==="$"?r:t.split(".").reduce((e,n)=>e==null?void 0:e[n],r),E=(r,t,e)=>e.startsWith("$item.")?(e=e.slice(6),T(t,e)):e==="$item"?t:T(r,e),$=new Set(["dangerouslySetInnerHTML"]),U=(r,t,e)=>{if(!e)return e;if("$bind"in e){let n=E(r,t,e.$bind);return Object.keys(n).forEach(a=>{$.has(a)&&delete n[a]}),n}return Object.keys(e).forEach(n=>{if($.has(n)){delete e[n];return}let a=e[n];typeof a=="object"&&(e[n]=U(r,t,a))}),e},N=(r,t,e)=>typeof e=="object"?E(r,t,e.$bind):e;function v(r){var S,u,y;let[t,e]=Y(!1);G(()=>{let o=requestAnimationFrame(()=>e(!0));return()=>cancelAnimationFrame(o)},[]);let{id:n,componentMap:a,childrenMap:b,global:p,local:m,allowedComponents:g,animate:i}=r,s=a[n];if(s.type==="TEXT")return h(F,{children:N(p,m,s.content)});let x=(S=s.props)==null?void 0:S.source;if(s.type==="__ForEach__"&&x){let o=E(p,m,x);if(!Array.isArray(o))return null;let f=b[s.id];return h(F,{children:f==null?void 0:f.map((L,X)=>h(D,{children:o.map((_,k)=>j(v,{...r,id:L,local:_,key:k}))},X))})}let l=g[s.type]||s.type,c={...U(p,m,s.props)};c.style={...c.style||{}};let d=((u=c.style)==null?void 0:u.opacity)??1;c.style.opacity=t?d:0,c.style.transform=t?"translateY(0)":`translateY(${(i==null?void 0:i.offset)??10}px)`,c.style.transition=`opacity ${(i==null?void 0:i.duration)??200}ms ease-out, transform ${(i==null?void 0:i.duration)??200}ms ease-out`,c.style.willChange="opacity, transform";let P=N(p,m,s.content),w=((y=b[s.id])==null?void 0:y.map((o,f)=>h(v,{...r,id:o},f)))||[],M=[P,...w].filter(o=>o!=null);return M.length>0?h(l,{...c,children:M}):h(l,{...c})}import{createContext as q,useContext as H}from"react";var O=q(null);function se(){let r=H(O);if(!r)throw new Error("useSyntux must be used inside a GeneratedUI.");return r}import{Fragment as V,jsx as C}from"react/jsx-runtime";function ye({value:r,allowedComponents:t,inputStream:e,placeholder:n,errorFallback:a,animate:b,rerender:p}){var S;let[m,g]=I(r),[i,s]=I(e),[,x]=B(u=>u+1,0),l=K(null),[A,c]=I(!1);J(()=>{s(e)},[e]),J(()=>{let u=!0;return l.current=new R,(async()=>{var o;try{for await(let f of W(i)){if(!u)break;l.current&&f!==void 0&&l.current.addDelta(f)&&x()}u&&((o=l.current)==null||o.finish(),x())}catch{u&&c(!0)}})(),()=>{u=!1}},[i]);let d=(S=l==null?void 0:l.current)==null?void 0:S.schema,P=()=>A&&a?C(V,{children:a}):d!=null&&d.root?C(v,{id:d.root.id,componentMap:d.componentMap,childrenMap:d.childrenMap,allowedComponents:t,global:m,local:m,animate:b}):C(V,{children:n}),w=async(u,y)=>{if(!y||!y.regenerate)g(u);else if(p.action){if(l.current)return g(u),new Promise(async o=>{let{value:f}=await p.action(p.context,l.current.total,y.hint);s(f),o(null)})}else throw new Error("No rerender server action provided. Use the 'rerender' prop.")},M=z(()=>({value:m,setValue:w}),[m]);return C(V,{children:C(O.Provider,{value:M,children:P()})})}export{ye as GeneratedClient,v as Renderer,O as SyntuxContext,se as useSyntux};
|
|
3
3
|
//# sourceMappingURL=client.mjs.map
|
package/dist/client.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client/GeneratedClient.tsx","../src/ResponseParser.ts","../src/client/Renderer.tsx","../src/client/SyntuxContext.tsx"],"sourcesContent":["\"use client\";\r\n\r\nimport { StreamableValue, readStreamableValue } from '@ai-sdk/rsc';\r\nimport React, { JSX, useEffect, useMemo, useReducer, useRef, useState } from 'react';\r\nimport { AnimateOptions } from 'src/types';\r\nimport { ResponseParser } from '../ResponseParser';\r\nimport { Renderer } from './Renderer';\r\nimport { SyntuxContext } from './SyntuxContext';\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 errorFallback,\r\n animate\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 errorFallback?: JSX.Element,\r\n animate?: AnimateOptions\r\n}) {\r\n const [statefulValue, setStatefulValue] = useState(value); // stateful because changeable through context\r\n const [, forceUpdate] = useReducer(x => x + 1, 0);\r\n const parser = useRef<ResponseParser | null>(null);\r\n const [errored, setErrored] = useState(false)\r\n\r\n useEffect(() => {\r\n // forcibly create a new one for HMR\r\n parser.current = new ResponseParser();\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 }).catch(() => {\r\n setErrored(true)\r\n });\r\n }, [inputStream]);\r\n\r\n const schema = parser?.current?.schema;\r\n\r\n const providerValue = useMemo(() => ({ value: statefulValue, setValue: setStatefulValue }), [statefulValue]);\r\n\r\n const renderContent = () => {\r\n if(errored && errorFallback) return <>{errorFallback}</>\r\n\r\n if(schema?.root){\r\n return <Renderer id={schema.root.id} componentMap={schema.componentMap} childrenMap={schema.childrenMap} allowedComponents={allowedComponents} global={statefulValue} local={statefulValue} /> \r\n } else {\r\n return <>{placeholder}</>\r\n }\r\n }\r\n\r\n return (\r\n <>\r\n <SyntuxContext.Provider value={providerValue}>\r\n {renderContent()}\r\n </SyntuxContext.Provider >\r\n </>\r\n )\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}","\"use client\";\r\n\r\nimport { ComponentType, Fragment, useEffect, useState } from 'react';\r\nimport { AnimateOptions, ChildrenMap, ComponentMap } 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\nconst blacklistedProps = new Set([\"dangerouslySetInnerHTML\"])\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\r\n if (\"$bind\" in props) { // $bind may be falsy value\r\n const resolved = get(global, local, props.$bind);\r\n Object.keys(resolved).forEach((key) => {\r\n if (blacklistedProps.has(key)) {\r\n delete resolved[key];\r\n }\r\n })\r\n return resolved;\r\n }\r\n\r\n Object.keys(props).forEach((key) => {\r\n if (blacklistedProps.has(key)) {\r\n delete props[key];\r\n return;\r\n }\r\n\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\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 animate?: AnimateOptions;\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 [isVisible, setIsVisible] = useState(false);\r\n\r\n useEffect(() => {\r\n const frame = requestAnimationFrame(() => setIsVisible(true));\r\n return () => cancelAnimationFrame(frame)\r\n }, [])\r\n\r\n const {\r\n id, componentMap, childrenMap, global, local, allowedComponents, animate\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\r\n const animatedProps = {...componentProps}\r\n animatedProps.style = {...(animatedProps.style) || {}}\r\n\r\n const initialOpacity = animatedProps.style?.opacity ?? 1;\r\n animatedProps.style.opacity = isVisible ? initialOpacity : 0;\r\n animatedProps.style.transform = isVisible ? 'translateY(0)' : `translateY(${animate?.offset ?? 10}px)`;\r\n animatedProps.style.transition = `opacity ${animate?.duration ?? 200}ms ease-out, transform ${animate?.duration ?? 200}ms ease-out`;\r\n animatedProps.style.willChange = 'opacity, transform';\r\n\r\n const contentNode = renderContent(global, local, element.content);\r\n const childNodes = 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\r\n const nodesToRender = [contentNode, ...childNodes].filter(node => node !== null && node !== undefined) // 0 is falsy\r\n\r\n if (nodesToRender.length > 0) {\r\n return <Component {...animatedProps}>\r\n {nodesToRender}\r\n </Component>\r\n }\r\n\r\n return <Component {...animatedProps}/>\r\n}\r\n","import { createContext, useContext } from \"react\";\r\n\r\nexport type SyntuxContextType = {\r\n value: any,\r\n setValue: (arg0: any) => void\r\n}\r\n\r\nexport const SyntuxContext = createContext<SyntuxContextType | null>(null)\r\n\r\nexport function useSyntux(){\r\n const context = useContext(SyntuxContext);\r\n if(!context) throw new Error(\"useSyntux must be used inside a GeneratedUI.\");\r\n return context;\r\n}"],"mappings":"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","names":["readStreamableValue","useEffect","useMemo","useReducer","useRef","useState","ResponseParser","delta","split","line","node","childrenMap","componentMap","Fragment","useEffect","useState","jsx","createElement","resolvePath","obj","path","acc","curr","get","global","local","blacklistedProps","resolveProps","props","resolved","key","val","renderContent","content","Renderer","_a","_b","_c","isVisible","setIsVisible","frame","id","componentMap","childrenMap","allowedComponents","animate","element","sourceArrPath","sourceArr","childrenArr","childId","index","item","index1","Component","animatedProps","initialOpacity","contentNode","childNodes","nodesToRender","node","createContext","useContext","SyntuxContext","useSyntux","context","Fragment","jsx","GeneratedClient","value","allowedComponents","inputStream","placeholder","errorFallback","animate","_a","statefulValue","setStatefulValue","useState","forceUpdate","useReducer","x","parser","useRef","errored","setErrored","useEffect","ResponseParser","delta","readStreamableValue","schema","providerValue","useMemo","renderContent","Renderer","SyntuxContext"]}
|
|
1
|
+
{"version":3,"sources":["../src/client/GeneratedClient.tsx","../src/ResponseParser.ts","../src/client/Renderer.tsx","../src/client/SyntuxContext.tsx"],"sourcesContent":["\"use client\";\r\n\r\nimport { StreamableValue, readStreamableValue } from '@ai-sdk/rsc';\r\nimport React, { JSX, useEffect, useMemo, useReducer, useRef, useState } from 'react';\r\nimport { AnimateOptions, RerenderContext, RerenderOptions } from 'src/types';\r\nimport { ResponseParser } from 'src/ResponseParser';\r\nimport { Renderer } from './Renderer';\r\nimport { SyntuxContext } from './SyntuxContext';\r\n\r\n\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 errorFallback,\r\n animate,\r\n rerender\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 errorFallback?: JSX.Element,\r\n animate?: AnimateOptions,\r\n rerender: RerenderContext\r\n}) {\r\n const [statefulValue, setStatefulValue] = useState(value); // stateful because changeable through context\r\n const [statefulInputStream, setStatefulInputStream] = useState(inputStream);\r\n\r\n const [, forceUpdate] = useReducer(x => x + 1, 0);\r\n const parser = useRef<ResponseParser | null>(null);\r\n const [errored, setErrored] = useState(false)\r\n\r\n // HMR support\r\n useEffect(() => {\r\n setStatefulInputStream(inputStream)\r\n }, [inputStream])\r\n\r\n useEffect(() => {\r\n /**\r\n * flag to avoid conflicting streams from mutating UI.\r\n */\r\n let isActive = true;\r\n\r\n // forcibly create a new one for HMR\r\n parser.current = new ResponseParser();\r\n\r\n const parseStream = async () => {\r\n try {\r\n for await (const delta of readStreamableValue(statefulInputStream)) {\r\n if (!isActive) break;\r\n\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 if (isActive) {\r\n parser.current?.finish();\r\n forceUpdate();\r\n }\r\n } catch (err) {\r\n if (isActive) setErrored(true);\r\n }\r\n };\r\n\r\n parseStream();\r\n\r\n return () => {\r\n isActive = false;\r\n }\r\n }, [statefulInputStream]);\r\n\r\n const schema = parser?.current?.schema;\r\n\r\n\r\n const renderContent = () => {\r\n if (errored && errorFallback) return <>{errorFallback}</>\r\n\r\n if (schema?.root) {\r\n return <Renderer id={schema.root.id} componentMap={schema.componentMap} childrenMap={schema.childrenMap} allowedComponents={allowedComponents} global={statefulValue} local={statefulValue} animate={animate} />\r\n } else {\r\n return <>{placeholder}</>\r\n }\r\n }\r\n\r\n const modifyValue = async (value: any, options?: RerenderOptions): Promise<null> | null => {\r\n if (!options || !options.regenerate) {\r\n setStatefulValue(value);\r\n } else {\r\n if (!rerender.action) {\r\n throw new Error(\"No rerender server action provided. Use the 'rerender' prop.\")\r\n } else {\r\n if (parser.current) {\r\n setStatefulValue(value);\r\n return new Promise(async (resolve) => {\r\n const { value } = await rerender.action(rerender.context, parser.current.total, options.hint);\r\n setStatefulInputStream(value);\r\n resolve(null);\r\n })\r\n }\r\n }\r\n }\r\n }\r\n\r\n const providerValue = useMemo(() => ({\r\n value: statefulValue, setValue: modifyValue\r\n }), [statefulValue]);\r\n return (\r\n <>\r\n <SyntuxContext.Provider value={providerValue}>\r\n {renderContent()}\r\n </SyntuxContext.Provider >\r\n </>\r\n )\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 total = \"\"; // accumulator\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.total += delta;\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}","\"use client\";\r\n\r\nimport { ComponentType, Fragment, useEffect, useState } from 'react';\r\nimport { AnimateOptions, ChildrenMap, ComponentMap } 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\nconst blacklistedProps = new Set([\"dangerouslySetInnerHTML\"])\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\r\n if (\"$bind\" in props) { // $bind may be falsy value\r\n const resolved = get(global, local, props.$bind);\r\n Object.keys(resolved).forEach((key) => {\r\n if (blacklistedProps.has(key)) {\r\n delete resolved[key];\r\n }\r\n })\r\n return resolved;\r\n }\r\n\r\n Object.keys(props).forEach((key) => {\r\n if (blacklistedProps.has(key)) {\r\n delete props[key];\r\n return;\r\n }\r\n\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\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 animate?: AnimateOptions;\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 [isVisible, setIsVisible] = useState(false);\r\n\r\n useEffect(() => {\r\n const frame = requestAnimationFrame(() => setIsVisible(true));\r\n return () => cancelAnimationFrame(frame)\r\n }, [])\r\n\r\n const {\r\n id, componentMap, childrenMap, global, local, allowedComponents, animate\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\r\n const animatedProps = {...componentProps}\r\n animatedProps.style = {...(animatedProps.style) || {}}\r\n\r\n const initialOpacity = animatedProps.style?.opacity ?? 1;\r\n animatedProps.style.opacity = isVisible ? initialOpacity : 0;\r\n animatedProps.style.transform = isVisible ? 'translateY(0)' : `translateY(${animate?.offset ?? 10}px)`;\r\n animatedProps.style.transition = `opacity ${animate?.duration ?? 200}ms ease-out, transform ${animate?.duration ?? 200}ms ease-out`;\r\n animatedProps.style.willChange = 'opacity, transform';\r\n\r\n const contentNode = renderContent(global, local, element.content);\r\n const childNodes = 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\r\n const nodesToRender = [contentNode, ...childNodes].filter(node => node !== null && node !== undefined) // 0 is falsy\r\n\r\n if (nodesToRender.length > 0) {\r\n return <Component {...animatedProps}>\r\n {nodesToRender}\r\n </Component>\r\n }\r\n\r\n return <Component {...animatedProps}/>\r\n}\r\n","import { createContext, useContext } from \"react\";\r\nimport { RerenderOptions } from \"src/types\";\r\n\r\nexport type SyntuxContextType = {\r\n value: any,\r\n setValue: (value: any, options?: RerenderOptions) => Promise<null> | null\r\n}\r\n\r\nexport const SyntuxContext = createContext<SyntuxContextType | null>(null)\r\n\r\nexport function useSyntux(){\r\n const context = useContext(SyntuxContext);\r\n if(!context) throw new Error(\"useSyntux must be used inside a GeneratedUI.\");\r\n return context;\r\n}"],"mappings":"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","names":["readStreamableValue","useEffect","useMemo","useReducer","useRef","useState","ResponseParser","delta","split","line","node","childrenMap","componentMap","Fragment","useEffect","useState","jsx","createElement","resolvePath","obj","path","acc","curr","get","global","local","blacklistedProps","resolveProps","props","resolved","key","val","renderContent","content","Renderer","_a","_b","_c","isVisible","setIsVisible","frame","id","componentMap","childrenMap","allowedComponents","animate","element","sourceArrPath","sourceArr","childrenArr","childId","index","item","index1","Component","animatedProps","initialOpacity","contentNode","childNodes","nodesToRender","node","createContext","useContext","SyntuxContext","useSyntux","context","Fragment","jsx","GeneratedClient","value","allowedComponents","inputStream","placeholder","errorFallback","animate","rerender","_a","statefulValue","setStatefulValue","useState","statefulInputStream","setStatefulInputStream","forceUpdate","useReducer","x","parser","useRef","errored","setErrored","useEffect","isActive","ResponseParser","delta","readStreamableValue","schema","renderContent","Renderer","modifyValue","options","resolve","providerValue","useMemo","SyntuxContext"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { S as SyntuxComponent$1, U as UISchema } from './types-
|
|
2
|
-
export { A as AnimateOptions, C as ChildrenMap, a as ComponentMap, b as SchemaNode } from './types-
|
|
1
|
+
import { S as SyntuxComponent$1, U as UISchema } from './types-0XgxLwqz.mjs';
|
|
2
|
+
export { A as AnimateOptions, C as ChildrenMap, a as ComponentMap, R as RerenderContext, b as RerenderOptions, c as SchemaNode } from './types-0XgxLwqz.mjs';
|
|
3
3
|
import * as react from 'react';
|
|
4
4
|
import { JSX } from 'react';
|
|
5
|
+
import { StreamableValue } from '@ai-sdk/rsc';
|
|
5
6
|
import { LanguageModel } from 'ai';
|
|
6
7
|
import { SyntuxComponent, AnimateOptions } from 'getsyntux';
|
|
7
8
|
|
|
@@ -17,6 +18,9 @@ interface GeneratedContentProps {
|
|
|
17
18
|
onError?: (arg0: any) => void;
|
|
18
19
|
errorFallback?: JSX.Element;
|
|
19
20
|
animate?: AnimateOptions;
|
|
21
|
+
rerender?: (arg0: string, arg1: string, arg2: string) => Promise<{
|
|
22
|
+
value: StreamableValue;
|
|
23
|
+
}>;
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
/**
|
|
@@ -41,6 +45,7 @@ declare function createSkeleton(input: any): any;
|
|
|
41
45
|
*/
|
|
42
46
|
declare class ResponseParser {
|
|
43
47
|
buffer: string;
|
|
48
|
+
total: string;
|
|
44
49
|
schema: UISchema;
|
|
45
50
|
/**
|
|
46
51
|
* Update schema with latest data chunk.
|
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
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"),
|
|
2
|
-
<ComponentContext>${
|
|
3
|
-
<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"),l=(i==null?void 0:i.map(r=>r.context?`${r.name} [props: ${r.props}, details: ${r.context}]`:`${r.name} [props: ${r.props}]`).join(","))||"",p=o,u=JSON.stringify(t?a(n):n);return`<AllowedComponents>${s}</AllowedComponents>
|
|
2
|
+
<ComponentContext>${l}</ComponentContext>
|
|
3
|
+
<UserContext>${p||""}</UserContext>
|
|
4
4
|
<IsSkeleton>${t.toString()}</IsSkeleton>
|
|
5
5
|
<Value>
|
|
6
6
|
${u}
|
|
7
|
-
</Value>`}function
|
|
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{
|
|
7
|
+
</Value>`}function a(n){return n===null?"null":typeof n!="object"?typeof n:Array.isArray(n)?n.length==0?"null":[a(n[0])]:Object.entries(n).reduce((t,[e,o])=>(t[e]=a(o),t),{})}var f=class{buffer="";total="";schema={childrenMap:{},componentMap:{},root:null};addDelta(t){this.total+=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{f as ResponseParser,d as constructInput,a as createSkeleton,h as generateComponentMap};
|
|
9
9
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -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, 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\r\n const inputValue = JSON.stringify(skeletonize ? createSkeleton(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 createSkeleton(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 [createSkeleton(input[0])]\r\n }\r\n }\r\n return Object.entries(input).reduce((acc, [key, value]) => {\r\n acc[key] = createSkeleton(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,EAEdK,EAAa,KAAK,UAAUP,EAAcQ,EAAeT,CAAK,EAAIA,CAAK,EAE7E,MAAO,sBAAsBJ,CAAiB;AAAA,oBAA2CU,CAAgB;AAAA,eAAqCC,GAAe,EAAE;AAAA,cAA+BN,EAAY,SAAS,CAAC;AAAA;AAAA,EAA2BO,CAAU;AAAA,SAC7P,CAQO,SAASC,EAAeC,EAAY,CACvC,OAAIA,IAAU,KAAa,OAEvB,OAAOA,GAAU,SAAiB,OAAOA,EAEzC,MAAM,QAAQA,CAAK,EACfA,EAAM,QAAU,EACT,OAEA,CAACD,EAAeC,EAAM,CAAC,CAAC,CAAC,EAGjC,OAAO,QAAQA,CAAK,EAAE,OAAO,CAACb,EAAK,CAACc,EAAKX,CAAK,KACjDH,EAAIc,CAAG,EAAIF,EAAeT,CAAK,EACxBH,GACR,CAAC,CAAC,CACT,CC/DO,IAAMe,EAAN,KAAqB,CACxB,OAAS,
|
|
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\r\n const inputValue = JSON.stringify(skeletonize ? createSkeleton(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 createSkeleton(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 [createSkeleton(input[0])]\r\n }\r\n }\r\n return Object.entries(input).reduce((acc, [key, value]) => {\r\n acc[key] = createSkeleton(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 total = \"\"; // accumulator\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.total += delta;\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,EAEdK,EAAa,KAAK,UAAUP,EAAcQ,EAAeT,CAAK,EAAIA,CAAK,EAE7E,MAAO,sBAAsBJ,CAAiB;AAAA,oBAA2CU,CAAgB;AAAA,eAAqCC,GAAe,EAAE;AAAA,cAA+BN,EAAY,SAAS,CAAC;AAAA;AAAA,EAA2BO,CAAU;AAAA,SAC7P,CAQO,SAASC,EAAeC,EAAY,CACvC,OAAIA,IAAU,KAAa,OAEvB,OAAOA,GAAU,SAAiB,OAAOA,EAEzC,MAAM,QAAQA,CAAK,EACfA,EAAM,QAAU,EACT,OAEA,CAACD,EAAeC,EAAM,CAAC,CAAC,CAAC,EAGjC,OAAO,QAAQA,CAAK,EAAE,OAAO,CAACb,EAAK,CAACc,EAAKX,CAAK,KACjDH,EAAIc,CAAG,EAAIF,EAAeT,CAAK,EACxBH,GACR,CAAC,CAAC,CACT,CC/DO,IAAMe,EAAN,KAAqB,CACxB,OAAS,GACT,MAAQ,GAGR,OAAmB,CACf,YAAa,CAAC,EACd,aAAc,CAAC,EACf,KAAM,IACV,EAUA,SAASC,EAAe,CACpB,KAAK,OAASA,EACd,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","createSkeleton","input","key","ResponseParser","delta","split","line","node","childrenMap","componentMap"]}
|
package/dist/metafile-esm.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"src/types.ts":{"bytes":
|
|
1
|
+
{"inputs":{"src/types.ts":{"bytes":1256,"imports":[{"path":"@ai-sdk/rsc","kind":"import-statement","external":true}],"format":"esm"},"src/util.ts":{"bytes":2558,"imports":[{"path":"./templates/GeneratedUI","kind":"import-statement","external":true},{"path":"./types","kind":"import-statement","external":true}],"format":"esm"},"src/ResponseParser.ts":{"bytes":1923,"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":4570,"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/SyntuxContext.tsx":{"bytes":497,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"src/types","kind":"import-statement","external":true}],"format":"esm"},"src/client/GeneratedClient.tsx":{"bytes":3579,"imports":[{"path":"@ai-sdk/rsc","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"src/types","kind":"import-statement","external":true},{"path":"src/ResponseParser.ts","kind":"import-statement","original":"src/ResponseParser"},{"path":"src/client/Renderer.tsx","kind":"import-statement","original":"./Renderer"},{"path":"src/client/SyntuxContext.tsx","kind":"import-statement","original":"./SyntuxContext"},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/client.ts":{"bytes":133,"imports":[{"path":"src/client/GeneratedClient.tsx","kind":"import-statement","original":"./client/GeneratedClient"},{"path":"src/client/Renderer.tsx","kind":"import-statement","original":"./client/Renderer"},{"path":"src/client/SyntuxContext.tsx","kind":"import-statement","original":"./client/SyntuxContext"}],"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":6941},"dist/index.mjs":{"imports":[],"exports":["ResponseParser","constructInput","createSkeleton","generateComponentMap"],"entryPoint":"src/index.ts","inputs":{"src/index.ts":{"bytesInOutput":0},"src/util.ts":{"bytesInOutput":809},"src/ResponseParser.ts":{"bytesInOutput":508}},"bytes":1412},"dist/client.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":17974},"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","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"exports":["GeneratedClient","Renderer","SyntuxContext","useSyntux"],"entryPoint":"src/client.ts","inputs":{"src/client/GeneratedClient.tsx":{"bytesInOutput":1220},"src/ResponseParser.ts":{"bytesInOutput":508},"src/client/Renderer.tsx":{"bytesInOutput":1727},"src/client/SyntuxContext.tsx":{"bytesInOutput":172},"src/client.ts":{"bytesInOutput":0}},"bytes":3720},"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,9 +1,9 @@
|
|
|
1
1
|
import { JSX } from 'react';
|
|
2
2
|
|
|
3
|
-
import { createStreamableValue } from '@ai-sdk/rsc';
|
|
3
|
+
import { createStreamableValue, StreamableValue } from '@ai-sdk/rsc';
|
|
4
4
|
import { LanguageModel, streamText } from 'ai';
|
|
5
5
|
|
|
6
|
-
import { AnimateOptions,
|
|
6
|
+
import { AnimateOptions, constructInput, generateComponentMap, ResponseParser, SyntuxComponent, UISchema } from "getsyntux";
|
|
7
7
|
import { GeneratedClient, Renderer } from 'getsyntux/client';
|
|
8
8
|
|
|
9
9
|
import spec from './spec';
|
|
@@ -26,6 +26,8 @@ export interface GeneratedContentProps {
|
|
|
26
26
|
onError?: (arg0: any) => void;
|
|
27
27
|
errorFallback?: JSX.Element;
|
|
28
28
|
animate?: AnimateOptions
|
|
29
|
+
|
|
30
|
+
rerender?: (arg0: string, arg1: string, arg2: string) => Promise<{ value: StreamableValue }>
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
/**
|
|
@@ -37,67 +39,54 @@ export interface GeneratedContentProps {
|
|
|
37
39
|
* @param placeholder A placeholder to show while awaiting streaming (NOT during streaming)
|
|
38
40
|
* @param cached Schema returned from onGenerate, used for caching UI
|
|
39
41
|
* @param onGenerate Callback which accepts a string, to be passed to `cached` to reuse same UI
|
|
40
|
-
* @param skeletonize Compresses value for large inputs (arrays) or untrusted input
|
|
41
|
-
* @param onError Callback which accepts an error, invoked when necessary. If not provided, runtime error occurs.
|
|
42
|
-
* @param errorFallback An element fallback to show if an error occurs during generation.
|
|
43
42
|
*/
|
|
44
43
|
export async function GeneratedUI(props: GeneratedContentProps) {
|
|
45
44
|
const input = constructInput(props);
|
|
45
|
+
const rerenderContext = input.split('\n').slice(0, 2);
|
|
46
46
|
|
|
47
|
-
const { value, model, components, placeholder, cached, onGenerate,
|
|
48
|
-
|
|
47
|
+
const { value, model, components, placeholder, cached, onGenerate, rerender } = props;
|
|
48
|
+
|
|
49
49
|
const allowedComponents = generateComponentMap(components || []);
|
|
50
50
|
|
|
51
51
|
// prerender if cached
|
|
52
|
-
if
|
|
52
|
+
if(cached){
|
|
53
53
|
const parser = new ResponseParser();
|
|
54
54
|
parser.addDelta(cached);
|
|
55
55
|
parser.finish();
|
|
56
56
|
|
|
57
57
|
const schema: UISchema = parser.schema;
|
|
58
58
|
|
|
59
|
-
if
|
|
59
|
+
if(schema.root){
|
|
60
60
|
return <Renderer id={schema.root.id} componentMap={schema.componentMap} childrenMap={schema.childrenMap}
|
|
61
|
-
allowedComponents={allowedComponents} global={value} local={value}
|
|
61
|
+
allowedComponents={allowedComponents} global={value} local={value} />
|
|
62
62
|
} else {
|
|
63
63
|
return <></>;
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
const stream = createStreamableValue('');
|
|
68
|
+
|
|
68
69
|
(async () => {
|
|
69
70
|
let total = "";
|
|
70
|
-
let errored = false;
|
|
71
71
|
|
|
72
72
|
const { textStream } = await streamText({
|
|
73
73
|
model,
|
|
74
74
|
system: spec,
|
|
75
|
-
prompt: input
|
|
76
|
-
onError: (err) => {
|
|
77
|
-
stream.error(err)
|
|
78
|
-
errored = true;
|
|
79
|
-
|
|
80
|
-
if (!onError) {
|
|
81
|
-
if (!errorFallback) {
|
|
82
|
-
throw err;
|
|
83
|
-
}
|
|
84
|
-
} else {
|
|
85
|
-
onError(err)
|
|
86
|
-
}
|
|
87
|
-
}
|
|
75
|
+
prompt: input
|
|
88
76
|
})
|
|
89
77
|
|
|
90
|
-
for await
|
|
78
|
+
for await(const delta of textStream){
|
|
91
79
|
stream.update(delta);
|
|
92
80
|
total += delta;
|
|
93
81
|
}
|
|
94
82
|
|
|
95
|
-
|
|
96
|
-
stream.done();
|
|
97
|
-
}
|
|
83
|
+
stream.done();
|
|
98
84
|
|
|
99
|
-
if
|
|
85
|
+
if(onGenerate) onGenerate(total);
|
|
100
86
|
})()
|
|
101
87
|
|
|
102
|
-
return <GeneratedClient value={value} allowedComponents={allowedComponents} inputStream={stream.value} placeholder={placeholder}
|
|
88
|
+
return <GeneratedClient value={value} allowedComponents={allowedComponents} inputStream={stream.value} placeholder={placeholder} rerender={{
|
|
89
|
+
context: rerenderContext.join('\n'),
|
|
90
|
+
action: rerender
|
|
91
|
+
}} />
|
|
103
92
|
}
|
|
@@ -1,8 +1,54 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* this file is optional!
|
|
5
|
+
* you can delete it if you do not need rerender functionality.
|
|
6
|
+
*
|
|
7
|
+
* run "npm i @ai-sdk/anthropic" if you want to use Claude.
|
|
8
|
+
*/
|
|
9
|
+
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
10
|
+
import { createStreamableValue } from "@ai-sdk/rsc";
|
|
11
|
+
import { streamText } from "ai";
|
|
12
|
+
|
|
13
|
+
import spec from "./spec";
|
|
14
|
+
|
|
1
15
|
/**
|
|
2
|
-
*
|
|
3
|
-
* This is UNSECURED! Please modify it to perform authencation checks etc,.
|
|
16
|
+
* IMPORTANT: replace the below with your own model.
|
|
4
17
|
*/
|
|
18
|
+
const anthropic = createAnthropic({
|
|
19
|
+
apiKey: "..."
|
|
20
|
+
})
|
|
21
|
+
const model = anthropic("claude-haiku-4-5");
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The server action for rerendering the UI.
|
|
25
|
+
* This is an UNSECURED ENDPOINT. Please modify it to perform your own authentication etc,.
|
|
26
|
+
*
|
|
27
|
+
* @param context information about allowed components, component context etc,.
|
|
28
|
+
* @param existing the current UI schema.
|
|
29
|
+
* @param hint the update request.
|
|
30
|
+
*/
|
|
31
|
+
export async function rerenderAction(context: string, existing: string, hint: string) {
|
|
32
|
+
const stream = createStreamableValue('');
|
|
33
|
+
(async () => {
|
|
34
|
+
let total = "";
|
|
35
|
+
let errored = false;
|
|
36
|
+
|
|
37
|
+
const { textStream } = await streamText({
|
|
38
|
+
model,
|
|
39
|
+
system: spec,
|
|
40
|
+
prompt: `${context}\n<Existing>${existing}</Existing>\n<UserContext>${hint}</UserContext>`,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
for await (const delta of textStream) {
|
|
44
|
+
stream.update(delta);
|
|
45
|
+
total += delta;
|
|
46
|
+
}
|
|
5
47
|
|
|
6
|
-
|
|
48
|
+
if (!errored) {
|
|
49
|
+
stream.done();
|
|
50
|
+
}
|
|
51
|
+
})()
|
|
7
52
|
|
|
53
|
+
return { value: stream.value }
|
|
8
54
|
}
|
package/dist/templates/spec.md
CHANGED
package/dist/templates/spec.ts
CHANGED
|
@@ -78,6 +78,17 @@ Input:
|
|
|
78
78
|
Output:
|
|
79
79
|
{ ... }
|
|
80
80
|
... more lines
|
|
81
|
+
|
|
82
|
+
Or for an existing UI:
|
|
83
|
+
|
|
84
|
+
<AllowedComponents>...</AllowedComponents>
|
|
85
|
+
<ComponentContext>...</ComponentContext>
|
|
86
|
+
<UserContext>...</UserContext>
|
|
87
|
+
<Existing>...</Existing>
|
|
88
|
+
|
|
89
|
+
Output:
|
|
90
|
+
{ ... }
|
|
91
|
+
... more lines
|
|
81
92
|
</output_formatting>
|
|
82
93
|
|
|
83
94
|
<reasoning_requirements>
|
|
@@ -88,6 +99,7 @@ Output:
|
|
|
88
99
|
|
|
89
100
|
<IMPORTANT>
|
|
90
101
|
Do NOT output anything EXCEPT the list of JSON.
|
|
102
|
+
Do NOT add event listeners.
|
|
91
103
|
</IMPORTANT>`;
|
|
92
104
|
|
|
93
105
|
export default spec;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { StreamableValue } from '@ai-sdk/rsc';
|
|
2
|
+
|
|
3
|
+
type SchemaNode = {
|
|
4
|
+
id: string;
|
|
5
|
+
parentId: string | null;
|
|
6
|
+
type: string;
|
|
7
|
+
props?: Record<string, any>;
|
|
8
|
+
content?: any | {
|
|
9
|
+
"$bind": string;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
type ComponentMap = Record<string, SchemaNode>;
|
|
13
|
+
type ChildrenMap = Record<string, string[]>;
|
|
14
|
+
type UISchema = {
|
|
15
|
+
componentMap: ComponentMap;
|
|
16
|
+
childrenMap: ChildrenMap;
|
|
17
|
+
root: SchemaNode | null;
|
|
18
|
+
};
|
|
19
|
+
type SyntuxComponent = {
|
|
20
|
+
name: string;
|
|
21
|
+
props: string;
|
|
22
|
+
component: React.ComponentType<any>;
|
|
23
|
+
context?: string;
|
|
24
|
+
};
|
|
25
|
+
type AnimateOptions = {
|
|
26
|
+
offset: number;
|
|
27
|
+
duration: number;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* setValue will not send a request if regenerate is false.
|
|
31
|
+
* however, the value will still be updated (statically).
|
|
32
|
+
* as opposed to a falsy options existence check, this is more robust for DX.
|
|
33
|
+
*/
|
|
34
|
+
type RerenderOptions = {
|
|
35
|
+
regenerate: boolean;
|
|
36
|
+
hint: string;
|
|
37
|
+
};
|
|
38
|
+
type RerenderContext = {
|
|
39
|
+
context: string;
|
|
40
|
+
action?: (arg0: string, arg1: string, arg2: string) => Promise<{
|
|
41
|
+
value: StreamableValue;
|
|
42
|
+
}>;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type { AnimateOptions as A, ChildrenMap as C, RerenderContext as R, SyntuxComponent as S, UISchema as U, ComponentMap as a, RerenderOptions as b, SchemaNode as c };
|
package/package.json
CHANGED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
type SchemaNode = {
|
|
2
|
-
id: string;
|
|
3
|
-
parentId: string | null;
|
|
4
|
-
type: string;
|
|
5
|
-
props?: Record<string, any>;
|
|
6
|
-
content?: any | {
|
|
7
|
-
"$bind": string;
|
|
8
|
-
};
|
|
9
|
-
};
|
|
10
|
-
type ComponentMap = Record<string, SchemaNode>;
|
|
11
|
-
type ChildrenMap = Record<string, string[]>;
|
|
12
|
-
type UISchema = {
|
|
13
|
-
componentMap: ComponentMap;
|
|
14
|
-
childrenMap: ChildrenMap;
|
|
15
|
-
root: SchemaNode | null;
|
|
16
|
-
};
|
|
17
|
-
type SyntuxComponent = {
|
|
18
|
-
name: string;
|
|
19
|
-
props: string;
|
|
20
|
-
component: React.ComponentType<any>;
|
|
21
|
-
context?: string;
|
|
22
|
-
};
|
|
23
|
-
type AnimateOptions = {
|
|
24
|
-
offset: number;
|
|
25
|
-
duration: number;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export type { AnimateOptions as A, ChildrenMap as C, SyntuxComponent as S, UISchema as U, ComponentMap as a, SchemaNode as b };
|