@json-render/core 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +159 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +159 -19
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1173,27 +1173,72 @@ function generatePrompt(catalog, options) {
|
|
|
1173
1173
|
const lines = [];
|
|
1174
1174
|
lines.push(system);
|
|
1175
1175
|
lines.push("");
|
|
1176
|
-
lines.push("OUTPUT FORMAT:");
|
|
1176
|
+
lines.push("OUTPUT FORMAT (JSONL, RFC 6902 JSON Patch):");
|
|
1177
1177
|
lines.push(
|
|
1178
|
-
"Output JSONL (one JSON object per line)
|
|
1178
|
+
"Output JSONL (one JSON object per line) using RFC 6902 JSON Patch operations to build a UI tree."
|
|
1179
1179
|
);
|
|
1180
1180
|
lines.push(
|
|
1181
|
-
"Each line is a JSON patch operation. Start with /root, then stream /elements and /state patches interleaved so the UI fills in progressively as it streams."
|
|
1181
|
+
"Each line is a JSON patch operation (add, remove, replace). Start with /root, then stream /elements and /state patches interleaved so the UI fills in progressively as it streams."
|
|
1182
1182
|
);
|
|
1183
1183
|
lines.push("");
|
|
1184
1184
|
lines.push("Example output (each line is a separate JSON object):");
|
|
1185
1185
|
lines.push("");
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
{
|
|
1193
|
-
|
|
1194
|
-
|
|
1186
|
+
const allComponents = catalog.data.components;
|
|
1187
|
+
const cn = catalog.componentNames;
|
|
1188
|
+
const comp1 = cn[0] || "Component";
|
|
1189
|
+
const comp2 = cn.length > 1 ? cn[1] : comp1;
|
|
1190
|
+
const comp1Def = allComponents?.[comp1];
|
|
1191
|
+
const comp2Def = allComponents?.[comp2];
|
|
1192
|
+
const comp1Props = comp1Def ? getExampleProps(comp1Def) : {};
|
|
1193
|
+
const comp2Props = comp2Def ? getExampleProps(comp2Def) : {};
|
|
1194
|
+
const dynamicPropName = comp2Def?.props ? findFirstStringProp(comp2Def.props) : null;
|
|
1195
|
+
const dynamicProps = dynamicPropName ? { ...comp2Props, [dynamicPropName]: { $path: "$item/title" } } : comp2Props;
|
|
1196
|
+
const exampleOutput = [
|
|
1197
|
+
JSON.stringify({ op: "add", path: "/root", value: "main" }),
|
|
1198
|
+
JSON.stringify({
|
|
1199
|
+
op: "add",
|
|
1200
|
+
path: "/elements/main",
|
|
1201
|
+
value: {
|
|
1202
|
+
type: comp1,
|
|
1203
|
+
props: comp1Props,
|
|
1204
|
+
children: ["child-1", "list"]
|
|
1205
|
+
}
|
|
1206
|
+
}),
|
|
1207
|
+
JSON.stringify({
|
|
1208
|
+
op: "add",
|
|
1209
|
+
path: "/elements/child-1",
|
|
1210
|
+
value: { type: comp2, props: comp2Props, children: [] }
|
|
1211
|
+
}),
|
|
1212
|
+
JSON.stringify({
|
|
1213
|
+
op: "add",
|
|
1214
|
+
path: "/elements/list",
|
|
1215
|
+
value: {
|
|
1216
|
+
type: comp1,
|
|
1217
|
+
props: comp1Props,
|
|
1218
|
+
repeat: { path: "/items", key: "id" },
|
|
1219
|
+
children: ["item"]
|
|
1220
|
+
}
|
|
1221
|
+
}),
|
|
1222
|
+
JSON.stringify({
|
|
1223
|
+
op: "add",
|
|
1224
|
+
path: "/elements/item",
|
|
1225
|
+
value: { type: comp2, props: dynamicProps, children: [] }
|
|
1226
|
+
}),
|
|
1227
|
+
JSON.stringify({ op: "add", path: "/state/items", value: [] }),
|
|
1228
|
+
JSON.stringify({
|
|
1229
|
+
op: "add",
|
|
1230
|
+
path: "/state/items/0",
|
|
1231
|
+
value: { id: "1", title: "First Item" }
|
|
1232
|
+
}),
|
|
1233
|
+
JSON.stringify({
|
|
1234
|
+
op: "add",
|
|
1235
|
+
path: "/state/items/1",
|
|
1236
|
+
value: { id: "2", title: "Second Item" }
|
|
1237
|
+
})
|
|
1238
|
+
].join("\n");
|
|
1239
|
+
lines.push(`${exampleOutput}
|
|
1195
1240
|
|
|
1196
|
-
Note: state patches appear right after the elements that use them, so the UI fills in as it streams.`);
|
|
1241
|
+
Note: state patches appear right after the elements that use them, so the UI fills in as it streams. ONLY use component types from the AVAILABLE COMPONENTS list below.`);
|
|
1197
1242
|
lines.push("");
|
|
1198
1243
|
lines.push("INITIAL STATE:");
|
|
1199
1244
|
lines.push(
|
|
@@ -1232,7 +1277,7 @@ Note: state patches appear right after the elements that use them, so the UI fil
|
|
|
1232
1277
|
'The element itself renders once (as the container), and its children are expanded once per array item. "path" is the state array path. "key" is an optional field name on each item for stable React keys.'
|
|
1233
1278
|
);
|
|
1234
1279
|
lines.push(
|
|
1235
|
-
|
|
1280
|
+
`Example: ${JSON.stringify({ type: comp1, props: comp1Props, repeat: { path: "/todos", key: "id" }, children: ["todo-item"] })}`
|
|
1236
1281
|
);
|
|
1237
1282
|
lines.push(
|
|
1238
1283
|
'Inside children of a repeated element, use "$item/field" for per-item paths: statePath:"$item/completed", { "$path": "$item/title" }. Use "$index" for the current array index.'
|
|
@@ -1268,7 +1313,7 @@ Note: state patches appear right after the elements that use them, so the UI fil
|
|
|
1268
1313
|
'IMPORTANT: State paths use RFC 6901 JSON Pointer syntax (e.g. "/todos/0/title"). Do NOT use JavaScript-style dot notation (e.g. "/todos.length" is WRONG). To generate unique IDs for new items, use "$id" instead of trying to read array length.'
|
|
1269
1314
|
);
|
|
1270
1315
|
lines.push("");
|
|
1271
|
-
const components =
|
|
1316
|
+
const components = allComponents;
|
|
1272
1317
|
if (components) {
|
|
1273
1318
|
lines.push(`AVAILABLE COMPONENTS (${catalog.componentNames.length}):`);
|
|
1274
1319
|
lines.push("");
|
|
@@ -1301,7 +1346,7 @@ Note: state patches appear right after the elements that use them, so the UI fil
|
|
|
1301
1346
|
lines.push("");
|
|
1302
1347
|
lines.push("Example:");
|
|
1303
1348
|
lines.push(
|
|
1304
|
-
|
|
1349
|
+
` ${JSON.stringify({ type: comp1, props: comp1Props, on: { press: { action: "setState", params: { path: "/saved", value: true } } }, children: [] })}`
|
|
1305
1350
|
);
|
|
1306
1351
|
lines.push("");
|
|
1307
1352
|
lines.push(
|
|
@@ -1316,7 +1361,7 @@ Note: state patches appear right after the elements that use them, so the UI fil
|
|
|
1316
1361
|
"Elements can have an optional `visible` field to conditionally show/hide based on data state. IMPORTANT: `visible` is a top-level field on the element object (sibling of type/props/children), NOT inside props."
|
|
1317
1362
|
);
|
|
1318
1363
|
lines.push(
|
|
1319
|
-
|
|
1364
|
+
`Correct: ${JSON.stringify({ type: comp1, props: comp1Props, visible: { eq: [{ path: "/tab" }, "home"] }, children: ["..."] })}`
|
|
1320
1365
|
);
|
|
1321
1366
|
lines.push(
|
|
1322
1367
|
'- `{ "eq": [{ "path": "/statePath" }, "value"] }` - visible when state at path equals value'
|
|
@@ -1331,10 +1376,10 @@ Note: state patches appear right after the elements that use them, so the UI fil
|
|
|
1331
1376
|
lines.push("- `true` / `false` - always visible/hidden");
|
|
1332
1377
|
lines.push("");
|
|
1333
1378
|
lines.push(
|
|
1334
|
-
"Use
|
|
1379
|
+
"Use a component with on.press bound to setState to update state and drive visibility."
|
|
1335
1380
|
);
|
|
1336
1381
|
lines.push(
|
|
1337
|
-
|
|
1382
|
+
`Example: A ${comp1} with on: { "press": { "action": "setState", "params": { "path": "/activeTab", "value": "home" } } } sets state, then a container with visible: { "eq": [{ "path": "/activeTab" }, "home"] } shows only when that tab is active.`
|
|
1338
1383
|
);
|
|
1339
1384
|
lines.push("");
|
|
1340
1385
|
lines.push("DYNAMIC PROPS:");
|
|
@@ -1380,6 +1425,101 @@ Note: state patches appear right after the elements that use them, so the UI fil
|
|
|
1380
1425
|
});
|
|
1381
1426
|
return lines.join("\n");
|
|
1382
1427
|
}
|
|
1428
|
+
function getExampleProps(def) {
|
|
1429
|
+
if (def.example && Object.keys(def.example).length > 0) {
|
|
1430
|
+
return def.example;
|
|
1431
|
+
}
|
|
1432
|
+
if (def.props) {
|
|
1433
|
+
return generateExamplePropsFromZod(def.props);
|
|
1434
|
+
}
|
|
1435
|
+
return {};
|
|
1436
|
+
}
|
|
1437
|
+
function generateExamplePropsFromZod(schema) {
|
|
1438
|
+
if (!schema || !schema._def) return {};
|
|
1439
|
+
const def = schema._def;
|
|
1440
|
+
const typeName = getZodTypeName(schema);
|
|
1441
|
+
if (typeName !== "ZodObject" && typeName !== "object") return {};
|
|
1442
|
+
const shape = typeof def.shape === "function" ? def.shape() : def.shape;
|
|
1443
|
+
if (!shape) return {};
|
|
1444
|
+
const result = {};
|
|
1445
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
1446
|
+
const innerTypeName = getZodTypeName(value);
|
|
1447
|
+
if (innerTypeName === "ZodOptional" || innerTypeName === "optional" || innerTypeName === "ZodNullable" || innerTypeName === "nullable") {
|
|
1448
|
+
continue;
|
|
1449
|
+
}
|
|
1450
|
+
result[key] = generateExampleValue(value);
|
|
1451
|
+
}
|
|
1452
|
+
return result;
|
|
1453
|
+
}
|
|
1454
|
+
function generateExampleValue(schema) {
|
|
1455
|
+
if (!schema || !schema._def) return "...";
|
|
1456
|
+
const def = schema._def;
|
|
1457
|
+
const typeName = getZodTypeName(schema);
|
|
1458
|
+
switch (typeName) {
|
|
1459
|
+
case "ZodString":
|
|
1460
|
+
case "string":
|
|
1461
|
+
return "example";
|
|
1462
|
+
case "ZodNumber":
|
|
1463
|
+
case "number":
|
|
1464
|
+
return 0;
|
|
1465
|
+
case "ZodBoolean":
|
|
1466
|
+
case "boolean":
|
|
1467
|
+
return true;
|
|
1468
|
+
case "ZodLiteral":
|
|
1469
|
+
case "literal":
|
|
1470
|
+
return def.value;
|
|
1471
|
+
case "ZodEnum":
|
|
1472
|
+
case "enum": {
|
|
1473
|
+
if (Array.isArray(def.values) && def.values.length > 0)
|
|
1474
|
+
return def.values[0];
|
|
1475
|
+
if (def.entries && typeof def.entries === "object") {
|
|
1476
|
+
const values = Object.values(def.entries);
|
|
1477
|
+
return values.length > 0 ? values[0] : "example";
|
|
1478
|
+
}
|
|
1479
|
+
return "example";
|
|
1480
|
+
}
|
|
1481
|
+
case "ZodOptional":
|
|
1482
|
+
case "optional":
|
|
1483
|
+
case "ZodNullable":
|
|
1484
|
+
case "nullable":
|
|
1485
|
+
case "ZodDefault":
|
|
1486
|
+
case "default": {
|
|
1487
|
+
const inner = def.innerType ?? def.wrapped;
|
|
1488
|
+
return inner ? generateExampleValue(inner) : null;
|
|
1489
|
+
}
|
|
1490
|
+
case "ZodArray":
|
|
1491
|
+
case "array":
|
|
1492
|
+
return [];
|
|
1493
|
+
case "ZodObject":
|
|
1494
|
+
case "object":
|
|
1495
|
+
return generateExamplePropsFromZod(schema);
|
|
1496
|
+
case "ZodUnion":
|
|
1497
|
+
case "union": {
|
|
1498
|
+
const options = def.options;
|
|
1499
|
+
return options && options.length > 0 ? generateExampleValue(options[0]) : "...";
|
|
1500
|
+
}
|
|
1501
|
+
default:
|
|
1502
|
+
return "...";
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
function findFirstStringProp(schema) {
|
|
1506
|
+
if (!schema || !schema._def) return null;
|
|
1507
|
+
const def = schema._def;
|
|
1508
|
+
const typeName = getZodTypeName(schema);
|
|
1509
|
+
if (typeName !== "ZodObject" && typeName !== "object") return null;
|
|
1510
|
+
const shape = typeof def.shape === "function" ? def.shape() : def.shape;
|
|
1511
|
+
if (!shape) return null;
|
|
1512
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
1513
|
+
const innerTypeName = getZodTypeName(value);
|
|
1514
|
+
if (innerTypeName === "ZodOptional" || innerTypeName === "optional" || innerTypeName === "ZodNullable" || innerTypeName === "nullable") {
|
|
1515
|
+
continue;
|
|
1516
|
+
}
|
|
1517
|
+
if (innerTypeName === "ZodString" || innerTypeName === "string") {
|
|
1518
|
+
return key;
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
return null;
|
|
1522
|
+
}
|
|
1383
1523
|
function getZodTypeName(schema) {
|
|
1384
1524
|
if (!schema || !schema._def) return "";
|
|
1385
1525
|
const def = schema._def;
|