@json-render/core 0.5.1 → 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.d.mts
CHANGED
|
@@ -1012,6 +1012,8 @@ interface ComponentDefinition<TProps extends ComponentSchema = ComponentSchema>
|
|
|
1012
1012
|
hasChildren?: boolean;
|
|
1013
1013
|
/** Description for AI generation */
|
|
1014
1014
|
description?: string;
|
|
1015
|
+
/** Example prop values used in prompt examples (auto-generated from Zod schema if omitted) */
|
|
1016
|
+
example?: Record<string, unknown>;
|
|
1015
1017
|
}
|
|
1016
1018
|
/**
|
|
1017
1019
|
* Catalog configuration
|
package/dist/index.d.ts
CHANGED
|
@@ -1012,6 +1012,8 @@ interface ComponentDefinition<TProps extends ComponentSchema = ComponentSchema>
|
|
|
1012
1012
|
hasChildren?: boolean;
|
|
1013
1013
|
/** Description for AI generation */
|
|
1014
1014
|
description?: string;
|
|
1015
|
+
/** Example prop values used in prompt examples (auto-generated from Zod schema if omitted) */
|
|
1016
|
+
example?: Record<string, unknown>;
|
|
1015
1017
|
}
|
|
1016
1018
|
/**
|
|
1017
1019
|
* Catalog configuration
|
package/dist/index.js
CHANGED
|
@@ -1244,27 +1244,72 @@ function generatePrompt(catalog, options) {
|
|
|
1244
1244
|
const lines = [];
|
|
1245
1245
|
lines.push(system);
|
|
1246
1246
|
lines.push("");
|
|
1247
|
-
lines.push("OUTPUT FORMAT:");
|
|
1247
|
+
lines.push("OUTPUT FORMAT (JSONL, RFC 6902 JSON Patch):");
|
|
1248
1248
|
lines.push(
|
|
1249
|
-
"Output JSONL (one JSON object per line)
|
|
1249
|
+
"Output JSONL (one JSON object per line) using RFC 6902 JSON Patch operations to build a UI tree."
|
|
1250
1250
|
);
|
|
1251
1251
|
lines.push(
|
|
1252
|
-
"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."
|
|
1252
|
+
"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."
|
|
1253
1253
|
);
|
|
1254
1254
|
lines.push("");
|
|
1255
1255
|
lines.push("Example output (each line is a separate JSON object):");
|
|
1256
1256
|
lines.push("");
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
{
|
|
1264
|
-
|
|
1265
|
-
|
|
1257
|
+
const allComponents = catalog.data.components;
|
|
1258
|
+
const cn = catalog.componentNames;
|
|
1259
|
+
const comp1 = cn[0] || "Component";
|
|
1260
|
+
const comp2 = cn.length > 1 ? cn[1] : comp1;
|
|
1261
|
+
const comp1Def = allComponents?.[comp1];
|
|
1262
|
+
const comp2Def = allComponents?.[comp2];
|
|
1263
|
+
const comp1Props = comp1Def ? getExampleProps(comp1Def) : {};
|
|
1264
|
+
const comp2Props = comp2Def ? getExampleProps(comp2Def) : {};
|
|
1265
|
+
const dynamicPropName = comp2Def?.props ? findFirstStringProp(comp2Def.props) : null;
|
|
1266
|
+
const dynamicProps = dynamicPropName ? { ...comp2Props, [dynamicPropName]: { $path: "$item/title" } } : comp2Props;
|
|
1267
|
+
const exampleOutput = [
|
|
1268
|
+
JSON.stringify({ op: "add", path: "/root", value: "main" }),
|
|
1269
|
+
JSON.stringify({
|
|
1270
|
+
op: "add",
|
|
1271
|
+
path: "/elements/main",
|
|
1272
|
+
value: {
|
|
1273
|
+
type: comp1,
|
|
1274
|
+
props: comp1Props,
|
|
1275
|
+
children: ["child-1", "list"]
|
|
1276
|
+
}
|
|
1277
|
+
}),
|
|
1278
|
+
JSON.stringify({
|
|
1279
|
+
op: "add",
|
|
1280
|
+
path: "/elements/child-1",
|
|
1281
|
+
value: { type: comp2, props: comp2Props, children: [] }
|
|
1282
|
+
}),
|
|
1283
|
+
JSON.stringify({
|
|
1284
|
+
op: "add",
|
|
1285
|
+
path: "/elements/list",
|
|
1286
|
+
value: {
|
|
1287
|
+
type: comp1,
|
|
1288
|
+
props: comp1Props,
|
|
1289
|
+
repeat: { path: "/items", key: "id" },
|
|
1290
|
+
children: ["item"]
|
|
1291
|
+
}
|
|
1292
|
+
}),
|
|
1293
|
+
JSON.stringify({
|
|
1294
|
+
op: "add",
|
|
1295
|
+
path: "/elements/item",
|
|
1296
|
+
value: { type: comp2, props: dynamicProps, children: [] }
|
|
1297
|
+
}),
|
|
1298
|
+
JSON.stringify({ op: "add", path: "/state/items", value: [] }),
|
|
1299
|
+
JSON.stringify({
|
|
1300
|
+
op: "add",
|
|
1301
|
+
path: "/state/items/0",
|
|
1302
|
+
value: { id: "1", title: "First Item" }
|
|
1303
|
+
}),
|
|
1304
|
+
JSON.stringify({
|
|
1305
|
+
op: "add",
|
|
1306
|
+
path: "/state/items/1",
|
|
1307
|
+
value: { id: "2", title: "Second Item" }
|
|
1308
|
+
})
|
|
1309
|
+
].join("\n");
|
|
1310
|
+
lines.push(`${exampleOutput}
|
|
1266
1311
|
|
|
1267
|
-
Note: state patches appear right after the elements that use them, so the UI fills in as it streams.`);
|
|
1312
|
+
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.`);
|
|
1268
1313
|
lines.push("");
|
|
1269
1314
|
lines.push("INITIAL STATE:");
|
|
1270
1315
|
lines.push(
|
|
@@ -1303,7 +1348,7 @@ Note: state patches appear right after the elements that use them, so the UI fil
|
|
|
1303
1348
|
'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.'
|
|
1304
1349
|
);
|
|
1305
1350
|
lines.push(
|
|
1306
|
-
|
|
1351
|
+
`Example: ${JSON.stringify({ type: comp1, props: comp1Props, repeat: { path: "/todos", key: "id" }, children: ["todo-item"] })}`
|
|
1307
1352
|
);
|
|
1308
1353
|
lines.push(
|
|
1309
1354
|
'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.'
|
|
@@ -1339,7 +1384,7 @@ Note: state patches appear right after the elements that use them, so the UI fil
|
|
|
1339
1384
|
'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.'
|
|
1340
1385
|
);
|
|
1341
1386
|
lines.push("");
|
|
1342
|
-
const components =
|
|
1387
|
+
const components = allComponents;
|
|
1343
1388
|
if (components) {
|
|
1344
1389
|
lines.push(`AVAILABLE COMPONENTS (${catalog.componentNames.length}):`);
|
|
1345
1390
|
lines.push("");
|
|
@@ -1372,7 +1417,7 @@ Note: state patches appear right after the elements that use them, so the UI fil
|
|
|
1372
1417
|
lines.push("");
|
|
1373
1418
|
lines.push("Example:");
|
|
1374
1419
|
lines.push(
|
|
1375
|
-
|
|
1420
|
+
` ${JSON.stringify({ type: comp1, props: comp1Props, on: { press: { action: "setState", params: { path: "/saved", value: true } } }, children: [] })}`
|
|
1376
1421
|
);
|
|
1377
1422
|
lines.push("");
|
|
1378
1423
|
lines.push(
|
|
@@ -1387,7 +1432,7 @@ Note: state patches appear right after the elements that use them, so the UI fil
|
|
|
1387
1432
|
"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."
|
|
1388
1433
|
);
|
|
1389
1434
|
lines.push(
|
|
1390
|
-
|
|
1435
|
+
`Correct: ${JSON.stringify({ type: comp1, props: comp1Props, visible: { eq: [{ path: "/tab" }, "home"] }, children: ["..."] })}`
|
|
1391
1436
|
);
|
|
1392
1437
|
lines.push(
|
|
1393
1438
|
'- `{ "eq": [{ "path": "/statePath" }, "value"] }` - visible when state at path equals value'
|
|
@@ -1402,10 +1447,10 @@ Note: state patches appear right after the elements that use them, so the UI fil
|
|
|
1402
1447
|
lines.push("- `true` / `false` - always visible/hidden");
|
|
1403
1448
|
lines.push("");
|
|
1404
1449
|
lines.push(
|
|
1405
|
-
"Use
|
|
1450
|
+
"Use a component with on.press bound to setState to update state and drive visibility."
|
|
1406
1451
|
);
|
|
1407
1452
|
lines.push(
|
|
1408
|
-
|
|
1453
|
+
`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.`
|
|
1409
1454
|
);
|
|
1410
1455
|
lines.push("");
|
|
1411
1456
|
lines.push("DYNAMIC PROPS:");
|
|
@@ -1451,6 +1496,101 @@ Note: state patches appear right after the elements that use them, so the UI fil
|
|
|
1451
1496
|
});
|
|
1452
1497
|
return lines.join("\n");
|
|
1453
1498
|
}
|
|
1499
|
+
function getExampleProps(def) {
|
|
1500
|
+
if (def.example && Object.keys(def.example).length > 0) {
|
|
1501
|
+
return def.example;
|
|
1502
|
+
}
|
|
1503
|
+
if (def.props) {
|
|
1504
|
+
return generateExamplePropsFromZod(def.props);
|
|
1505
|
+
}
|
|
1506
|
+
return {};
|
|
1507
|
+
}
|
|
1508
|
+
function generateExamplePropsFromZod(schema) {
|
|
1509
|
+
if (!schema || !schema._def) return {};
|
|
1510
|
+
const def = schema._def;
|
|
1511
|
+
const typeName = getZodTypeName(schema);
|
|
1512
|
+
if (typeName !== "ZodObject" && typeName !== "object") return {};
|
|
1513
|
+
const shape = typeof def.shape === "function" ? def.shape() : def.shape;
|
|
1514
|
+
if (!shape) return {};
|
|
1515
|
+
const result = {};
|
|
1516
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
1517
|
+
const innerTypeName = getZodTypeName(value);
|
|
1518
|
+
if (innerTypeName === "ZodOptional" || innerTypeName === "optional" || innerTypeName === "ZodNullable" || innerTypeName === "nullable") {
|
|
1519
|
+
continue;
|
|
1520
|
+
}
|
|
1521
|
+
result[key] = generateExampleValue(value);
|
|
1522
|
+
}
|
|
1523
|
+
return result;
|
|
1524
|
+
}
|
|
1525
|
+
function generateExampleValue(schema) {
|
|
1526
|
+
if (!schema || !schema._def) return "...";
|
|
1527
|
+
const def = schema._def;
|
|
1528
|
+
const typeName = getZodTypeName(schema);
|
|
1529
|
+
switch (typeName) {
|
|
1530
|
+
case "ZodString":
|
|
1531
|
+
case "string":
|
|
1532
|
+
return "example";
|
|
1533
|
+
case "ZodNumber":
|
|
1534
|
+
case "number":
|
|
1535
|
+
return 0;
|
|
1536
|
+
case "ZodBoolean":
|
|
1537
|
+
case "boolean":
|
|
1538
|
+
return true;
|
|
1539
|
+
case "ZodLiteral":
|
|
1540
|
+
case "literal":
|
|
1541
|
+
return def.value;
|
|
1542
|
+
case "ZodEnum":
|
|
1543
|
+
case "enum": {
|
|
1544
|
+
if (Array.isArray(def.values) && def.values.length > 0)
|
|
1545
|
+
return def.values[0];
|
|
1546
|
+
if (def.entries && typeof def.entries === "object") {
|
|
1547
|
+
const values = Object.values(def.entries);
|
|
1548
|
+
return values.length > 0 ? values[0] : "example";
|
|
1549
|
+
}
|
|
1550
|
+
return "example";
|
|
1551
|
+
}
|
|
1552
|
+
case "ZodOptional":
|
|
1553
|
+
case "optional":
|
|
1554
|
+
case "ZodNullable":
|
|
1555
|
+
case "nullable":
|
|
1556
|
+
case "ZodDefault":
|
|
1557
|
+
case "default": {
|
|
1558
|
+
const inner = def.innerType ?? def.wrapped;
|
|
1559
|
+
return inner ? generateExampleValue(inner) : null;
|
|
1560
|
+
}
|
|
1561
|
+
case "ZodArray":
|
|
1562
|
+
case "array":
|
|
1563
|
+
return [];
|
|
1564
|
+
case "ZodObject":
|
|
1565
|
+
case "object":
|
|
1566
|
+
return generateExamplePropsFromZod(schema);
|
|
1567
|
+
case "ZodUnion":
|
|
1568
|
+
case "union": {
|
|
1569
|
+
const options = def.options;
|
|
1570
|
+
return options && options.length > 0 ? generateExampleValue(options[0]) : "...";
|
|
1571
|
+
}
|
|
1572
|
+
default:
|
|
1573
|
+
return "...";
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
function findFirstStringProp(schema) {
|
|
1577
|
+
if (!schema || !schema._def) return null;
|
|
1578
|
+
const def = schema._def;
|
|
1579
|
+
const typeName = getZodTypeName(schema);
|
|
1580
|
+
if (typeName !== "ZodObject" && typeName !== "object") return null;
|
|
1581
|
+
const shape = typeof def.shape === "function" ? def.shape() : def.shape;
|
|
1582
|
+
if (!shape) return null;
|
|
1583
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
1584
|
+
const innerTypeName = getZodTypeName(value);
|
|
1585
|
+
if (innerTypeName === "ZodOptional" || innerTypeName === "optional" || innerTypeName === "ZodNullable" || innerTypeName === "nullable") {
|
|
1586
|
+
continue;
|
|
1587
|
+
}
|
|
1588
|
+
if (innerTypeName === "ZodString" || innerTypeName === "string") {
|
|
1589
|
+
return key;
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
return null;
|
|
1593
|
+
}
|
|
1454
1594
|
function getZodTypeName(schema) {
|
|
1455
1595
|
if (!schema || !schema._def) return "";
|
|
1456
1596
|
const def = schema._def;
|