pxengine 0.1.2 → 0.1.4
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.cjs +467 -378
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +395 -304
- package/dist/registry.json +1 -1
- package/package.json +2 -2
- package/src/render/PXEngineRenderer.tsx +69 -247
package/dist/registry.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pxengine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Shadcn-based UI component library for agent-driven interfaces",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"src"
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
|
-
"build": "tsup
|
|
15
|
+
"build": "tsup && npm run generate-metadata",
|
|
16
16
|
"generate-metadata": "tsx scripts/generate-metadata.ts",
|
|
17
17
|
"dev": "tsup src/index.ts --format cjs,esm --watch --dts",
|
|
18
18
|
"lint": "eslint src --ext .ts,.tsx",
|
|
@@ -1,272 +1,94 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { UIComponent, UISchema
|
|
2
|
+
import { UIComponent, UISchema } from "../types/schema";
|
|
3
3
|
import * as Atoms from "../atoms";
|
|
4
|
-
import * as Molecules from "../molecules/index";
|
|
5
|
-
|
|
6
|
-
// NOTE: This renderer will be used by the frontend to interpret JSON schemas
|
|
7
|
-
// generated by the AI agent via the MCP server.
|
|
4
|
+
import * as Molecules from "../molecules/index";
|
|
8
5
|
|
|
6
|
+
/**
|
|
7
|
+
* PXEngineRenderer
|
|
8
|
+
*
|
|
9
|
+
* Handles both the full schema { version, root } and individual components.
|
|
10
|
+
* Dynamically resolves components from the Atoms/Molecules registry.
|
|
11
|
+
*/
|
|
9
12
|
interface PXEngineRendererProps {
|
|
10
13
|
schema: UISchema | UIComponent;
|
|
11
14
|
onAction?: (action: string, payload?: any) => void;
|
|
12
15
|
}
|
|
13
16
|
|
|
14
|
-
/**
|
|
15
|
-
* PXEngineRenderer
|
|
16
|
-
*
|
|
17
|
-
* The core engine of the @pxengine-ui library.
|
|
18
|
-
* Recursively renders Atoms and Molecules based on the provided JSON schema.
|
|
19
|
-
*/
|
|
20
17
|
export const PXEngineRenderer: React.FC<PXEngineRendererProps> = ({
|
|
21
18
|
schema,
|
|
22
19
|
onAction,
|
|
23
20
|
}) => {
|
|
24
|
-
|
|
25
|
-
const root = "root" in schema ? schema.root : schema;
|
|
21
|
+
if (!schema) return null;
|
|
26
22
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
*/
|
|
30
|
-
const renderAtom = (
|
|
31
|
-
atom: UIAtom,
|
|
32
|
-
renderComponent: (c: UIComponent) => React.ReactNode,
|
|
33
|
-
): React.ReactNode => {
|
|
34
|
-
const { type, id } = atom;
|
|
23
|
+
// Extract root if it's a full UISchema
|
|
24
|
+
const root = (schema as any).root || (schema as UIComponent);
|
|
35
25
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
renderComponent={renderComponent}
|
|
43
|
-
/>
|
|
44
|
-
);
|
|
45
|
-
case "card":
|
|
46
|
-
return (
|
|
47
|
-
<Atoms.CardAtom
|
|
48
|
-
key={id}
|
|
49
|
-
{...(atom as any)}
|
|
50
|
-
renderComponent={renderComponent}
|
|
51
|
-
/>
|
|
52
|
-
);
|
|
53
|
-
case "text":
|
|
54
|
-
return <Atoms.TextAtom key={id} {...(atom as any)} />;
|
|
55
|
-
case "button":
|
|
56
|
-
return (
|
|
57
|
-
<Atoms.ButtonAtom key={id} {...(atom as any)} onAction={onAction} />
|
|
58
|
-
);
|
|
59
|
-
case "input":
|
|
60
|
-
return <Atoms.InputAtom key={id} {...(atom as any)} />;
|
|
61
|
-
case "badge":
|
|
62
|
-
return <Atoms.BadgeAtom key={id} {...(atom as any)} />;
|
|
63
|
-
case "avatar":
|
|
64
|
-
return <Atoms.AvatarAtom key={id} {...(atom as any)} />;
|
|
65
|
-
case "progress":
|
|
66
|
-
return <Atoms.ProgressAtom key={id} {...(atom as any)} />;
|
|
67
|
-
case "skeleton":
|
|
68
|
-
return <Atoms.SkeletonAtom key={id} {...(atom as any)} />;
|
|
69
|
-
case "alert":
|
|
70
|
-
return <Atoms.AlertAtom key={id} {...(atom as any)} />;
|
|
71
|
-
case "separator":
|
|
72
|
-
return <Atoms.SeparatorAtom key={id} {...(atom as any)} />;
|
|
73
|
-
case "table":
|
|
74
|
-
return <Atoms.TableAtom key={id} {...(atom as any)} />;
|
|
75
|
-
case "tabs":
|
|
76
|
-
return (
|
|
77
|
-
<Atoms.TabsAtom
|
|
78
|
-
key={id}
|
|
79
|
-
{...(atom as any)}
|
|
80
|
-
renderComponent={renderComponent}
|
|
81
|
-
/>
|
|
82
|
-
);
|
|
83
|
-
case "accordion":
|
|
84
|
-
return (
|
|
85
|
-
<Atoms.AccordionAtom
|
|
86
|
-
key={id}
|
|
87
|
-
{...(atom as any)}
|
|
88
|
-
renderComponent={renderComponent}
|
|
89
|
-
/>
|
|
90
|
-
);
|
|
91
|
-
case "scroll-area":
|
|
92
|
-
return (
|
|
93
|
-
<Atoms.ScrollAreaAtom
|
|
94
|
-
key={id}
|
|
95
|
-
{...(atom as any)}
|
|
96
|
-
renderComponent={renderComponent}
|
|
97
|
-
/>
|
|
98
|
-
);
|
|
99
|
-
case "carousel":
|
|
100
|
-
return (
|
|
101
|
-
<Atoms.CarouselAtom
|
|
102
|
-
key={id}
|
|
103
|
-
{...(atom as any)}
|
|
104
|
-
renderComponent={renderComponent}
|
|
105
|
-
/>
|
|
106
|
-
);
|
|
107
|
-
case "aspect-ratio":
|
|
108
|
-
return (
|
|
109
|
-
<Atoms.AspectRatioAtom
|
|
110
|
-
key={id}
|
|
111
|
-
{...(atom as any)}
|
|
112
|
-
renderComponent={renderComponent}
|
|
113
|
-
/>
|
|
114
|
-
);
|
|
115
|
-
case "collapsible":
|
|
116
|
-
return (
|
|
117
|
-
<Atoms.CollapsibleAtom
|
|
118
|
-
key={id}
|
|
119
|
-
{...(atom as any)}
|
|
120
|
-
renderComponent={renderComponent}
|
|
121
|
-
/>
|
|
122
|
-
);
|
|
123
|
-
case "tooltip":
|
|
124
|
-
return (
|
|
125
|
-
<Atoms.TooltipAtom
|
|
126
|
-
key={id}
|
|
127
|
-
{...(atom as any)}
|
|
128
|
-
renderComponent={renderComponent}
|
|
129
|
-
/>
|
|
130
|
-
);
|
|
131
|
-
case "popover":
|
|
132
|
-
return (
|
|
133
|
-
<Atoms.PopoverAtom
|
|
134
|
-
key={id}
|
|
135
|
-
{...(atom as any)}
|
|
136
|
-
renderComponent={renderComponent}
|
|
137
|
-
/>
|
|
138
|
-
);
|
|
139
|
-
case "dialog":
|
|
140
|
-
return (
|
|
141
|
-
<Atoms.DialogAtom
|
|
142
|
-
key={id}
|
|
143
|
-
{...(atom as any)}
|
|
144
|
-
renderComponent={renderComponent}
|
|
145
|
-
/>
|
|
146
|
-
);
|
|
147
|
-
case "sheet":
|
|
148
|
-
return (
|
|
149
|
-
<Atoms.SheetAtom
|
|
150
|
-
key={id}
|
|
151
|
-
{...(atom as any)}
|
|
152
|
-
renderComponent={renderComponent}
|
|
153
|
-
/>
|
|
154
|
-
);
|
|
155
|
-
case "alert-dialog":
|
|
156
|
-
return (
|
|
157
|
-
<Atoms.AlertDialogAtom
|
|
158
|
-
key={id}
|
|
159
|
-
{...(atom as any)}
|
|
160
|
-
onAction={onAction}
|
|
161
|
-
renderComponent={renderComponent}
|
|
162
|
-
/>
|
|
163
|
-
);
|
|
164
|
-
case "breadcrumb":
|
|
165
|
-
return <Atoms.BreadcrumbAtom key={id} {...(atom as any)} />;
|
|
166
|
-
case "spinner":
|
|
167
|
-
return <Atoms.SpinnerAtom key={id} {...(atom as any)} />;
|
|
168
|
-
case "calendar":
|
|
169
|
-
return <Atoms.CalendarAtom key={id} {...(atom as any)} />;
|
|
170
|
-
case "pagination":
|
|
171
|
-
return <Atoms.PaginationAtom key={id} {...(atom as any)} />;
|
|
172
|
-
case "command":
|
|
173
|
-
return <Atoms.CommandAtom key={id} {...(atom as any)} />;
|
|
174
|
-
default:
|
|
175
|
-
return null;
|
|
26
|
+
const renderRecursive = (
|
|
27
|
+
component: UIComponent | string | any,
|
|
28
|
+
): React.ReactNode => {
|
|
29
|
+
// 1. Handle text nodes (string or number)
|
|
30
|
+
if (typeof component === "string" || typeof component === "number") {
|
|
31
|
+
return component;
|
|
176
32
|
}
|
|
177
|
-
};
|
|
178
33
|
|
|
179
|
-
|
|
180
|
-
* Render Molecule components (Domain-specific / Complex)
|
|
181
|
-
*/
|
|
182
|
-
const renderMolecule = (molecule: UIMolecule): React.ReactNode => {
|
|
183
|
-
const { type, id } = molecule;
|
|
34
|
+
if (!component) return null;
|
|
184
35
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
{...(molecule as any)}
|
|
191
|
-
onProceed={() =>
|
|
192
|
-
onAction?.((molecule as any).proceedAction || "proceed")
|
|
193
|
-
}
|
|
194
|
-
/>
|
|
195
|
-
);
|
|
196
|
-
case "search-spec":
|
|
197
|
-
return (
|
|
198
|
-
<Molecules.SearchSpecCard
|
|
199
|
-
key={id}
|
|
200
|
-
{...(molecule as any)}
|
|
201
|
-
onProceed={() =>
|
|
202
|
-
onAction?.((molecule as any).proceedAction || "proceed")
|
|
203
|
-
}
|
|
204
|
-
/>
|
|
205
|
-
);
|
|
206
|
-
case "mcq":
|
|
207
|
-
return (
|
|
208
|
-
<Molecules.MCQCard
|
|
209
|
-
key={id}
|
|
210
|
-
{...(molecule as any)}
|
|
211
|
-
onProceed={() =>
|
|
212
|
-
onAction?.((molecule as any).proceedAction || "proceed")
|
|
213
|
-
}
|
|
214
|
-
/>
|
|
215
|
-
);
|
|
216
|
-
case "action-button":
|
|
217
|
-
return (
|
|
218
|
-
<Molecules.ActionButton
|
|
219
|
-
key={id}
|
|
220
|
-
{...(molecule as any)}
|
|
221
|
-
onProceed={() => onAction?.((molecule as any).action || "proceed")}
|
|
222
|
-
/>
|
|
223
|
-
);
|
|
224
|
-
default:
|
|
225
|
-
return null;
|
|
226
|
-
}
|
|
227
|
-
};
|
|
36
|
+
const { type, name, props = {}, children = [], id } = component;
|
|
37
|
+
|
|
38
|
+
// Determine the component name to search for
|
|
39
|
+
const componentName = name || type;
|
|
40
|
+
if (!componentName) return null;
|
|
228
41
|
|
|
229
|
-
|
|
230
|
-
const
|
|
42
|
+
// Normalize name to PascalCase and check for "Atom" suffix
|
|
43
|
+
const normalizedName =
|
|
44
|
+
componentName.charAt(0).toUpperCase() + componentName.slice(1);
|
|
45
|
+
const atomName = normalizedName.endsWith("Atom")
|
|
46
|
+
? normalizedName
|
|
47
|
+
: `${normalizedName}Atom`;
|
|
231
48
|
|
|
232
|
-
//
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
"badge",
|
|
240
|
-
"avatar",
|
|
241
|
-
"progress",
|
|
242
|
-
"skeleton",
|
|
243
|
-
"alert",
|
|
244
|
-
"separator",
|
|
245
|
-
"table",
|
|
246
|
-
"tabs",
|
|
247
|
-
"accordion",
|
|
248
|
-
"scroll-area",
|
|
249
|
-
"carousel",
|
|
250
|
-
"aspect-ratio",
|
|
251
|
-
"collapsible",
|
|
252
|
-
"tooltip",
|
|
253
|
-
"popover",
|
|
254
|
-
"dialog",
|
|
255
|
-
"sheet",
|
|
256
|
-
"alert-dialog",
|
|
257
|
-
"breadcrumb",
|
|
258
|
-
"spinner",
|
|
259
|
-
"calendar",
|
|
260
|
-
"pagination",
|
|
261
|
-
"command",
|
|
262
|
-
].includes(type);
|
|
49
|
+
// 2. Resolve Component from library registry
|
|
50
|
+
const TargetComponent =
|
|
51
|
+
(Atoms as any)[atomName] ||
|
|
52
|
+
(Atoms as any)[normalizedName] ||
|
|
53
|
+
(Atoms as any)[componentName] ||
|
|
54
|
+
(Molecules as any)[normalizedName] ||
|
|
55
|
+
(Molecules as any)[componentName];
|
|
263
56
|
|
|
264
|
-
if (
|
|
265
|
-
|
|
57
|
+
if (!TargetComponent) {
|
|
58
|
+
console.warn(`[PXEngineRenderer] Component not found: ${componentName}`);
|
|
59
|
+
return (
|
|
60
|
+
<div
|
|
61
|
+
key={id}
|
|
62
|
+
className="p-2 border border-dashed border-red-500/50 text-red-500 text-[10px] rounded"
|
|
63
|
+
>
|
|
64
|
+
Unknown: {componentName}
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
266
67
|
}
|
|
267
68
|
|
|
268
|
-
|
|
69
|
+
// 3. Render Component
|
|
70
|
+
// We pass 'renderComponent' for components that handle their own child rendering (like Layout/Card)
|
|
71
|
+
// We also pass children as props for standard React children behavior
|
|
72
|
+
return (
|
|
73
|
+
<TargetComponent
|
|
74
|
+
key={id || Math.random().toString(36).substr(2, 9)}
|
|
75
|
+
{...props}
|
|
76
|
+
onAction={onAction}
|
|
77
|
+
renderComponent={renderRecursive}
|
|
78
|
+
children={Array.isArray(children) ? children : undefined}
|
|
79
|
+
>
|
|
80
|
+
{Array.isArray(children)
|
|
81
|
+
? children.map((child, idx) => (
|
|
82
|
+
<React.Fragment key={idx}>
|
|
83
|
+
{renderRecursive(child)}
|
|
84
|
+
</React.Fragment>
|
|
85
|
+
))
|
|
86
|
+
: typeof children === "string"
|
|
87
|
+
? children
|
|
88
|
+
: null}
|
|
89
|
+
</TargetComponent>
|
|
90
|
+
);
|
|
269
91
|
};
|
|
270
92
|
|
|
271
|
-
return
|
|
93
|
+
return <div className="px-engine-root">{renderRecursive(root)}</div>;
|
|
272
94
|
};
|