Skip to content

Commit a85fa02

Browse files
committed
feat: add logo SVG and image component renderer
- Introduced a new SVG logo file with a gradient background and UI layout representation. - Added an image component renderer to the component registry, allowing for dynamic image rendering with customizable source URL, alt text, and CSS classes.
1 parent 36f922b commit a85fa02

9 files changed

Lines changed: 204 additions & 1086 deletions

File tree

docs/spec/architecture.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ This document outlines the technical architecture for Steedos Object UI, designe
2424
* **Component Base**: **Shadcn/UI** (built on Radix UI)
2525
* Acceessible, unstyled primitives handling complex interactions (Dialogs, Popovers, Tabs).
2626
* The implementation serves as the concrete "UI Kit" mapped to JSON nodes.
27-
* **Icons**: **Lucide React** (Standard for Shadcn) or **Heroicons**.
27+
* **Icons**: **Lucide React** (Standard for Shadcn).
2828

2929
### C. State & Data
3030
* **App State**: **Zustand** or **Jotai**

examples/prototype/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
<html lang="en">
33
<head>
44
<meta charset="UTF-8" />
5-
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
5+
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7-
<title>prototype</title>
7+
<title>Object UI</title>
88
</head>
99
<body>
1010
<div id="root"></div>

examples/prototype/public/logo.svg

Lines changed: 18 additions & 0 deletions
Loading

examples/prototype/src/App.tsx

