From d3452dd4526d4ef31d5e138f7e67cc23ce9842d8 Mon Sep 17 00:00:00 2001 From: Kenneth Date: Fri, 13 Mar 2026 23:56:34 +0000 Subject: [PATCH] feat(client): add json-render catalog and registry (#67) Co-authored-by: Ona --- apps/aelis-client/package.json | 4 +- apps/aelis-client/src/json-render/catalog.ts | 68 +++++++++++++++++++ apps/aelis-client/src/json-render/index.ts | 2 + .../aelis-client/src/json-render/registry.tsx | 43 ++++++++++++ bun.lock | 6 ++ 5 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 apps/aelis-client/src/json-render/catalog.ts create mode 100644 apps/aelis-client/src/json-render/index.ts create mode 100644 apps/aelis-client/src/json-render/registry.tsx diff --git a/apps/aelis-client/package.json b/apps/aelis-client/package.json index 626730e..ee1f2be 100644 --- a/apps/aelis-client/package.json +++ b/apps/aelis-client/package.json @@ -18,6 +18,7 @@ "@expo-google-fonts/inter": "^0.4.2", "@expo-google-fonts/source-serif-4": "^0.4.1", "@expo/vector-icons": "^15.0.3", + "@json-render/react-native": "^0.13.0", "@react-navigation/bottom-tabs": "^7.4.0", "@react-navigation/elements": "^2.6.3", "@react-navigation/native": "^7.1.8", @@ -45,7 +46,8 @@ "react-native-svg": "15.12.1", "react-native-web": "~0.21.0", "react-native-worklets": "0.5.1", - "twrnc": "^4.16.0" + "twrnc": "^4.16.0", + "zod": "^4.3.6" }, "devDependencies": { "@types/react": "~19.1.0", diff --git a/apps/aelis-client/src/json-render/catalog.ts b/apps/aelis-client/src/json-render/catalog.ts new file mode 100644 index 0000000..9bbc7a6 --- /dev/null +++ b/apps/aelis-client/src/json-render/catalog.ts @@ -0,0 +1,68 @@ +import { defineCatalog } from "@json-render/core" +import { schema } from "@json-render/react-native/schema" +import { z } from "zod" + +export const catalog = defineCatalog(schema, { + components: { + View: { + props: z.object({ + style: z.string().nullable(), + }), + slots: ["default"], + description: + "Generic layout container. The style prop accepts a twrnc class string (e.g. 'flex-row gap-2 p-4 items-center').", + example: { style: "flex-row gap-2 p-4" }, + }, + Button: { + props: z.object({ + label: z.string(), + leadingIcon: z.string().nullable(), + trailingIcon: z.string().nullable(), + }), + events: ["press"], + slots: [], + description: + "Pressable button with a label and optional Feather icons. Icon values are Feather icon names (e.g. 'plus', 'arrow-right'). Bind on.press to trigger an action.", + example: { label: "Add item", leadingIcon: "plus", trailingIcon: null }, + }, + FeedCard: { + props: z.object({ + style: z.string().nullable(), + }), + slots: ["default"], + description: "Bordered card container for feed content. The style prop accepts a twrnc class string.", + example: { style: "p-4 gap-2" }, + }, + SansSerifText: { + props: z.object({ + text: z.string(), + style: z.string().nullable(), + }), + slots: [], + description: + "Sans-serif text (Inter font). The style prop accepts a twrnc class string for size, weight, color, etc.", + example: { text: "Hello world", style: "text-base font-medium" }, + }, + SerifText: { + props: z.object({ + text: z.string(), + style: z.string().nullable(), + }), + slots: [], + description: + "Serif text (Source Serif 4 font). The style prop accepts a twrnc class string for size, color, etc.", + example: { text: "Heading", style: "text-xl" }, + }, + MonospaceText: { + props: z.object({ + text: z.string(), + style: z.string().nullable(), + }), + slots: [], + description: + "Monospace text (Menlo font). The style prop accepts a twrnc class string for size, color, etc.", + example: { text: "const x = 42", style: "text-sm" }, + }, + }, + actions: {}, +}) diff --git a/apps/aelis-client/src/json-render/index.ts b/apps/aelis-client/src/json-render/index.ts new file mode 100644 index 0000000..fb3f93a --- /dev/null +++ b/apps/aelis-client/src/json-render/index.ts @@ -0,0 +1,2 @@ +export { catalog } from "./catalog" +export { registry } from "./registry" diff --git a/apps/aelis-client/src/json-render/registry.tsx b/apps/aelis-client/src/json-render/registry.tsx new file mode 100644 index 0000000..41a9f98 --- /dev/null +++ b/apps/aelis-client/src/json-render/registry.tsx @@ -0,0 +1,43 @@ +import Feather from "@expo/vector-icons/Feather" +import { defineRegistry } from "@json-render/react-native" +import { View } from "react-native" +import tw from "twrnc" + +import { Button } from "@/components/ui/button" +import { FeedCard } from "@/components/ui/feed-card" +import { MonospaceText } from "@/components/ui/monospace-text" +import { SansSerifText } from "@/components/ui/sans-serif-text" +import { SerifText } from "@/components/ui/serif-text" + +import { catalog } from "./catalog" + +function featherIcon(name: string | null | undefined) { + if (!name) return undefined + return ["name"]} size={18} color="#e7e5e4" /> +} + +export const { registry } = defineRegistry(catalog, { + components: { + View: ({ props, children }) => {children}, + Button: ({ props, emit }) => ( +