@json-render/react-native 0.4.3
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/LICENSE +201 -0
- package/README.md +225 -0
- package/dist/catalog.d.mts +466 -0
- package/dist/catalog.d.ts +466 -0
- package/dist/catalog.js +406 -0
- package/dist/catalog.js.map +1 -0
- package/dist/catalog.mjs +9 -0
- package/dist/catalog.mjs.map +1 -0
- package/dist/chunk-MP3Y6GEF.mjs +381 -0
- package/dist/chunk-MP3Y6GEF.mjs.map +1 -0
- package/dist/chunk-VBNJFRLI.mjs +81 -0
- package/dist/chunk-VBNJFRLI.mjs.map +1 -0
- package/dist/index.d.mts +591 -0
- package/dist/index.d.ts +591 -0
- package/dist/index.js +2682 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2232 -0
- package/dist/index.mjs.map +1 -0
- package/dist/schema.d.mts +98 -0
- package/dist/schema.d.ts +98 -0
- package/dist/schema.js +106 -0
- package/dist/schema.js.map +1 -0
- package/dist/schema.mjs +9 -0
- package/dist/schema.mjs.map +1 -0
- package/package.json +74 -0
package/dist/schema.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/schema.ts
|
|
21
|
+
var schema_exports = {};
|
|
22
|
+
__export(schema_exports, {
|
|
23
|
+
elementTreeSchema: () => elementTreeSchema,
|
|
24
|
+
schema: () => schema
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(schema_exports);
|
|
27
|
+
var import_core = require("@json-render/core");
|
|
28
|
+
var schema = (0, import_core.defineSchema)(
|
|
29
|
+
(s) => ({
|
|
30
|
+
// What the AI-generated SPEC looks like
|
|
31
|
+
spec: s.object({
|
|
32
|
+
/** Root element key */
|
|
33
|
+
root: s.string(),
|
|
34
|
+
/** Flat map of elements by key */
|
|
35
|
+
elements: s.record(
|
|
36
|
+
s.object({
|
|
37
|
+
/** Component type from catalog */
|
|
38
|
+
type: s.ref("catalog.components"),
|
|
39
|
+
/** Component props */
|
|
40
|
+
props: s.propsOf("catalog.components"),
|
|
41
|
+
/** Child element keys (flat reference) */
|
|
42
|
+
children: s.array(s.string()),
|
|
43
|
+
/** Visibility condition */
|
|
44
|
+
visible: s.any()
|
|
45
|
+
})
|
|
46
|
+
)
|
|
47
|
+
}),
|
|
48
|
+
// What the CATALOG must provide
|
|
49
|
+
catalog: s.object({
|
|
50
|
+
/** Component definitions */
|
|
51
|
+
components: s.map({
|
|
52
|
+
/** Zod schema for component props */
|
|
53
|
+
props: s.zod(),
|
|
54
|
+
/** Slots for this component. Use ['default'] for children, or named slots like ['header', 'footer'] */
|
|
55
|
+
slots: s.array(s.string()),
|
|
56
|
+
/** Description for AI generation hints */
|
|
57
|
+
description: s.string()
|
|
58
|
+
}),
|
|
59
|
+
/** Action definitions (optional) */
|
|
60
|
+
actions: s.map({
|
|
61
|
+
/** Zod schema for action params */
|
|
62
|
+
params: s.zod(),
|
|
63
|
+
/** Description for AI generation hints */
|
|
64
|
+
description: s.string()
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
}),
|
|
68
|
+
{
|
|
69
|
+
defaultRules: [
|
|
70
|
+
// Layout patterns
|
|
71
|
+
"FIXED BOTTOM BAR PATTERN: When building a screen with a fixed header and/or fixed bottom tab bar, the outermost Column must have flex:1 so it fills the SafeArea. The scrollable content area must also have flex:1. Structure: SafeArea > Column(flex:1, gap:0) > [header, Container(flex:1) > [ScrollContainer(...)], bottom-tabs]. Both the outer Column AND the content Container need flex:1.",
|
|
72
|
+
"NEVER place a bottom tab bar or fixed footer inside a ScrollContainer. It must be a sibling AFTER the flex:1 container that holds the ScrollContainer.",
|
|
73
|
+
// Element integrity
|
|
74
|
+
"CRITICAL INTEGRITY CHECK: Before outputting ANY element that references children, you MUST have already output (or will output) each child as its own element. If an element has children: ['a', 'b'], then elements 'a' and 'b' MUST exist. A missing child element causes that entire branch of the UI to be invisible.",
|
|
75
|
+
"SELF-CHECK: After generating all elements, mentally walk the tree from root. Every key in every children array must resolve to a defined element. If you find a gap, output the missing element immediately.",
|
|
76
|
+
'When building repeating content backed by a state array (e.g. todos, posts, cart items), use the "repeat" field on the container element. Example: { "type": "Column", "props": { "gap": 8 }, "repeat": { "path": "/todos", "key": "id" }, "children": ["todo-item"] }. Inside repeated children, use "$item/field" for per-item state paths and "$index" for the current array index. Do NOT hardcode individual elements for each array item.',
|
|
77
|
+
// Visible field placement
|
|
78
|
+
'CRITICAL: The "visible" field goes on the ELEMENT object, NOT inside "props". Correct: {"type":"Column","props":{"gap":8},"visible":{"eq":[{"path":"/activeTab"},"home"]},"children":[...]}. WRONG: {"type":"Column","props":{"gap":8,"visible":{...}},"children":[...]}.',
|
|
79
|
+
// Tab navigation pattern
|
|
80
|
+
"TAB NAVIGATION PATTERN: When building a UI with multiple tabs, use Pressable + setState action + visible conditions to make tabs functional.",
|
|
81
|
+
'Each tab button should be a Pressable wrapping its icon/label children, with action "setState" and actionParams { "path": "/activeTab", "value": "tabName" }.',
|
|
82
|
+
`Each tab's content section should have a visible condition: { "eq": [{ "path": "/activeTab" }, "tabName"] }.`,
|
|
83
|
+
"The first tab's content should NOT have a visible condition (so it shows by default when no tab is selected yet). All other tabs MUST have a visible condition.",
|
|
84
|
+
// Tab active state highlighting (using dynamic props)
|
|
85
|
+
"TAB ACTIVE STYLING: Use $cond dynamic props on the Icon inside each tab Pressable so a single Icon changes appearance based on the active tab.",
|
|
86
|
+
' - For the icon name: { "$cond": { "eq": [{ "path": "/activeTab" }, "thisTabName"] }, "$then": "home", "$else": "home-outline" }',
|
|
87
|
+
' - For the icon color: { "$cond": { "eq": [{ "path": "/activeTab" }, "thisTabName"] }, "$then": "#007AFF", "$else": "#8E8E93" }',
|
|
88
|
+
" - For labels, use $cond on the color prop similarly.",
|
|
89
|
+
' - For the FIRST/DEFAULT tab, use { "$cond": { "or": [{ "eq": [{ "path": "/activeTab" }, "thisTabName"] }, { "not": { "path": "/activeTab" } }] }, "$then": "#007AFF", "$else": "#8E8E93" } so it appears active before any tab is tapped.',
|
|
90
|
+
// Push/pop screen navigation (all screens in one spec)
|
|
91
|
+
'SCREEN NAVIGATION: Use Pressable with action "push" and actionParams { "screen": "screenName" } to navigate to a new screen. Use action "pop" to go back. All screens must be defined in the SAME spec.',
|
|
92
|
+
'Each screen section uses a visible condition on /currentScreen: { "eq": [{ "path": "/currentScreen" }, "screenName"] }. The default/home screen should also be visible when /currentScreen is not set: { "or": [{ "eq": [{ "path": "/currentScreen" }, "home"] }, { "not": { "path": "/currentScreen" } }] }.',
|
|
93
|
+
"push automatically maintains a /navStack in the state model so pop always returns to the previous screen.",
|
|
94
|
+
'Include a back button on pushed screens using action "pop". Example: Pressable(action:"pop") > Row > Icon(name:"chevron-back") + Label(text:"Back").',
|
|
95
|
+
"Use push/pop for drill-down flows: tapping a list item to see details, opening a profile, etc. Use setState + visible conditions for tab switching within a screen.",
|
|
96
|
+
'Example: A list screen with items that push to detail: Pressable(action:"push", actionParams:{screen:"repo-detail"}) wrapping each list item card. The detail screen section has visible:{"eq":[{"path":"/currentScreen"},"repo-detail"]} and contains a back button with action:"pop".'
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
var elementTreeSchema = schema;
|
|
101
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
102
|
+
0 && (module.exports = {
|
|
103
|
+
elementTreeSchema,
|
|
104
|
+
schema
|
|
105
|
+
});
|
|
106
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/schema.ts"],"sourcesContent":["import { defineSchema } from \"@json-render/core\";\n\n/**\n * The schema for @json-render/react-native\n *\n * Defines:\n * - Spec: A flat tree of elements with keys, types, props, and children references\n * - Catalog: Components with props schemas, and optional actions\n */\nexport const schema = defineSchema(\n (s) => ({\n // What the AI-generated SPEC looks like\n spec: s.object({\n /** Root element key */\n root: s.string(),\n /** Flat map of elements by key */\n elements: s.record(\n s.object({\n /** Component type from catalog */\n type: s.ref(\"catalog.components\"),\n /** Component props */\n props: s.propsOf(\"catalog.components\"),\n /** Child element keys (flat reference) */\n children: s.array(s.string()),\n /** Visibility condition */\n visible: s.any(),\n }),\n ),\n }),\n\n // What the CATALOG must provide\n catalog: s.object({\n /** Component definitions */\n components: s.map({\n /** Zod schema for component props */\n props: s.zod(),\n /** Slots for this component. Use ['default'] for children, or named slots like ['header', 'footer'] */\n slots: s.array(s.string()),\n /** Description for AI generation hints */\n description: s.string(),\n }),\n /** Action definitions (optional) */\n actions: s.map({\n /** Zod schema for action params */\n params: s.zod(),\n /** Description for AI generation hints */\n description: s.string(),\n }),\n }),\n }),\n {\n defaultRules: [\n // Layout patterns\n \"FIXED BOTTOM BAR PATTERN: When building a screen with a fixed header and/or fixed bottom tab bar, the outermost Column must have flex:1 so it fills the SafeArea. The scrollable content area must also have flex:1. Structure: SafeArea > Column(flex:1, gap:0) > [header, Container(flex:1) > [ScrollContainer(...)], bottom-tabs]. Both the outer Column AND the content Container need flex:1.\",\n \"NEVER place a bottom tab bar or fixed footer inside a ScrollContainer. It must be a sibling AFTER the flex:1 container that holds the ScrollContainer.\",\n\n // Element integrity\n \"CRITICAL INTEGRITY CHECK: Before outputting ANY element that references children, you MUST have already output (or will output) each child as its own element. If an element has children: ['a', 'b'], then elements 'a' and 'b' MUST exist. A missing child element causes that entire branch of the UI to be invisible.\",\n \"SELF-CHECK: After generating all elements, mentally walk the tree from root. Every key in every children array must resolve to a defined element. If you find a gap, output the missing element immediately.\",\n 'When building repeating content backed by a state array (e.g. todos, posts, cart items), use the \"repeat\" field on the container element. Example: { \"type\": \"Column\", \"props\": { \"gap\": 8 }, \"repeat\": { \"path\": \"/todos\", \"key\": \"id\" }, \"children\": [\"todo-item\"] }. Inside repeated children, use \"$item/field\" for per-item state paths and \"$index\" for the current array index. Do NOT hardcode individual elements for each array item.',\n\n // Visible field placement\n 'CRITICAL: The \"visible\" field goes on the ELEMENT object, NOT inside \"props\". Correct: {\"type\":\"Column\",\"props\":{\"gap\":8},\"visible\":{\"eq\":[{\"path\":\"/activeTab\"},\"home\"]},\"children\":[...]}. WRONG: {\"type\":\"Column\",\"props\":{\"gap\":8,\"visible\":{...}},\"children\":[...]}.',\n\n // Tab navigation pattern\n \"TAB NAVIGATION PATTERN: When building a UI with multiple tabs, use Pressable + setState action + visible conditions to make tabs functional.\",\n 'Each tab button should be a Pressable wrapping its icon/label children, with action \"setState\" and actionParams { \"path\": \"/activeTab\", \"value\": \"tabName\" }.',\n 'Each tab\\'s content section should have a visible condition: { \"eq\": [{ \"path\": \"/activeTab\" }, \"tabName\"] }.',\n \"The first tab's content should NOT have a visible condition (so it shows by default when no tab is selected yet). All other tabs MUST have a visible condition.\",\n\n // Tab active state highlighting (using dynamic props)\n \"TAB ACTIVE STYLING: Use $cond dynamic props on the Icon inside each tab Pressable so a single Icon changes appearance based on the active tab.\",\n ' - For the icon name: { \"$cond\": { \"eq\": [{ \"path\": \"/activeTab\" }, \"thisTabName\"] }, \"$then\": \"home\", \"$else\": \"home-outline\" }',\n ' - For the icon color: { \"$cond\": { \"eq\": [{ \"path\": \"/activeTab\" }, \"thisTabName\"] }, \"$then\": \"#007AFF\", \"$else\": \"#8E8E93\" }',\n \" - For labels, use $cond on the color prop similarly.\",\n ' - For the FIRST/DEFAULT tab, use { \"$cond\": { \"or\": [{ \"eq\": [{ \"path\": \"/activeTab\" }, \"thisTabName\"] }, { \"not\": { \"path\": \"/activeTab\" } }] }, \"$then\": \"#007AFF\", \"$else\": \"#8E8E93\" } so it appears active before any tab is tapped.',\n\n // Push/pop screen navigation (all screens in one spec)\n 'SCREEN NAVIGATION: Use Pressable with action \"push\" and actionParams { \"screen\": \"screenName\" } to navigate to a new screen. Use action \"pop\" to go back. All screens must be defined in the SAME spec.',\n 'Each screen section uses a visible condition on /currentScreen: { \"eq\": [{ \"path\": \"/currentScreen\" }, \"screenName\"] }. The default/home screen should also be visible when /currentScreen is not set: { \"or\": [{ \"eq\": [{ \"path\": \"/currentScreen\" }, \"home\"] }, { \"not\": { \"path\": \"/currentScreen\" } }] }.',\n \"push automatically maintains a /navStack in the state model so pop always returns to the previous screen.\",\n 'Include a back button on pushed screens using action \"pop\". Example: Pressable(action:\"pop\") > Row > Icon(name:\"chevron-back\") + Label(text:\"Back\").',\n \"Use push/pop for drill-down flows: tapping a list item to see details, opening a profile, etc. Use setState + visible conditions for tab switching within a screen.\",\n 'Example: A list screen with items that push to detail: Pressable(action:\"push\", actionParams:{screen:\"repo-detail\"}) wrapping each list item card. The detail screen section has visible:{\"eq\":[{\"path\":\"/currentScreen\"},\"repo-detail\"]} and contains a back button with action:\"pop\".',\n ],\n },\n);\n\n/**\n * Type for the React Native schema\n */\nexport type ReactNativeSchema = typeof schema;\n\n/**\n * Infer the spec type from a catalog\n */\nexport type ReactNativeSpec<TCatalog> = typeof schema extends {\n createCatalog: (catalog: TCatalog) => { _specType: infer S };\n}\n ? S\n : never;\n\n// Backward compatibility aliases\n/** @deprecated Use `schema` instead */\nexport const elementTreeSchema = schema;\n/** @deprecated Use `ReactNativeSchema` instead */\nexport type ElementTreeSchema = ReactNativeSchema;\n/** @deprecated Use `ReactNativeSpec` instead */\nexport type ElementTreeSpec<T> = ReactNativeSpec<T>;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAA6B;AAStB,IAAM,aAAS;AAAA,EACpB,CAAC,OAAO;AAAA;AAAA,IAEN,MAAM,EAAE,OAAO;AAAA;AAAA,MAEb,MAAM,EAAE,OAAO;AAAA;AAAA,MAEf,UAAU,EAAE;AAAA,QACV,EAAE,OAAO;AAAA;AAAA,UAEP,MAAM,EAAE,IAAI,oBAAoB;AAAA;AAAA,UAEhC,OAAO,EAAE,QAAQ,oBAAoB;AAAA;AAAA,UAErC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA;AAAA,UAE5B,SAAS,EAAE,IAAI;AAAA,QACjB,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA;AAAA,IAGD,SAAS,EAAE,OAAO;AAAA;AAAA,MAEhB,YAAY,EAAE,IAAI;AAAA;AAAA,QAEhB,OAAO,EAAE,IAAI;AAAA;AAAA,QAEb,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA;AAAA,QAEzB,aAAa,EAAE,OAAO;AAAA,MACxB,CAAC;AAAA;AAAA,MAED,SAAS,EAAE,IAAI;AAAA;AAAA,QAEb,QAAQ,EAAE,IAAI;AAAA;AAAA,QAEd,aAAa,EAAE,OAAO;AAAA,MACxB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EACA;AAAA,IACE,cAAc;AAAA;AAAA,MAEZ;AAAA,MACA;AAAA;AAAA,MAGA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAGA;AAAA;AAAA,MAGA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAGA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAGA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAkBO,IAAM,oBAAoB;","names":[]}
|
package/dist/schema.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@json-render/react-native",
|
|
3
|
+
"version": "0.4.3",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"description": "React Native renderer for @json-render/core. JSON becomes React Native components.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"json",
|
|
8
|
+
"ui",
|
|
9
|
+
"react-native",
|
|
10
|
+
"ai",
|
|
11
|
+
"generative-ui",
|
|
12
|
+
"llm",
|
|
13
|
+
"renderer",
|
|
14
|
+
"streaming",
|
|
15
|
+
"components"
|
|
16
|
+
],
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/vercel-labs/json-render.git",
|
|
20
|
+
"directory": "packages/react-native"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/vercel-labs/json-render#readme",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/vercel-labs/json-render/issues"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"main": "./dist/index.js",
|
|
30
|
+
"module": "./dist/index.mjs",
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"import": "./dist/index.mjs",
|
|
36
|
+
"require": "./dist/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./schema": {
|
|
39
|
+
"types": "./dist/schema.d.ts",
|
|
40
|
+
"import": "./dist/schema.mjs",
|
|
41
|
+
"require": "./dist/schema.js"
|
|
42
|
+
},
|
|
43
|
+
"./catalog": {
|
|
44
|
+
"types": "./dist/catalog.d.ts",
|
|
45
|
+
"import": "./dist/catalog.mjs",
|
|
46
|
+
"require": "./dist/catalog.js"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"files": [
|
|
50
|
+
"dist"
|
|
51
|
+
],
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@json-render/core": "0.4.4"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/react": "19.2.3",
|
|
57
|
+
"react-native": "0.83.1",
|
|
58
|
+
"tsup": "^8.0.2",
|
|
59
|
+
"typescript": "^5.4.5",
|
|
60
|
+
"zod": "^4.0.0",
|
|
61
|
+
"@repo/typescript-config": "0.0.0"
|
|
62
|
+
},
|
|
63
|
+
"peerDependencies": {
|
|
64
|
+
"react": "^19.0.0",
|
|
65
|
+
"react-native": ">=0.71.0",
|
|
66
|
+
"zod": "^4.0.0"
|
|
67
|
+
},
|
|
68
|
+
"scripts": {
|
|
69
|
+
"build": "tsup",
|
|
70
|
+
"dev": "tsup --watch",
|
|
71
|
+
"check-types": "tsc --noEmit",
|
|
72
|
+
"typecheck": "tsc --noEmit"
|
|
73
|
+
}
|
|
74
|
+
}
|