Lines changed: 144 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,21 @@ const schema = {
1010
{
1111
type: 'sidebar-header',
1212
body: [
13-
{
14-
type: 'div',
15-
className: 'px-4 py-2 font-bold text-xl',
16-
body: { type: 'text', content: 'Object UI' }
13+
{
14+
type: 'div',
15+
className: 'px-4 py-4 font-bold text-xl flex items-center gap-2',
16+
body: [
17+
{
18+
type: 'image',
19+
src: '/logo.svg',
20+
className: 'size-8 rounded-lg',
21+
alt: 'Object UI Logo'
22+
},
23+
{
24+
type: 'text',
25+
content: 'Object UI'
26+
}
27+
]
1728
}
1829
]
1930
},
@@ -31,22 +42,22 @@ const schema = {
3142
type: 'sidebar-menu-item',
3243
body: {
3344
type: 'sidebar-menu-button',
34-
active: true,
45+
active: true,
3546
body: { type: 'span', body: { type: 'text', content: 'Dashboard' } }
3647
}
3748
},
3849
{
3950
type: 'sidebar-menu-item',
4051
body: {
4152
type: 'sidebar-menu-button',
42-
body: { type: 'span', body: { type: 'text', content: 'Objects' } }
53+
body: { type: 'span', body: { type: 'text', content: 'Projects' } }
4354
}
4455
},
4556
{
4657
type: 'sidebar-menu-item',
4758
body: {
4859
type: 'sidebar-menu-button',
49-
body: { type: 'span', body: { type: 'text', content: 'Apps' } }
60+
body: { type: 'span', body: { type: 'text', content: 'Tasks' } }
5061
}
5162
}
5263
]
@@ -84,7 +95,7 @@ const schema = {
8495
type: 'sidebar-footer',
8596
body: {
8697
type: 'div',
87-
className: 'p-4 text-xs text-muted-foreground',
98+
className: 'p-4 text-xs text-muted-foreground border-t',
8899
body: { type: 'text', content: 'v1.0.0' }
89100
}
90101
}
@@ -95,80 +106,115 @@ const schema = {
95106
body: [
96107
{
97108
type: 'header-bar',
109+
className: 'border-b px-6 py-3',
98110
crumbs: [
99111
{ label: 'Platform', href: '#' },
100112
{ label: 'Dashboard' }
101113
]
102114
},
103115
{
104116
type: 'div',
105-
className: 'flex flex-1 flex-col gap-4 p-4 md:p-8',
117+
className: 'flex flex-1 flex-col gap-6 p-8 bg-muted/10 min-h-[calc(100vh-4rem)]',
106118
body: [
107119
{
108120
type: 'div',
109-
className: 'grid gap-4 md:grid-cols-2 lg:grid-cols-4',
121+
className: 'flex items-center justify-between',
122+
body: [
123+
{
124+
type: 'div',
125+
className: 'space-y-1',
126+
body: [
127+
{ type: 'div', className: 'text-2xl font-bold tracking-tight', body: { type: 'text', content: 'Dashboard' } },
128+
{ type: 'div', className: 'text-sm text-muted-foreground', body: { type: 'text', content: 'Overview of your project performance and metrics.' } }
129+
]
130+
},
131+
{
132+
type: 'div',
133+
className: 'flex items-center gap-2',
134+
body: [
135+
{ type: 'button', label: 'Download', variant: 'outline', size: 'sm' },
136+
{ type: 'button', label: 'Create New', size: 'sm' }
137+
]
138+
}
139+
]
140+
},
141+
{
142+
type: 'div',
143+
className: 'grid gap-6 md:grid-cols-2 lg:grid-cols-4',
110144
body: [
111145
{
112146
type: 'card',
113-
title: 'Total Revenue',
147+
className: 'shadow-sm hover:shadow-md transition-shadow',
114148
body: [
115-
{
116-
type: 'div',
117-
className: 'text-2xl font-bold',
118-
body: { type: 'text', content: '$45,231.89' }
149+
{
150+
type: 'div',
151+
className: 'p-6 pb-2',
152+
body: { type: 'div', className: 'text-sm font-medium text-muted-foreground', body: { type: 'text', content: 'Total Revenue' } }
119153
},
120-
{
121-
type: 'div',
122-
className: 'text-xs text-muted-foreground',
123-
body: { type: 'text', content: '+20.1% from last month' }
154+
{
155+
type: 'div',
156+
className: 'p-6 pt-0',
157+
body: [
158+
{ type: 'div', className: 'text-2xl font-bold', body: { type: 'text', content: '$45,231.89' } },
159+
{ type: 'div', className: 'text-xs text-muted-foreground mt-1', body: { type: 'text', content: '+20.1% from last month' } }
160+
]
124161
}
125162
]
126163
},
127164
{
128165
type: 'card',
129-
title: 'Subscriptions',
166+
className: 'shadow-sm hover:shadow-md transition-shadow',
130167
body: [
131-
{
132-
type: 'div',
133-
className: 'text-2xl font-bold',
134-
body: { type: 'text', content: '+2350' }
168+
{
169+
type: 'div',
170+
className: 'p-6 pb-2',
171+
body: { type: 'div', className: 'text-sm font-medium text-muted-foreground', body: { type: 'text', content: 'Subscriptions' } }
135172
},
136-
{
137-
type: 'div',
138-
className: 'text-xs text-muted-foreground',
139-
body: { type: 'text', content: '+180.1% from last month' }
173+
{
174+
type: 'div',
175+
className: 'p-6 pt-0',
176+
body: [
177+
{ type: 'div', className: 'text-2xl font-bold', body: { type: 'text', content: '+2,350' } },
178+
{ type: 'div', className: 'text-xs text-muted-foreground mt-1', body: { type: 'text', content: '+180.1% from last month' } }
179+
]
140180
}
141181
]
142182
},
143183
{
144184
type: 'card',
145-
title: 'Sales',
185+
className: 'shadow-sm hover:shadow-md transition-shadow',
146186
body: [
147-
{
148-
type: 'div',
149-
className: 'text-2xl font-bold',
150-
body: { type: 'text', content: '+12,234' }
187+
{
188+
type: 'div',
189+
className: 'p-6 pb-2',
190+
body: { type: 'div', className: 'text-sm font-medium text-muted-foreground', body: { type: 'text', content: 'Sales' } }
151191
},
152-
{
153-
type: 'div',
154-
className: 'text-xs text-muted-foreground',
155-
body: { type: 'text', content: '+19% from last month' }
192+
{
193+
type: 'div',
194+
className: 'p-6 pt-0',
195+
body: [
196+
{ type: 'div', className: 'text-2xl font-bold', body: { type: 'text', content: '+12,234' } },
197+
{ type: 'div', className: 'text-xs text-muted-foreground mt-1', body: { type: 'text', content: '+19% from last month' } }
198+
]
156199
}
157200
]
158201
},
159202
{
160203
type: 'card',
161-
title: 'Active Now',
204+
className: 'shadow-sm hover:shadow-md transition-shadow',
162205
body: [
163-
{
164-
type: 'div',
165-
className: 'text-2xl font-bold',
166-
body: { type: 'text', content: '+573' }
206+
{
207+
type: 'div',
208+
className: 'p-6 pb-2',
209+
body: { type: 'div', className: 'text-sm font-medium text-muted-foreground', body: { type: 'text', content: 'Active Now' } }
167210
},
168-
{
169-
type: 'div',
170-
className: 'text-xs text-muted-foreground',
171-
body: { type: 'text', content: '+201 since last hour' }
211+
{
212+
type: 'div',
213+
className: 'p-6 pt-0',
214+
body: [
215+
{ type: 'div', className: 'text-2xl font-bold', body: { type: 'text', content: '+573' } },
216+
{ type: 'div', className: 'text-xs text-muted-foreground mt-1', body: { type: 'text', content: '+201 since last hour' } }
217+
]
172218
}
173219
]
174220
}
@@ -177,59 +223,73 @@ const schema = {
177223
{
178224
type: 'tabs',
179225
defaultValue: 'overview',
180-
className: 'space-y-4',
226+
className: 'space-y-6',
181227
items: [
182228
{
183229
value: 'overview',
184230
label: 'Overview',
185231
body: [
186-
{
187-
type: 'card',
188-
title: 'Overview',
189-
description: 'View your dashboard overview here.',
190-
className: 'mb-4',
191-
body: [
192-
{ type: 'div', className: 'mb-4', body: { type: 'text', content: 'This is a demonstration of the Object UI components rendered from JSON schema.' } },
193-
{
194-
type: 'div',
195-
className: 'flex gap-2 flex-wrap',
196-
body: [
197-
{ type: 'button', label: 'Primary Action' },
198-
{ type: 'button', label: 'Secondary', variant: 'secondary' },
199-
{ type: 'button', label: 'Outline', variant: 'outline' },
200-
{ type: 'button', label: 'Destructive', variant: 'destructive' },
201-
{ type: 'button', label: 'Ghost', variant: 'ghost' },
202-
{ type: 'button', label: 'Link', variant: 'link' }
203-
]
204-
}
205-
]
206-
},
207232
{
208233
type: 'div',
209-
className: 'grid gap-4 md:grid-cols-2 lg:grid-cols-7',
234+
className: 'grid gap-6 md:grid-cols-2 lg:grid-cols-7',
210235
body: [
211236
{
212-
type: 'card',
213-
className: 'col-span-4',
214-
title: 'Interactive Chart',
215-
body: {
216-
type: 'div',
217-
className: 'h-[200px] flex items-center justify-center bg-gray-50 rounded border border-dashed text-muted-foreground',
218-
body: { type: 'text', content: 'Chart Area Placeholder' }
219-
}
237+
type: 'card',
238+
className: 'col-span-4 shadow-sm',
239+
title: 'Interactive Chart',
240+
body: {
241+
type: 'div',
242+
className: 'h-[350px] flex items-center justify-center bg-muted/50 rounded-md border border-dashed text-muted-foreground m-6',
243+
body: { type: 'text', content: 'Chart Area Placeholder' }
244+
}
220245
},
221246
{
222247
type: 'card',
223-
className: 'col-span-3',
224-
title: 'Form Example',
225-
description: 'A simple form layout using Input components.',
248+
className: 'col-span-3 shadow-sm',
249+
title: 'Quick Access',
250+
description: 'Common actions and forms.',
226251
body: [
227-
{ type: 'input', label: 'Email', id: 'email', inputType: 'email', placeholder: 'm@example.com', wrapperClass: 'mb-2' },
228-
{ type: 'input', label: 'Username', id: 'username', placeholder: 'jdoe', wrapperClass: 'mb-4' },
229-
{ type: 'button', label: 'Save Changes', className: 'w-full' }
252+
{
253+
type: 'div',
254+
className: 'p-6 pt-0 space-y-4',
255+
body: [
256+
{
257+
type: 'input',
258+
label: 'Email Address',
259+
id: 'email',
260+
inputType: 'email',
261+
placeholder: 'm@example.com'
262+
},
263+
{
264+
type: 'input',
265+
label: 'Workspace Name',
266+
id: 'workspace',
267+
placeholder: 'Acme Inc.'
268+
},
269+
{
270+
type: 'button',
271+
label: 'Save Preferences',
272+
className: 'w-full mt-2'
273+
}
274+
]
275+
}
230276
]
231277
}
232278
]
279+
},
280+
{
281+
type: 'card',
282+
className: 'shadow-sm mt-6',
283+
title: 'Recent Activity',
284+
description: 'Your recent component usage history.',
285+
body: {
286+
type: 'div',
287+
className: 'p-6 pt-0',
288+
body: [
289+
{ type: 'div', className: 'text-sm', body: { type: 'text', content: 'User updated the schema at 10:42 AM' } },
290+
{ type: 'div', className: 'text-sm text-muted-foreground', body: { type: 'text', content: 'User created a new component at 09:15 AM' } }
291+
]
292+
}
233293
}
234294
]
235295
},
@@ -264,3 +324,4 @@ function App() {
264324
}
265325

266326
export default App
327+

0 commit comments

Comments
 (0)