getsyntux 0.1.2 → 0.2.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/dist/index.mjs CHANGED
@@ -1,232 +1,7 @@
1
- import { isValidElement } from 'react';
2
- import { jsx, Fragment } from 'react/jsx-runtime';
3
- import { generateText } from 'ai';
4
-
5
- // src/GeneratedPage.tsx
6
- function GeneratedContent(props) {
7
- return /* @__PURE__ */ jsx(Fragment, {});
8
- }
9
- var SIGNATURE = /* @__PURE__ */ Symbol("GeneratedContent");
10
- GeneratedContent.identifier = SIGNATURE;
11
- var resolvePath = (obj, path) => {
12
- if (path === "$") return obj;
13
- return path.split(".").reduce((acc, curr) => acc == null ? void 0 : acc[curr], obj);
14
- };
15
- var get = (global, local, path) => {
16
- if (path.startsWith("$item.")) {
17
- path = path.slice(6);
18
- return resolvePath(local, path);
19
- } else {
20
- if (path === "$item") return local;
21
- return resolvePath(global, path);
22
- }
23
- };
24
- var resolveProps = (global, local, props) => {
25
- if (!props) return;
26
- if ("$bind" in props) return get(global, local, props.$bind);
27
- Object.keys(props).forEach((key) => {
28
- const val = props[key];
29
- if (typeof val === "object") {
30
- props[key] = resolveProps(global, local, val);
31
- }
32
- });
33
- return props;
34
- };
35
- function Renderer({
36
- schema,
37
- global,
38
- local,
39
- components
40
- }) {
41
- var _a;
42
- if (typeof schema === "string") return /* @__PURE__ */ jsx(Fragment, { children: schema });
43
- if (schema.$bind) {
44
- return /* @__PURE__ */ jsx(Fragment, { children: get(global, local, schema.$bind) });
45
- }
46
- if (schema.type === "__ForEach__" && schema.source && schema.template) {
47
- const sourceArr = get(global, local, schema.source);
48
- if (!Array.isArray(sourceArr)) return null;
49
- return /* @__PURE__ */ jsx(Fragment, { children: sourceArr.map((item, index) => {
50
- return /* @__PURE__ */ jsx(
51
- Renderer,
52
- {
53
- schema: schema.template,
54
- global,
55
- local: item,
56
- components
57
- },
58
- index
59
- );
60
- }) });
61
- }
62
- if (!schema.type) return null;
63
- const Component = components[schema.type] || schema.type;
64
- return /* @__PURE__ */ jsx(Component, { ...resolveProps(global, local, schema.props), children: (_a = schema.children) == null ? void 0 : _a.map((item, index) => {
65
- return /* @__PURE__ */ jsx(
66
- Renderer,
67
- {
68
- schema: item,
69
- global,
70
- local,
71
- components
72
- },
73
- index
74
- );
75
- }) });
76
- }
77
-
78
- // src/util.ts
79
- var endDelimiter = "</UISchema>";
80
- function parseResponse(llmResponse) {
81
- const split = llmResponse.split(/\<UISchema index="\d+">/m).slice(1).map((e) => e.trim());
82
- const contents = split.map((e) => e.slice(0, -endDelimiter.length));
83
- const parsed = contents.map((e) => JSON.parse(e));
84
- return parsed;
85
- }
86
-
87
- // src/prompt.md
88
- var prompt_default = '<system_persona>\r\nYou are the **UI Schema Generation Engine**, a specialized architect responsible for converting raw data structures into abstract, reusable React Interface Schemas (JSON-DSL).\r\n\r\nYour output is **NOT** code. Your output is a JSON-based Abstract Syntax Tree (AST) that describes the UI structure. The rendering engine will hydrate this schema with data later.\r\n</system_persona>\r\n\r\n<core_philosophy>\r\n1. **Separation of Concerns:** You define the *structure*. You do NOT hardcode *values*.\r\n2. **Strict Adherence:** You may ONLY use components explicitly listed in the `<AllowedComponents>` block of the input. If the block is missing or empty, revert to the <default_component_library>.\r\n3. **Semantic Structure:** Even though you are generating JSON, the resulting UI must be semantically correct (using proper hierarchy, semantic HTML tags, and logical grouping).\r\n4. **Reusability:** Your schema must be valid regardless of the specific values in the data. It must handle list lengths of 0 or 1000 gracefully using the Iterator Protocol.\r\n</core_philosophy>\r\n\r\n<dsl_syntax_rules>\r\nThe output must be a JSON object adhering to this strict recursive interface:\r\n\r\n### 1. The Standard Node\r\nUsed for HTML tags or Custom Components.\r\n```json\r\n{\r\n "type": "div" | "h1" | "MyCustomComponent",\r\n "props": { "className": "...", "customProp": "..." },\r\n "children": [] // Array of Nodes or Strings\r\n}\r\n```\r\n\r\n### 2. The Binding Protocol (`$bind`)\r\n\r\n**NEVER** output raw data values (e.g., "John", "john@email.com") in the schema.\r\nInstead, bind to a path.\r\n\r\n```json\r\n// BAD\r\n{ "type": "span", "children": ["John"] }\r\n\r\n// GOOD\r\n{ "type": "span", "children": [{ "$bind": "user.firstName" }] }\r\n```\r\n\r\nYou have access to exactly **two scopes** at any time:\r\n- **Global Scope (Root)**: Any path **WITHOUT** the `$item` prefix accesses the top-level global data object. This is available at any nesting depth.\r\n - *Example:* `"$bind": "pageTitle"` (Always looks at `data.pageTitle`)\r\n- **Local Scope (Current Item):** Any path **STARTING WITH** `$item` accesses the immediate object currently being iterated in a loop.\r\n - *Example:* `"$bind": "$item.name"` (Looks at `name` on the current loop item).\r\n - *Example:* `"$bind": "$item"` (Renders the current loop item, usually a string).\r\n\r\nIn special cases, you may need to reference a specific, fixed index in an array. To do that, use dot notation to access the index. For instance: `arr.1.property` accesses the `property` property of the 2nd item in `arr`. However, use this conservatively; prefer to use the Iterator Protocol unless it doesn\'t make sense (e.g., it\'s a static array).\r\n\r\n**CRITICAL:** Intermediate scopes are not accessible. Inside a nested loop, `$item` refers *only* to the innermost item.\r\n\r\n### 3. The Iterator Protocol (`__ForEach__`)\r\n\r\nIf the data value is an Array, you MUST use this node. Do not manually unroll lists.\r\n\r\n- **Root Arrays**: If the input value is the array itself, use "source": "$".\r\n- **Nested Arrays**: If the array is a property on the current item, use `"source": "$item.path.to.array"`.\r\n- **Global Arrays**: If the array is a property on the Global Root, use `"source": "path.to.array"`.\r\n\r\n```json\r\n{\r\n "type": "__ForEach__",\r\n "source": "$" | "$item.path.to.array" | "path.to.array",\r\n "template": {\r\n // This is the shape of a SINGLE item.\r\n // Inside here, use "$item" to refer to the current array element.\r\n "type": "li",\r\n "children": [{ "$bind": "$item.name" }]\r\n }\r\n}\r\n```\r\n\r\n</dsl_syntax_rules>\r\n\r\n<default_component_library>\r\nIf no specific allowed components are provided, you have access to this semantic HTML suite:\r\n\r\n * **Layout:** `div`, `section`, `article`, `main`, `aside`, `header`, `footer`\r\n * **Typography:** `h1`, `h2`, `h3`, `p`, `span`, `strong`, `em`, `blockquote`, `pre`, `code`\r\n * **Lists:** `ul`, `ol`, `li`\r\n * **Interaction:** `button`, `a` (use href prop), `details`, `summary`\r\n * **Form:** `input`, `label`, `textarea`, `select`, `option`\r\n * **Media:** `img`, `figure`, `figcaption`\r\n\r\n</default_component_library>\r\n\r\n<input_processing_rules>\r\nThe user will provide one or more `<GeneratedContent>` blocks.\r\n\r\n1. **Parse `AllowedComponents`:** A comma-separated list.\r\n * Lowercase = Native HTML tags.\r\n * Uppercase = Custom React Components.\r\n2. **Parse `ComponentContext`:** Defines the TypeScript interface for Custom Components.\r\n * *CRITICAL:* You must strictly adhere to the prop names and types defined here. Do not hallucinate props for custom components.\r\n * 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. Use this information in whatever way to improve your generation output. If you encounter a complex type in `props`, look at the input values and make your best guess at what value is the best fit.\r\n3. **Parse `UserContext`:** This is anything the developer believes is relevant for your task. It could describe a specific UI style or the correct way to use a custom component.\r\n * *CRITICAL*: To the best of your ability, respect the developer\'s wishes. If no UserContext is provided, you should accomplish the task as you see fit, with no constraints.\r\n * *CRITICAL*: This is only relevant to the current <GeneratedContent> block!\r\n3. **Parse `Value`:** The JSON data structure you are building the UI for.\r\n\r\nAdditionally, the user may provide a <PageContext> before all <GeneratedContent> blocks. Think of this as a global UserContext. This is meant to provide additional context and guiding when designing the UI, which you should try to adhere to. If none is provided, accomplish the task as you see fit, with no constraints.\r\n\r\n</input_processing_rules>\r\n\r\n<output_formatting>\r\nFor every `<GeneratedContent>` input block, you must generate exactly one `<UISchema>` output block.\r\nThe order must be preserved.\r\n\r\nInput:\r\n<PageContext></PageContext>\r\n<GeneratedContent> ... </GeneratedContent> (Section 1)\r\n<GeneratedContent> ... </GeneratedContent> (Section 2)\r\n\r\nOutput:\r\n<UISchema index="0"> ...JSON... </UISchema>\r\n<UISchema index="1"> ...JSON... </UISchema>\r\n</output_formatting>\r\n\r\n<examples>\r\n**Input:**\r\n<GeneratedContent>\r\n<AllowedComponents>div,span,Avatar</AllowedComponents>\r\n<ComponentContext>Avatar [props: { url: string }]</ComponentContext>\r\n<UserContext>grouped under one div</UserContext>\r\n<Value>{ authors: [{ name: "J.K.", img: "..." }, { name: "Tolkien", img: "..." }] }</Value>\r\n</GeneratedContent>\r\n\r\n**Correct Output:**\r\n<UISchema index="0">\r\n{\r\n "type": "div",\r\n "props": {\r\n "className": "author-list"\r\n },\r\n "children": [\r\n {\r\n "type": "__ForEach__",\r\n "source": "authors",\r\n "template": {\r\n "type": "div",\r\n "props": {\r\n "className": "author-card"\r\n },\r\n "children": [\r\n {\r\n "type": "Avatar",\r\n "props": {\r\n "url": {\r\n "$bind": "$item.img"\r\n }\r\n }\r\n },\r\n {\r\n "type": "span",\r\n "children": [\r\n {\r\n "$bind": "$item.name"\r\n }\r\n ]\r\n }\r\n ]\r\n }\r\n }\r\n ]\r\n}\r\n</UISchema>\r\n</examples>\r\n\r\n<reasoning_requirements>\r\nBefore generating the JSON, briefly analyze the data structure to identify arrays (requiring `__ForEach__`) and custom component opportunities. Use the mental scratchpad if necessary, but keep the final output strictly within `<UISchema>` tags.\r\n</reasoning_requirements>\r\n\r\n<IMPORTANT>\r\nDo NOT output anything except the UISchema.\r\n</IMPORTANT>';
89
- function extractChildren(element) {
90
- var _a;
91
- if ((_a = element == null ? void 0 : element.props) == null ? void 0 : _a.children) {
92
- let children = element.props.children;
93
- if (!Array.isArray(children)) children = [children];
94
- return children;
95
- }
96
- return [];
97
- }
98
- function searchChildren(element, acc) {
99
- if (!isValidElement(element)) return;
100
- if (element.type.identifier === SIGNATURE) {
101
- const { values, components, hint } = element.props;
102
- const realComponents = [];
103
- if (components) {
104
- components.forEach((comp) => {
105
- if (typeof comp === "string") {
106
- realComponents.push({
107
- llmName: comp
108
- });
109
- } else {
110
- realComponents.push({
111
- llmName: comp.llmName,
112
- llmContext: comp.llmContext,
113
- userContext: comp.userContext
114
- });
115
- }
116
- });
117
- }
118
- acc.push({
119
- values,
120
- components: realComponents,
121
- hint: hint || ""
122
- });
123
- } else {
124
- const children = extractChildren(element);
125
- children.forEach((child) => {
126
- searchChildren(child, acc);
127
- });
128
- }
129
- }
130
- function generateInput(content, context) {
131
- let str = "";
132
- if (context) {
133
- str = `<PageContext>${context}</PageContext>
134
- `;
135
- }
136
- content.forEach((schema) => {
137
- const allowedComponents = schema.components.map((e) => e.llmName);
138
- const componentContext = [];
139
- schema.components.forEach((comp) => {
140
- if (comp.llmContext) {
141
- let contextStr;
142
- if (comp.userContext) {
143
- contextStr = `${comp.llmName} [${comp.llmContext}, details: ${comp.userContext}]`;
144
- } else {
145
- contextStr = `${comp.llmName} [${comp.llmContext}]`;
146
- }
147
- componentContext.push(contextStr);
148
- }
149
- });
150
- const userContext = schema.hint;
151
- const value = JSON.stringify(schema.values);
152
- str += `<GeneratedContent>
153
- <AllowedComponents>${allowedComponents.join(",")}</AllowedComponents>
154
- <ComponentContext>${componentContext.join(",")}</ComponentContext>
155
- <UserContext>${userContext}</UserContext>
156
- <Value>${value}</Value>
157
- </GeneratedContent>
158
- `;
159
- });
160
- return str;
161
- }
162
- function createComponentRegistry(components) {
163
- if (!components) return {};
164
- return components.reduce((acc, curr) => {
165
- if (typeof curr === "string") {
166
- acc[curr] = curr;
167
- } else {
168
- if (curr.llmName) {
169
- acc[curr.llmName] = curr;
170
- }
171
- }
172
- return acc;
173
- }, {});
174
- }
175
- function hydrate(schema, dsl) {
176
- let componentIndex = 0;
177
- function swapChildren(element) {
178
- if (!isValidElement(element)) return element;
179
- if (element.type.identifier === SIGNATURE) {
180
- const { values, components, hint } = element.props;
181
- return /* @__PURE__ */ jsx(Renderer, { schema: dsl[componentIndex++], global: values, local: values, components: createComponentRegistry(components) }, componentIndex);
182
- }
183
- const children = extractChildren(element);
184
- if (children.length == 0) {
185
- return element;
186
- }
187
- return children.map((child, ind) => {
188
- return swapChildren(child);
189
- });
190
- }
191
- return swapChildren(schema);
192
- }
193
- var createGeneratedPage = (model) => {
194
- return async function GeneratedPage({
195
- context,
196
- schema,
197
- cached,
198
- onGenerate
199
- }) {
200
- if (cached) {
201
- return /* @__PURE__ */ jsx(Fragment, { children: hydrate(schema, cached) });
202
- }
203
- const contents = [];
204
- searchChildren(schema, contents);
205
- console.log("contents", contents);
206
- if (contents.length == 0) return schema;
207
- const llmInput = generateInput(contents, context);
208
- const { text: llmResponse } = await generateText({
209
- model,
210
- system: prompt_default,
211
- prompt: llmInput
212
- });
213
- const parsedResponse = parseResponse(llmResponse);
214
- if (onGenerate) {
215
- onGenerate(parsedResponse);
216
- }
217
- return /* @__PURE__ */ jsx(Fragment, { children: hydrate(schema, parsedResponse) });
218
- };
219
- };
220
-
221
- // src/index.ts
222
- var createSyntuxFactory = (config) => {
223
- return {
224
- GeneratedPage: createGeneratedPage(config.model),
225
- GeneratedContent,
226
- Renderer
227
- };
228
- };
229
-
230
- export { createSyntuxFactory };
231
- //# sourceMappingURL=index.mjs.map
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>
4
+ <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};
232
7
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/GeneratedContent.tsx","../src/Renderer.tsx","../src/util.ts","../src/prompt.md","../src/GeneratedPage.tsx","../src/index.ts"],"names":["jsx","Fragment"],"mappings":"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","file":"index.mjs","sourcesContent":["import { ComponentType } from 'react'\r\nimport { SyntuxComponent } from './types';\r\n\r\nexport interface GeneratedContentProps {\r\n values: any;\r\n components?: (SyntuxComponent<any> | string)[];\r\n hint?: string;\r\n}\r\n\r\n/**\r\n * Section of user interface for LLM to generate.\r\n * @param values The values (object, primitive, or array) to be displayed.\r\n * @param components List of allowed components that LLM can use.\r\n * @param hint Additional custom instructions for the LLM.\r\n */\r\nexport function GeneratedContent(props: GeneratedContentProps) {\r\n /**\r\n * This is an empty component.\r\n * It acts as a declarative slot that <GeneratedPage> recognizes\r\n * and replaces with a Renderer during hydration.\r\n */\r\n return <></>;\r\n}\r\nexport const SIGNATURE = Symbol('GeneratedContent');\r\nGeneratedContent.identifier = SIGNATURE;","import { ComponentType } from 'react'\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\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\nconst resolveProps = (global: any, local: any, props: any) => {\r\n if(!props) return;\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\nexport interface SchemaNode {\r\n type?: string;\r\n props?: Record<string, any>;\r\n children?: (SchemaNode | string)[];\r\n source?: string;\r\n template?: SchemaNode;\r\n $bind?: string;\r\n}\r\n\r\nexport interface RendererProps {\r\n schema: SchemaNode | string; // string occurs recursively\r\n global: any;\r\n local?: any;\r\n components: Record<string, ComponentType<any> | string>;\r\n}\r\n\r\nexport function Renderer({\r\n schema, global, local, components\r\n}: RendererProps) {\r\n if(typeof schema === \"string\") return <>{schema}</>;\r\n if(schema.$bind) {\r\n return <>{get(global, local, schema.$bind)}</>\r\n }\r\n\r\n if(schema.type === '__ForEach__' && schema.source && schema.template){\r\n const sourceArr = get(global, local, schema.source);\r\n if(!Array.isArray(sourceArr)) return null;\r\n\r\n return <>\r\n {sourceArr.map((item, index) => {\r\n return <Renderer\r\n key={index}\r\n schema={schema.template!}\r\n global={global}\r\n local={item}\r\n components={components}\r\n />\r\n })}\r\n </>\r\n }\r\n\r\n if(!schema.type) return null;\r\n\r\n const Component = components[schema.type] || schema.type;\r\n return <Component {...resolveProps(global, local, schema.props)}>\r\n {schema.children?.map((item, index) => {\r\n return <Renderer\r\n key={index}\r\n schema={item}\r\n global={global}\r\n local={local}\r\n components={components}\r\n />\r\n })}\r\n </Component>\r\n}\r\n","import { SchemaNode } from \"./Renderer\"\r\n\r\nconst endDelimiter = \"</UISchema>\"\r\nexport function parseResponse(llmResponse: string): SchemaNode[] {\r\n const split = llmResponse.split(/\\<UISchema index=\"\\d+\">/m).slice(1).map(e => e.trim())\r\n const contents = split.map(e => e.slice(0, -endDelimiter.length));\r\n const parsed = contents.map(e => JSON.parse(e))\r\n return parsed\r\n}","<system_persona>\r\nYou are the **UI Schema Generation Engine**, a specialized architect responsible for converting raw data structures into abstract, reusable React Interface Schemas (JSON-DSL).\r\n\r\nYour output is **NOT** code. Your output is a JSON-based Abstract Syntax Tree (AST) that describes the UI structure. The rendering engine will hydrate this schema with data later.\r\n</system_persona>\r\n\r\n<core_philosophy>\r\n1. **Separation of Concerns:** You define the *structure*. You do NOT hardcode *values*.\r\n2. **Strict Adherence:** You may ONLY use components explicitly listed in the `<AllowedComponents>` block of the input. If the block is missing or empty, revert to the <default_component_library>.\r\n3. **Semantic Structure:** Even though you are generating JSON, the resulting UI must be semantically correct (using proper hierarchy, semantic HTML tags, and logical grouping).\r\n4. **Reusability:** Your schema must be valid regardless of the specific values in the data. It must handle list lengths of 0 or 1000 gracefully using the Iterator Protocol.\r\n</core_philosophy>\r\n\r\n<dsl_syntax_rules>\r\nThe output must be a JSON object adhering to this strict recursive interface:\r\n\r\n### 1. The Standard Node\r\nUsed for HTML tags or Custom Components.\r\n```json\r\n{\r\n \"type\": \"div\" | \"h1\" | \"MyCustomComponent\",\r\n \"props\": { \"className\": \"...\", \"customProp\": \"...\" },\r\n \"children\": [] // Array of Nodes or Strings\r\n}\r\n```\r\n\r\n### 2. The Binding Protocol (`$bind`)\r\n\r\n**NEVER** output raw data values (e.g., \"John\", \"john@email.com\") in the schema.\r\nInstead, bind to a path.\r\n\r\n```json\r\n// BAD\r\n{ \"type\": \"span\", \"children\": [\"John\"] }\r\n\r\n// GOOD\r\n{ \"type\": \"span\", \"children\": [{ \"$bind\": \"user.firstName\" }] }\r\n```\r\n\r\nYou have access to exactly **two scopes** at any time:\r\n- **Global Scope (Root)**: Any path **WITHOUT** the `$item` prefix accesses the top-level global data object. This is available at any nesting depth.\r\n - *Example:* `\"$bind\": \"pageTitle\"` (Always looks at `data.pageTitle`)\r\n- **Local Scope (Current Item):** Any path **STARTING WITH** `$item` accesses the immediate object currently being iterated in a loop.\r\n - *Example:* `\"$bind\": \"$item.name\"` (Looks at `name` on the current loop item).\r\n - *Example:* `\"$bind\": \"$item\"` (Renders the current loop item, usually a string).\r\n\r\nIn special cases, you may need to reference a specific, fixed index in an array. To do that, use dot notation to access the index. For instance: `arr.1.property` accesses the `property` property of the 2nd item in `arr`. However, use this conservatively; prefer to use the Iterator Protocol unless it doesn't make sense (e.g., it's a static array).\r\n\r\n**CRITICAL:** Intermediate scopes are not accessible. Inside a nested loop, `$item` refers *only* to the innermost item.\r\n\r\n### 3. The Iterator Protocol (`__ForEach__`)\r\n\r\nIf the data value is an Array, you MUST use this node. Do not manually unroll lists.\r\n\r\n- **Root Arrays**: If the input value is the array itself, use \"source\": \"$\".\r\n- **Nested Arrays**: If the array is a property on the current item, use `\"source\": \"$item.path.to.array\"`.\r\n- **Global Arrays**: If the array is a property on the Global Root, use `\"source\": \"path.to.array\"`.\r\n\r\n```json\r\n{\r\n \"type\": \"__ForEach__\",\r\n \"source\": \"$\" | \"$item.path.to.array\" | \"path.to.array\",\r\n \"template\": {\r\n // This is the shape of a SINGLE item.\r\n // Inside here, use \"$item\" to refer to the current array element.\r\n \"type\": \"li\",\r\n \"children\": [{ \"$bind\": \"$item.name\" }]\r\n }\r\n}\r\n```\r\n\r\n</dsl_syntax_rules>\r\n\r\n<default_component_library>\r\nIf no specific allowed components are provided, you have access to this semantic HTML suite:\r\n\r\n * **Layout:** `div`, `section`, `article`, `main`, `aside`, `header`, `footer`\r\n * **Typography:** `h1`, `h2`, `h3`, `p`, `span`, `strong`, `em`, `blockquote`, `pre`, `code`\r\n * **Lists:** `ul`, `ol`, `li`\r\n * **Interaction:** `button`, `a` (use href prop), `details`, `summary`\r\n * **Form:** `input`, `label`, `textarea`, `select`, `option`\r\n * **Media:** `img`, `figure`, `figcaption`\r\n\r\n</default_component_library>\r\n\r\n<input_processing_rules>\r\nThe user will provide one or more `<GeneratedContent>` blocks.\r\n\r\n1. **Parse `AllowedComponents`:** A comma-separated list.\r\n * Lowercase = Native HTML tags.\r\n * Uppercase = Custom React Components.\r\n2. **Parse `ComponentContext`:** Defines the TypeScript interface for Custom Components.\r\n * *CRITICAL:* You must strictly adhere to the prop names and types defined here. Do not hallucinate props for custom components.\r\n * 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. Use this information in whatever way to improve your generation output. If you encounter a complex type in `props`, look at the input values and make your best guess at what value is the best fit.\r\n3. **Parse `UserContext`:** This is anything the developer believes is relevant for your task. It could describe a specific UI style or the correct way to use a custom component.\r\n * *CRITICAL*: To the best of your ability, respect the developer's wishes. If no UserContext is provided, you should accomplish the task as you see fit, with no constraints.\r\n * *CRITICAL*: This is only relevant to the current <GeneratedContent> block!\r\n3. **Parse `Value`:** The JSON data structure you are building the UI for.\r\n\r\nAdditionally, the user may provide a <PageContext> before all <GeneratedContent> blocks. Think of this as a global UserContext. This is meant to provide additional context and guiding when designing the UI, which you should try to adhere to. If none is provided, accomplish the task as you see fit, with no constraints.\r\n\r\n</input_processing_rules>\r\n\r\n<output_formatting>\r\nFor every `<GeneratedContent>` input block, you must generate exactly one `<UISchema>` output block.\r\nThe order must be preserved.\r\n\r\nInput:\r\n<PageContext></PageContext>\r\n<GeneratedContent> ... </GeneratedContent> (Section 1)\r\n<GeneratedContent> ... </GeneratedContent> (Section 2)\r\n\r\nOutput:\r\n<UISchema index=\"0\"> ...JSON... </UISchema>\r\n<UISchema index=\"1\"> ...JSON... </UISchema>\r\n</output_formatting>\r\n\r\n<examples>\r\n**Input:**\r\n<GeneratedContent>\r\n<AllowedComponents>div,span,Avatar</AllowedComponents>\r\n<ComponentContext>Avatar [props: { url: string }]</ComponentContext>\r\n<UserContext>grouped under one div</UserContext>\r\n<Value>{ authors: [{ name: \"J.K.\", img: \"...\" }, { name: \"Tolkien\", img: \"...\" }] }</Value>\r\n</GeneratedContent>\r\n\r\n**Correct Output:**\r\n<UISchema index=\"0\">\r\n{\r\n \"type\": \"div\",\r\n \"props\": {\r\n \"className\": \"author-list\"\r\n },\r\n \"children\": [\r\n {\r\n \"type\": \"__ForEach__\",\r\n \"source\": \"authors\",\r\n \"template\": {\r\n \"type\": \"div\",\r\n \"props\": {\r\n \"className\": \"author-card\"\r\n },\r\n \"children\": [\r\n {\r\n \"type\": \"Avatar\",\r\n \"props\": {\r\n \"url\": {\r\n \"$bind\": \"$item.img\"\r\n }\r\n }\r\n },\r\n {\r\n \"type\": \"span\",\r\n \"children\": [\r\n {\r\n \"$bind\": \"$item.name\"\r\n }\r\n ]\r\n }\r\n ]\r\n }\r\n }\r\n ]\r\n}\r\n</UISchema>\r\n</examples>\r\n\r\n<reasoning_requirements>\r\nBefore generating the JSON, briefly analyze the data structure to identify arrays (requiring `__ForEach__`) and custom component opportunities. Use the mental scratchpad if necessary, but keep the final output strictly within `<UISchema>` tags.\r\n</reasoning_requirements>\r\n\r\n<IMPORTANT>\r\nDo NOT output anything except the UISchema.\r\n</IMPORTANT>","import { isValidElement } from 'react';\r\nimport { GeneratedContentProps, SIGNATURE } from \"./GeneratedContent\";\r\nimport { Renderer, SchemaNode } from \"./Renderer\";\r\nimport { SyntuxComponent, SyntuxElement } from \"./types\";\r\nimport { parseResponse } from \"./util\";\r\nimport { generateText } from \"ai\";\r\nimport systemPrompt from './prompt.md';\r\n\r\nimport { type LanguageModel } from 'ai';\r\n\r\nexport interface GeneratedPageProps {\r\n context?: string;\r\n schema: SyntuxElement;\r\n onGenerate?: (result: SchemaNode[]) => void;\r\n cached?: (SchemaNode | string)[];\r\n}\r\n\r\nfunction extractChildren(element: any) {\r\n if (element?.props?.children) {\r\n let children = element.props.children;\r\n if (!Array.isArray(children)) children = [children];\r\n return children;\r\n }\r\n return [];\r\n}\r\n\r\nfunction searchChildren(element: SyntuxElement, acc: ContentSchema[]) {\r\n if(!isValidElement(element)) return;\r\n\r\n if(element.type.identifier === SIGNATURE){\r\n const { values, components, hint } = element.props as GeneratedContentProps;\r\n const realComponents: any = []\r\n\r\n if (components) {\r\n components.forEach((comp: string | SyntuxComponent) => {\r\n if (typeof comp === \"string\") {\r\n realComponents.push({\r\n llmName: comp\r\n })\r\n } else {\r\n realComponents.push({\r\n llmName: comp.llmName,\r\n llmContext: comp.llmContext,\r\n userContext: comp.userContext\r\n })\r\n }\r\n })\r\n }\r\n acc.push({\r\n values, components: realComponents, hint: hint || \"\"\r\n })\r\n } else {\r\n const children = extractChildren(element);\r\n children.forEach((child: SyntuxElement) => {\r\n searchChildren(child, acc)\r\n })\r\n }\r\n}\r\n\r\nfunction generateInput(content: ContentSchema[], context?: string) {\r\n let str = '';\r\n if (context) {\r\n str = `<PageContext>${context}</PageContext>\\n`;\r\n }\r\n\r\n content.forEach(schema => {\r\n const allowedComponents = schema.components.map(e => e.llmName);\r\n const componentContext: any = []\r\n schema.components.forEach(comp => {\r\n if (comp.llmContext) {\r\n let contextStr;\r\n if (comp.userContext) {\r\n contextStr = `${comp.llmName} [${comp.llmContext}, details: ${comp.userContext}]`\r\n } else {\r\n contextStr = `${comp.llmName} [${comp.llmContext}]`;\r\n }\r\n componentContext.push(contextStr)\r\n }\r\n })\r\n const userContext = schema.hint;\r\n const value = JSON.stringify(schema.values);\r\n\r\n str += `<GeneratedContent>\r\n <AllowedComponents>${allowedComponents.join(',')}</AllowedComponents>\r\n <ComponentContext>${componentContext.join(',')}</ComponentContext>\r\n <UserContext>${userContext}</UserContext>\r\n <Value>${value}</Value>\r\n</GeneratedContent>\\n`\r\n })\r\n\r\n return str;\r\n}\r\n\r\nfunction createComponentRegistry(components: (SyntuxComponent<any> | string)[] | undefined) {\r\n if (!components) return {};\r\n return components.reduce((acc: Record<string, SyntuxComponent<any> | string>, curr: SyntuxComponent<any> | string) => {\r\n if (typeof curr === \"string\") {\r\n acc[curr] = curr;\r\n } else {\r\n if (curr.llmName) {\r\n acc[curr.llmName] = curr;\r\n }\r\n }\r\n return acc;\r\n }, {})\r\n}\r\n\r\nfunction hydrate(schema: SyntuxElement, dsl: (SchemaNode | string)[]) {\r\n let componentIndex = 0;\r\n function swapChildren(element: SyntuxElement) {\r\n if(!isValidElement(element)) return element;\r\n\r\n if(element.type.identifier === SIGNATURE){\r\n const { values, components, hint } = element.props as GeneratedContentProps;\r\n return <Renderer key={componentIndex} schema={dsl[componentIndex++]} global={values} local={values} components={createComponentRegistry(components)} />;\r\n }\r\n\r\n\r\n const children = extractChildren(element);\r\n\r\n if(children.length == 0){\r\n return element;\r\n }\r\n\r\n return children.map((child: SyntuxElement, ind: number) => {\r\n return swapChildren(child);\r\n })\r\n }\r\n\r\n return swapChildren(schema);\r\n}\r\n\r\ninterface ContentSchema {\r\n values: any;\r\n components: { llmContext?: string, userContext?: string, llmName: string }[];\r\n hint: string;\r\n}\r\n\r\nexport const createGeneratedPage = (model: LanguageModel) => {\r\n /**\r\n * Container for content generation. Batches all <GeneratedContent> blocks into one LLM call.\r\n * \r\n * @param context Custom instructions for the LLM about the page, applicable to all components.\r\n * @param schema The structure of the page. Can be a mix of any type of component.\r\n * @param cached User provided React Interface Schema. Will skip schema generation if one is provided.\r\n * @param onGenerate Callback for receiving React Interface Schema after generation, to be used for caching. \r\n */\r\n return async function GeneratedPage({\r\n context, schema, cached, onGenerate\r\n }: GeneratedPageProps) {\r\n if(cached){\r\n return <>{hydrate(schema, cached)}</>\r\n }\r\n\r\n const contents: ContentSchema[] = [];\r\n searchChildren(schema, contents);\r\n console.log('contents', contents);\r\n \r\n if(contents.length == 0) return schema;\r\n\r\n const llmInput = generateInput(contents, context);\r\n\r\n const { text: llmResponse } = await generateText({\r\n model,\r\n system: systemPrompt,\r\n prompt: llmInput\r\n })\r\n const parsedResponse = parseResponse(llmResponse)\r\n\r\n if(onGenerate){\r\n onGenerate(parsedResponse)\r\n }\r\n \r\n return <>{hydrate(schema, parsedResponse)}</>\r\n }\r\n}","import { LanguageModel } from 'ai';\r\nimport { createGeneratedPage } from './GeneratedPage';\r\nimport { GeneratedContent } from './GeneratedContent';\r\nimport { Renderer } from './Renderer';\r\n\r\nexport { GeneratedContentProps } from './GeneratedContent';\r\nexport { GeneratedPageProps } from './GeneratedPage';\r\nexport {SchemaNode} from './Renderer';\r\n\r\nexport interface SyntuxFactoryConfig {\r\n model: LanguageModel;\r\n}\r\n\r\n\r\nexport const createSyntuxFactory = (config: SyntuxFactoryConfig) => {\r\n return {\r\n GeneratedPage: createGeneratedPage(config.model),\r\n GeneratedContent,\r\n Renderer\r\n }\r\n}"]}
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\nexport class ResponseParser {\r\n buffer = \"\";\r\n schema: UISchema = {\r\n childrenMap: {},\r\n componentMap: {},\r\n root: null\r\n }\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 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 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,CCzCO,IAAMC,EAAN,KAAqB,CACxB,OAAS,GACT,OAAmB,CACf,YAAa,CAAC,EACd,aAAc,CAAC,EACf,KAAM,IACV,EAEA,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,CAEA,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,CAEA,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"]}
@@ -0,0 +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":1163,"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":3047,"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":1373,"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.mjs":{"bytes":3494,"imports":[{"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":"url","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":4734},"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":10085},"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":572},"src/client/Renderer.tsx":{"bytesInOutput":1153},"src/ResponseParser.ts":{"bytesInOutput":473},"src/client.ts":{"bytesInOutput":0}},"bytes":2255},"dist/bin/cli.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":6062},"dist/bin/cli.mjs":{"imports":[{"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":"url","kind":"import-statement","external":true}],"exports":[],"entryPoint":"src/bin/cli.mjs","inputs":{"src/bin/cli.mjs":{"bytesInOutput":2132}},"bytes":2153}}}
@@ -0,0 +1,76 @@
1
+ import { JSX } from 'react';
2
+
3
+ import { createStreamableValue } from '@ai-sdk/rsc';
4
+ import { LanguageModel, streamText } from 'ai';
5
+
6
+ import { GeneratedClient, Renderer } from 'getsyntux/client';
7
+ import { ResponseParser, SyntuxComponent, UISchema, constructInput, generateComponentMap } from "getsyntux";
8
+
9
+ import spec from './spec';
10
+
11
+ export interface GeneratedContentProps {
12
+ value: any;
13
+ model: LanguageModel;
14
+ components?: (SyntuxComponent | string)[];
15
+ hint?: string;
16
+ placeholder?: JSX.Element;
17
+ cached?: string;
18
+ onGenerate?: (arg0: string) => void
19
+ }
20
+
21
+ /**
22
+ * Section of user interface for LLM to generate.
23
+ * @param values The values (object, primitive, or array) to be displayed.
24
+ * @param model The LanguageModel (as provided from AI SDK) to use. Must support streaming
25
+ * @param components List of allowed components that LLM can use.
26
+ * @param hint Additional custom instructions for the LLM.
27
+ * @param placeholder A placeholder to show while awaiting streaming (NOT during streaming)
28
+ * @param cached Schema returned from onGenerate, used for caching UI
29
+ * @param onGenerate Callback which accepts a string, to be passed to `cached` to reuse same UI
30
+ */
31
+ export async function GeneratedUI(props: GeneratedContentProps) {
32
+ const input = constructInput(props);
33
+
34
+ const { value, model, components, cached, onGenerate } = props;
35
+
36
+ const allowedComponents = generateComponentMap(components || []);
37
+
38
+ // prerender if cached
39
+ if(cached){
40
+ const parser = new ResponseParser();
41
+ parser.addDelta(cached);
42
+ parser.finish();
43
+
44
+ const schema: UISchema = parser.schema;
45
+
46
+ if(schema.root){
47
+ return <Renderer id={schema.root.id} componentMap={schema.componentMap} childrenMap={schema.childrenMap}
48
+ allowedComponents={allowedComponents} global={value} local={value} />
49
+ } else {
50
+ return <></>;
51
+ }
52
+ }
53
+
54
+ const stream = createStreamableValue('');
55
+
56
+ (async () => {
57
+ let total = "";
58
+
59
+ const { textStream } = await streamText({
60
+ model,
61
+ system: spec,
62
+ prompt: input
63
+ })
64
+
65
+ for await(const delta of textStream){
66
+ stream.update(delta);
67
+ total += delta;
68
+ }
69
+
70
+ stream.done();
71
+
72
+ if(onGenerate) onGenerate(total);
73
+ })()
74
+
75
+ return <GeneratedClient value={value} allowedComponents={allowedComponents} inputStream={stream.value} />
76
+ }
@@ -0,0 +1,78 @@
1
+ <system_persona>
2
+ You are the UI Stream Engine. You convert data into React UIs by emitting a linear stream of atomic component definitions.
3
+ </system_persona>
4
+
5
+ <core_philosophy>
6
+ - Linear Stream: Output flat, new-line delimited JSON. Build the UI one "brick" (node) at a time.
7
+ - Referential Integrity: Every node needs a unique id. Every node (except roots) points to a valid parentId created earlier. Roots must have parentId = null.
8
+ - Separation: Define structure only. Bind values dynamically; do not hardcode data.
9
+ </core_philosophy>
10
+
11
+ <output_protocol>
12
+ Output separate JSON objects, one per line. Each must adhere to this TypeScript interface:
13
+
14
+ type UINode = {
15
+ id: string; // A unique ID (e.g., "node_1", "header_main")
16
+ parentId: string | null; // The ID of the container this node lives inside.
17
+ type: string; // HTML tag ("div") or Component Name ("Card")
18
+ props?: Record<string, any>; // Attributes (className, variant, etc.) or props to a component
19
+ content?: string | { "$bind": string }; // Optional text content or data binding
20
+ }
21
+
22
+ Output visualy (depth first). Children will be added in the order to their parents which you output them.
23
+ Example: Output the Container, then the Header, then the Header's text, then the Footer.
24
+
25
+ To display a raw text node, set type = "TEXT".
26
+ </output_protocol>
27
+
28
+ <binding_rules>
29
+ Use "$bind" in `content` or `props` to link data.
30
+ - Global properties: "$bind": "path.to.prop"
31
+ - Loop Item: "$bind": "$item.path.prop" (current item in a loop)
32
+ Use "$bind": "$" to reference the global object itself, useful when the value itself is an array and you need to loop through it.
33
+ </binding_rules>
34
+
35
+ <iteration>
36
+ To render arrays, use the `__ForEach__` component.
37
+ To define a loop:
38
+ - Create node and set the `source`: { type: "__ForEach__", props: { source: "path.to.array" } }
39
+ - Any node referencing the loop ID of a __ForEach__ node becomes the template repeated for every item.
40
+
41
+ Example:
42
+ {"id":"loop_1", "parentId":"root", "type":"__ForEach__", "props":{"source":"authors"}}
43
+ {"id":"card_1", "parentId":"loop_1", "type":"div", "props":{"className":"card"}, "content": {"$bind": "$item.name"}}
44
+
45
+ In the example above, card_1 is the template that repeats for every author. It shows all authors.
46
+ </iteration>
47
+
48
+ <input_processing_rules>
49
+ 1. Parse Specs: Read `AllowedComponents` and `ComponentContext` (props definitions).
50
+ * `AllowedComponents` is a comma-separated list, lowercase for Native HTML tags, Uppercase for Custom React Components. If none are provided, you can use any HTML tag. If they are, only use them to the best of your ability.
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 understand of how to generate the UI.
52
+ 2. Parse Context: Read `UserContext` for specific design requests.
53
+ 3. Parse Data: Analyze `Value` to determine structure.
54
+ </input_processing_rules>
55
+
56
+ <output_formatting>
57
+ You must generate a list of UINodes, separated by a newline.
58
+
59
+ Input:
60
+ <AllowedComponents>...</AllowedComponents>
61
+ <ComponentContext>...</ComponentContext>
62
+ <UserContext>...</UserContext>
63
+ <Value>...</Value>
64
+
65
+ Output:
66
+ { ... }
67
+ ... more lines
68
+ </output_formatting>
69
+
70
+ <reasoning_requirements>
71
+ 1. Analyze `Value` for arrays (requiring `__ForEach__`) and structure.
72
+ 2. Select components from `AllowedComponents` that fit the data types.
73
+ 3. Begin streaming lines immediately.
74
+ </reasoning_requirements>
75
+
76
+ <IMPORTANT>
77
+ Do NOT output anything EXCEPT the list of JSON.
78
+ </IMPORTANT>
@@ -0,0 +1,80 @@
1
+ const spec = `<system_persona>
2
+ You are the UI Stream Engine. You convert data into React UIs by emitting a linear stream of atomic component definitions.
3
+ </system_persona>
4
+
5
+ <core_philosophy>
6
+ - Linear Stream: Output flat, new-line delimited JSON. Build the UI one "brick" (node) at a time.
7
+ - Referential Integrity: Every node needs a unique id. Every node (except roots) points to a valid parentId created earlier. Roots must have parentId = null.
8
+ - Separation: Define structure only. Bind values dynamically; do not hardcode data.
9
+ </core_philosophy>
10
+
11
+ <output_protocol>
12
+ Output separate JSON objects, one per line. Each must adhere to this TypeScript interface:
13
+
14
+ type UINode = {
15
+ id: string; // A unique ID (e.g., "node_1", "header_main")
16
+ parentId: string | null; // The ID of the container this node lives inside.
17
+ type: string; // HTML tag ("div") or Component Name ("Card")
18
+ props?: Record<string, any>; // Attributes (className, variant, etc.) or props to a component
19
+ content?: string | { "$bind": string }; // Optional text content or data binding
20
+ }
21
+
22
+ Output visualy (depth first). Children will be added in the order to their parents which you output them.
23
+ Example: Output the Container, then the Header, then the Header's text, then the Footer.
24
+
25
+ To display a raw text node, set type = "TEXT".
26
+ </output_protocol>
27
+
28
+ <binding_rules>
29
+ Use "$bind" in \`content\` or \`props\` to link data.
30
+ - Global properties: "$bind": "path.to.prop"
31
+ - Loop Item: "$bind": "$item.path.prop" (current item in a loop)
32
+ Use "$bind": "$" to reference the global object itself, useful when the value itself is an array and you need to loop through it.
33
+ </binding_rules>
34
+
35
+ <iteration>
36
+ To render arrays, use the \`__ForEach__\` component.
37
+ To define a loop:
38
+ - Create node and set the \`source\`: { type: "__ForEach__", props: { source: "path.to.array" } }
39
+ - Any node referencing the loop ID of a __ForEach__ node becomes the template repeated for every item.
40
+
41
+ Example:
42
+ {"id":"loop_1", "parentId":"root", "type":"__ForEach__", "props":{"source":"authors"}}
43
+ {"id":"card_1", "parentId":"loop_1", "type":"div", "props":{"className":"card"}, "content": {"$bind": "$item.name"}}
44
+
45
+ In the example above, card_1 is the template that repeats for every author. It shows all authors.
46
+ </iteration>
47
+
48
+ <input_processing_rules>
49
+ 1. Parse Specs: Read \`AllowedComponents\` and \`ComponentContext\` (props definitions).
50
+ * \`AllowedComponents\` is a comma-separated list, lowercase for Native HTML tags, Uppercase for Custom React Components. If none are provided, you can use any HTML tag. If they are, only use them to the best of your ability.
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 understand of how to generate the UI.
52
+ 2. Parse Context: Read \`UserContext\` for specific design requests.
53
+ 3. Parse Data: Analyze \`Value\` to determine structure.
54
+ </input_processing_rules>
55
+
56
+ <output_formatting>
57
+ You must generate a list of UINodes, separated by a newline.
58
+
59
+ Input:
60
+ <AllowedComponents>...</AllowedComponents>
61
+ <ComponentContext>...</ComponentContext>
62
+ <UserContext>...</UserContext>
63
+ <Value>...</Value>
64
+
65
+ Output:
66
+ { ... }
67
+ ... more lines
68
+ </output_formatting>
69
+
70
+ <reasoning_requirements>
71
+ 1. Analyze \`Value\` for arrays (requiring \`__ForEach__\`) and structure.
72
+ 2. Select components from \`AllowedComponents\` that fit the data types.
73
+ 3. Begin streaming lines immediately.
74
+ </reasoning_requirements>
75
+
76
+ <IMPORTANT>
77
+ Do NOT output anything EXCEPT the list of JSON.
78
+ </IMPORTANT>`;
79
+
80
+ export default spec;
@@ -0,0 +1,24 @@
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
+
24
+ export type { ComponentMap as C, SyntuxComponent as S, UISchema as U, SchemaNode as a, ChildrenMap as b };