From 30fdf4b1990ad73599d8914ec4fe6b49e0e3cb9b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 16 Jan 2026 17:45:06 +0000 Subject: [PATCH 1/4] Initial plan From 0867e40d50fb07abd5ea2eaeed980d9667fa2620 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 16 Jan 2026 17:53:00 +0000 Subject: [PATCH 2/4] Add navigation and footer components to home page Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com> --- packages/ui_home/components/ui.json | 165 +++++++++++++++++++++++++++- 1 file changed, 162 insertions(+), 3 deletions(-) diff --git a/packages/ui_home/components/ui.json b/packages/ui_home/components/ui.json index 7cad674db..9fbf32b09 100644 --- a/packages/ui_home/components/ui.json +++ b/packages/ui_home/components/ui.json @@ -13,17 +13,19 @@ "type": "element", "template": { "type": "Box", - "component": "main", + "component": "div", "className": "home-page", "sx": { "minHeight": "100vh", "background": "linear-gradient(135deg, rgba(var(--color-primary-rgb), 0.05) 0%, transparent 50%, rgba(var(--color-accent-rgb), 0.05) 100%)" }, "children": [ + { "$ref": "landing_nav" }, { "$ref": "hero_section" }, { "$ref": "features_section" }, { "$ref": "about_section" }, - { "$ref": "contact_section" } + { "$ref": "contact_section" }, + { "$ref": "landing_footer" } ] } } @@ -620,6 +622,161 @@ ] } } + }, + { + "id": "landing_nav", + "name": "LandingNav", + "description": "Navigation bar for landing page with logo, links, and mobile menu", + "props": [], + "render": { + "type": "element", + "template": { + "type": "Box", + "component": "nav", + "className": "landing-nav", + "sx": { + "borderBottom": 1, + "borderColor": "divider", + "bgcolor": "rgba(255, 255, 255, 0.5)", + "backdropFilter": "blur(8px)", + "position": "sticky", + "top": 0, + "zIndex": 50 + }, + "children": [ + { + "type": "Container", + "maxWidth": "lg", + "children": [ + { + "type": "Stack", + "direction": "row", + "justifyContent": "space-between", + "alignItems": "center", + "sx": { "height": 64 }, + "children": [ + { + "type": "Stack", + "direction": "row", + "alignItems": "center", + "spacing": 1.5, + "children": [ + { + "type": "Box", + "sx": { + "width": 32, + "height": 32, + "borderRadius": 2, + "background": "linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 100%)" + } + }, + { + "type": "Text", + "variant": "h6", + "children": "MetaBuilder", + "sx": { "fontWeight": 700 } + } + ] + }, + { + "type": "Stack", + "direction": "row", + "spacing": 2, + "sx": { "display": { "xs": "none", "md": "flex" } }, + "children": [ + { + "type": "Link", + "href": "#features", + "children": "Features", + "sx": { + "color": "text.secondary", + "textDecoration": "none", + "fontSize": "0.875rem", + "&:hover": { "color": "text.primary" } + } + }, + { + "type": "Link", + "href": "#about", + "children": "About", + "sx": { + "color": "text.secondary", + "textDecoration": "none", + "fontSize": "0.875rem", + "&:hover": { "color": "text.primary" } + } + }, + { + "type": "Link", + "href": "#contact", + "children": "Contact", + "sx": { + "color": "text.secondary", + "textDecoration": "none", + "fontSize": "0.875rem", + "&:hover": { "color": "text.primary" } + } + }, + { + "type": "Button", + "variant": "outlined", + "size": "small", + "children": "Sign In", + "href": "/ui/login" + }, + { + "type": "Button", + "variant": "contained", + "size": "small", + "children": "Admin" + } + ] + } + ] + } + ] + } + ] + } + } + }, + { + "id": "landing_footer", + "name": "LandingFooter", + "description": "Simple footer for landing page", + "props": [], + "render": { + "type": "element", + "template": { + "type": "Box", + "component": "footer", + "className": "landing-footer", + "sx": { + "borderTop": 1, + "borderColor": "divider", + "bgcolor": "action.hover", + "py": 4, + "mt": 10 + }, + "children": [ + { + "type": "Container", + "maxWidth": "lg", + "children": [ + { + "type": "Text", + "variant": "body2", + "sx": { + "color": "text.secondary", + "textAlign": "center" + }, + "children": "© 2025 MetaBuilder. Built with the power of visual workflows and declarative schemas." + } + ] + } + ] + } + } } ], "exports": { @@ -634,7 +791,9 @@ "FeatureCard5", "FeatureCard6", "AboutSection", - "ContactSection" + "ContactSection", + "LandingNav", + "LandingFooter" ] } } From 22c1c36da63694c8a5b29c55a36239690e1da201 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 16 Jan 2026 18:03:13 +0000 Subject: [PATCH 3/4] Build DBAL, generate types, set up database and bootstrap seed data Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com> --- frontends/nextjs/prisma-push.cjs | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 frontends/nextjs/prisma-push.cjs diff --git a/frontends/nextjs/prisma-push.cjs b/frontends/nextjs/prisma-push.cjs new file mode 100644 index 000000000..591bc4ee6 --- /dev/null +++ b/frontends/nextjs/prisma-push.cjs @@ -0,0 +1,3 @@ +const { execSync } = require('child_process'); +process.env.DATABASE_URL = process.env.DATABASE_URL || 'file:./dev.db'; +execSync('npx prisma db push --schema=../../dbal/shared/prisma/schema.prisma', { stdio: 'inherit' }); From 395104cd8b1672c2e48dd1e8283f4a4166a4a193 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 16 Jan 2026 18:06:51 +0000 Subject: [PATCH 4/4] Implement $ref resolution for JSON components (needs server restart) Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com> --- frontends/nextjs/src/app/page.tsx | 4 +- .../packages/json/render-json-component.tsx | 40 ++++++++++++++----- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/frontends/nextjs/src/app/page.tsx b/frontends/nextjs/src/app/page.tsx index 62844fa5f..742d86107 100644 --- a/frontends/nextjs/src/app/page.tsx +++ b/frontends/nextjs/src/app/page.tsx @@ -79,7 +79,7 @@ export default async function RootPage() { const component = pkg.components?.find(c => c.id === route.component || c.name === route.component) if (component !== undefined) { - return renderJSONComponent(component, {}, {}) + return renderJSONComponent(component, {}, {}, pkg.components) } } catch { // Package doesn't exist or can't be loaded, fall through @@ -116,7 +116,7 @@ export default async function RootPage() { ) ?? pkg.components[0] if (pageComponent !== undefined) { - return renderJSONComponent(pageComponent, {}, {}) + return renderJSONComponent(pageComponent, {}, {}, pkg.components) } } } catch { diff --git a/frontends/nextjs/src/lib/packages/json/render-json-component.tsx b/frontends/nextjs/src/lib/packages/json/render-json-component.tsx index 6ade3109a..f695bf52c 100644 --- a/frontends/nextjs/src/lib/packages/json/render-json-component.tsx +++ b/frontends/nextjs/src/lib/packages/json/render-json-component.tsx @@ -22,11 +22,13 @@ export interface RenderContext { * * By default, uses the FAKEMUI_REGISTRY to render components. * Pass a custom ComponentRegistry to override specific components. + * Pass allComponents to enable $ref resolution within the same package. */ export function renderJSONComponent( component: JSONComponent, props: Record = {}, - ComponentRegistry: Record>> = FAKEMUI_REGISTRY + ComponentRegistry: Record>> = FAKEMUI_REGISTRY, + allComponents?: JSONComponent[] ): React.ReactElement { if (component.render === undefined) { return ( @@ -36,6 +38,11 @@ export function renderJSONComponent( ) } + // Build component registry for $ref resolution + const componentRegistry = allComponents + ? new Map(allComponents.map(c => [c.id, c])) + : undefined + const context: RenderContext = { props, state: {}, @@ -50,7 +57,7 @@ export function renderJSONComponent( ) } - return renderTemplate(template, context, ComponentRegistry) + return renderTemplate(template, context, ComponentRegistry, componentRegistry) } catch (error) { return (
@@ -67,7 +74,8 @@ export function renderJSONComponent( function renderTemplate( node: JsonValue, context: RenderContext, - ComponentRegistry: Record>> + ComponentRegistry: Record>>, + componentRegistry?: Map ): React.ReactElement { if (node === null || typeof node !== 'object') { return <>{String(node)} @@ -81,6 +89,20 @@ function renderTemplate( // Now TypeScript knows it's a JsonObject (non-array object) const nodeObj = node as Record + // Handle $ref to other components in the same package + if (typeof nodeObj.$ref === 'string' && componentRegistry !== undefined) { + const referencedComponent = componentRegistry.get(nodeObj.$ref) + if (referencedComponent !== undefined && referencedComponent.render?.template !== undefined) { + return renderTemplate(referencedComponent.render.template, context, ComponentRegistry, componentRegistry) + } else { + return ( +
+ Warning: Component reference "${nodeObj.$ref}" not found +
+ ) + } + } + // Handle conditional rendering if (nodeObj.type === 'conditional') { const conditionValue = nodeObj.condition @@ -90,9 +112,9 @@ function renderTemplate( const condition = evaluateExpression(conditionValue, context) const conditionIsTrue = condition !== null && condition !== undefined && condition !== false && condition !== 0 && condition !== '' if (conditionIsTrue && nodeObj.then !== null && nodeObj.then !== undefined) { - return renderTemplate(nodeObj.then, context, ComponentRegistry) + return renderTemplate(nodeObj.then, context, ComponentRegistry, componentRegistry) } else if (!conditionIsTrue && nodeObj.else !== null && nodeObj.else !== undefined) { - return renderTemplate(nodeObj.else, context, ComponentRegistry) + return renderTemplate(nodeObj.else, context, ComponentRegistry, componentRegistry) } return <> } @@ -128,12 +150,12 @@ function renderTemplate( } return ( - {renderTemplate(child, context, ComponentRegistry)} + {renderTemplate(child, context, ComponentRegistry, componentRegistry)} ) }) } else { - children = renderTemplate(nodeChildren, context, ComponentRegistry) + children = renderTemplate(nodeChildren, context, ComponentRegistry, componentRegistry) } } @@ -189,12 +211,12 @@ function renderTemplate( } return ( - {renderTemplate(child, context, ComponentRegistry)} + {renderTemplate(child, context, ComponentRegistry, componentRegistry)} ) }) } else { - children = renderTemplate(nodeChildren, context, ComponentRegistry) + children = renderTemplate(nodeChildren, context, ComponentRegistry, componentRegistry) } }