@@ -89,88 +89,116 @@ const StudioToolbarContext = ({
8989 } ;
9090
9191 return (
92- < header className = "h-14 border-b bg-white flex items-center px-4 justify-between flex-shrink-0 z-20 relative" >
92+ < header className = "h-16 border-b bg-white flex items-center px-4 justify-between flex-shrink-0 z-50 relative shadow-sm " >
9393 < div className = "flex items-center gap-4" >
94- < button
95- onClick = { ( ) => navigate ( '/' ) }
96- className = "p-2 hover:bg-gray-100 rounded-full text-gray-500 transition-colors"
97- title = "Back to Gallery"
98- >
99- < ArrowLeft className = "w-5 h-5" />
100- </ button >
101-
102- < div >
103- < h1 className = "font-semibold text-sm text-gray-900" >
104- { exampleTitle }
105- </ h1 >
106- < div className = "text-xs text-gray-500 flex items-center gap-1" >
107- < span className = { `w-2 h-2 rounded-full ${ jsonError ? 'bg-red-500' : 'bg-green-500' } ` } > </ span >
108- { jsonError ? 'Invalid JSON' : 'Ready' }
94+ < div className = "flex items-center gap-2" >
95+ < button
96+ onClick = { ( ) => navigate ( '/' ) }
97+ className = "p-2 -ml-2 text-gray-500 hover:text-gray-900 hover:bg-gray-100 rounded-full transition-all"
98+ title = "Back to Gallery"
99+ >
100+ < ArrowLeft className = "w-5 h-5" />
101+ </ button >
102+ < div className = "h-6 w-px bg-gray-200 mx-1" > </ div >
103+ < div >
104+ < div className = "flex items-center gap-2" >
105+ < span className = "font-bold text-gray-900 tracking-tight" > Object Studio</ span >
106+ < span className = "text-gray-300" > /</ span >
107+ < span className = "text-gray-600 font-medium" > { exampleTitle } </ span >
108+ </ div >
109+ < div className = "flex items-center gap-1.5 mt-0.5" >
110+ < span className = { `w-1.5 h-1.5 rounded-full ${ jsonError ? 'bg-red-500' : 'bg-green-500' } ring-2 ring-opacity-20 ${ jsonError ? 'ring-red-500' : 'ring-green-500' } ` } > </ span >
111+ < span className = "text-[10px] uppercase tracking-wider font-semibold text-gray-400" >
112+ { jsonError ? 'Error' : 'Ready' }
113+ </ span >
114+ </ div >
109115 </ div >
110116 </ div >
111117 </ div >
112118
113119 { /* Center: View Mode Switcher */ }
114- < div className = "flex p-1 bg-gray-100 rounded-lg absolute left-1/2 transform -translate-x-1/2" >
115- < button
116- onClick = { ( ) => setViewMode ( 'design' ) }
117- className = { `flex items-center gap-2 px-3 py-1.5 text-xs font-medium rounded-md transition-all ${ viewMode === 'design' ? 'bg-white shadow text-indigo-600' : 'text-gray-600 hover:text-gray-900' } ` }
118- >
119- < PenTool className = "w-3.5 h-3.5" />
120- Design
121- </ button >
122- < button
123- onClick = { ( ) => setViewMode ( 'preview' ) }
124- className = { `flex items-center gap-2 px-3 py-1.5 text-xs font-medium rounded-md transition-all ${ viewMode === 'preview' ? 'bg-white shadow text-indigo-600' : 'text-gray-600 hover:text-gray-900' } ` }
125- >
126- < Monitor className = "w-3.5 h-3.5" />
127- Preview
128- </ button >
129- < button
130- onClick = { ( ) => setViewMode ( 'code' ) }
131- className = { `flex items-center gap-2 px-3 py-1.5 text-xs font-medium rounded-md transition-all ${ viewMode === 'code' ? 'bg-white shadow text-indigo-600' : 'text-gray-600 hover:text-gray-900' } ` }
132- >
133- < Code2 className = "w-3.5 h-3.5" />
134- Code
135- </ button >
120+ < div className = "absolute left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2" >
121+ < div className = "flex p-1 bg-gray-100/80 backdrop-blur-sm rounded-lg border border-gray-200/50" >
122+ < button
123+ onClick = { ( ) => setViewMode ( 'design' ) }
124+ className = { `flex items-center gap-2 px-4 py-1.5 text-xs font-medium rounded-md transition-all duration-200 ${
125+ viewMode === 'design'
126+ ? 'bg-white text-gray-900 shadow-sm ring-1 ring-black/5'
127+ : 'text-gray-500 hover:text-gray-900 hover:bg-gray-200/50'
128+ } `}
129+ >
130+ < PenTool className = "w-3.5 h-3.5" />
131+ Design
132+ </ button >
133+ < button
134+ onClick = { ( ) => setViewMode ( 'preview' ) }
135+ className = { `flex items-center gap-2 px-4 py-1.5 text-xs font-medium rounded-md transition-all duration-200 ${
136+ viewMode === 'preview'
137+ ? 'bg-white text-gray-900 shadow-sm ring-1 ring-black/5'
138+ : 'text-gray-500 hover:text-gray-900 hover:bg-gray-200/50'
139+ } `}
140+ >
141+ < Monitor className = "w-3.5 h-3.5" />
142+ Preview
143+ </ button >
144+ < button
145+ onClick = { ( ) => setViewMode ( 'code' ) }
146+ className = { `flex items-center gap-2 px-4 py-1.5 text-xs font-medium rounded-md transition-all duration-200 ${
147+ viewMode === 'code'
148+ ? 'bg-white text-gray-900 shadow-sm ring-1 ring-black/5'
149+ : 'text-gray-500 hover:text-gray-900 hover:bg-gray-200/50'
150+ } `}
151+ >
152+ < Code2 className = "w-3.5 h-3.5" />
153+ Code
154+ </ button >
155+ </ div >
136156 </ div >
137157
138158 { /* Right: Actions */ }
139159 < div className = "flex items-center gap-2" >
140160 { viewMode === 'design' && (
141- < >
161+ < div className = "flex items-center bg-gray-50 rounded-lg border border-gray-200 p-0.5 mr-2" >
142162 < button
143163 onClick = { undo }
144164 disabled = { ! canUndo }
145- className = { `p-2 rounded-md transition-colors ${ ! canUndo ? 'text-gray-300' : 'text-gray-600 hover:bg-gray-100' } ` }
165+ className = { `p-1.5 rounded-md transition-all ${ ! canUndo ? 'text-gray-300 cursor-not-allowed' : 'text-gray-600 hover:bg-white hover:text-gray-900 hover:shadow-sm' } ` }
166+ title = "Undo (Cmd+Z)"
146167 >
147168 < Undo className = "w-4 h-4" />
148169 </ button >
149170 < button
150171 onClick = { redo }
151172 disabled = { ! canRedo }
152- className = { `p-2 rounded-md transition-colors ${ ! canRedo ? 'text-gray-300' : 'text-gray-600 hover:bg-gray-100' } ` }
173+ className = { `p-1.5 rounded-md transition-all ${ ! canRedo ? 'text-gray-300 cursor-not-allowed' : 'text-gray-600 hover:bg-white hover:text-gray-900 hover:shadow-sm' } ` }
174+ title = "Redo (Cmd+Y)"
153175 >
154176 < Redo className = "w-4 h-4" />
155177 </ button >
156- < div className = "w-px h-4 bg-gray-200 mx-1" > </ div >
157- </ >
178+ </ div >
158179 ) }
159180
181+ < div className = "h-6 w-px bg-gray-200 mx-2" > </ div >
182+
160183 < button
161184 onClick = { handleExport }
162- className = "p-2 text-gray-600 hover:bg-gray-100 rounded-md transition-colors"
163- title = "Export JSON"
185+ className = "flex items-center gap-2 px-3 py-2 text-xs font-medium text- gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 hover:text-gray-900 transition-colors"
186+ title = "Download JSON"
164187 >
165188 < Download className = "w-4 h-4" />
189+ Export
166190 </ button >
167191
168192 < button
169193 onClick = { handleCopy }
170- className = "flex items-center gap-2 px-3 py-1.5 text-xs rounded-md bg-indigo-600 text-white hover:bg-indigo-700 transition-colors shadow-sm ml-2"
194+ className = { `flex items-center gap-2 px-3 py-2 text-xs font-medium rounded-lg transition-all shadow-sm ${
195+ copied
196+ ? 'bg-green-600 text-white hover:bg-green-700'
197+ : 'bg-indigo-600 text-white hover:bg-indigo-700'
198+ } `}
171199 >
172- { copied ? < Check className = "h-3 w-3" /> : < Copy className = "h-3 w-3" /> }
173- { copied ? 'Copied' : 'Copy JSON ' }
200+ { copied ? < Check className = "h-3.5 w-3.5 " /> : < Copy className = "h-3.5 w-3.5 " /> }
201+ { copied ? 'Copied' : 'Copy' }
174202 </ button >
175203 </ div >
176204 </ header >
@@ -259,31 +287,31 @@ const StudioEditor = ({ exampleId, initialJson }: { exampleId: ExampleKey, initi
259287 </ div >
260288 ) : viewMode === 'preview' ? (
261289 < div className = "h-full flex flex-col bg-gray-50 overflow-hidden absolute inset-0" >
262- < div className = "border-b px-4 py-2 bg-white flex items-center justify-center shadow-sm z-10" >
290+ < div className = "border-b px-4 py-2 bg-white/80 backdrop-blur-sm flex items-center justify-center z-10 sticky top-0 " >
263291 { /* Viewport Size Toggles */ }
264- < div className = "flex items-center gap-1 bg-gray-100 rounded-md p-1 " >
292+ < div className = "flex items-center gap-1 bg-gray-100/80 p-1 rounded-lg border border-gray-200 " >
265293 < button
266294 onClick = { ( ) => setViewportSize ( 'desktop' ) }
267- className = { `p-1.5 rounded transition-colors ${
268- viewportSize === 'desktop' ? 'bg-white shadow-sm text-indigo-600' : 'text-gray-500 hover:text-gray-700'
295+ className = { `p-1.5 rounded-md transition-all duration-200 ${
296+ viewportSize === 'desktop' ? 'bg-white shadow-sm text-indigo-600 ring-1 ring-black/5 ' : 'text-gray-400 hover:text-gray-700'
269297 } `}
270298 title = "Desktop View"
271299 >
272300 < Monitor className = "h-4 w-4" />
273301 </ button >
274302 < button
275303 onClick = { ( ) => setViewportSize ( 'tablet' ) }
276- className = { `p-1.5 rounded transition-colors ${
277- viewportSize === 'tablet' ? 'bg-white shadow-sm text-indigo-600' : 'text-gray-500 hover:text-gray-700'
304+ className = { `p-1.5 rounded-md transition-all duration-200 ${
305+ viewportSize === 'tablet' ? 'bg-white shadow-sm text-indigo-600 ring-1 ring-black/5 ' : 'text-gray-400 hover:text-gray-700'
278306 } `}
279307 title = "Tablet View"
280308 >
281309 < Tablet className = "h-4 w-4" />
282310 </ button >
283311 < button
284312 onClick = { ( ) => setViewportSize ( 'mobile' ) }
285- className = { `p-1.5 rounded transition-colors ${
286- viewportSize === 'mobile' ? 'bg-white shadow-sm text-indigo-600' : 'text-gray-500 hover:text-gray-700'
313+ className = { `p-1.5 rounded-md transition-all duration-200 ${
314+ viewportSize === 'mobile' ? 'bg-white shadow-sm text-indigo-600 ring-1 ring-black/5 ' : 'text-gray-400 hover:text-gray-700'
287315 } `}
288316 title = "Mobile View"
289317 >
@@ -292,62 +320,118 @@ const StudioEditor = ({ exampleId, initialJson }: { exampleId: ExampleKey, initi
292320 </ div >
293321 </ div >
294322
295- < div className = "flex-1 overflow-auto p-8 flex justify-center bg-gray-100/50" >
296- < div className = { `${ viewportStyles [ viewportSize ] } transition-all duration-300` } >
297- < div className = "rounded-xl border shadow-sm bg-background p-6 min-h-[500px] h-full ring-1 ring-black/5" >
298- { schema && ! jsonError ? (
299- < SchemaRenderer schema = { schema } />
300- ) : (
301- < div className = "text-center py-12 text-muted-foreground" >
302- { jsonError ? (
303- < div className = "space-y-2" >
304- < p className = "text-red-500 font-semibold" > Invalid JSON</ p >
305- < p className = "text-sm" > Fix the syntax error to see the preview</ p >
306- </ div >
307- ) : (
308- < p > Loading...</ p >
309- ) }
323+ < div
324+ className = "flex-1 overflow-auto p-8 flex justify-center bg-slate-50 relative"
325+ style = { {
326+ backgroundImage : 'radial-gradient(#cbd5e1 1px, transparent 1px)' ,
327+ backgroundSize : '24px 24px'
328+ } }
329+ >
330+ < div className = { `${ viewportStyles [ viewportSize ] } transition-all duration-300 ease-in-out` } >
331+ < div
332+ className = { `
333+ bg-background h-full min-h-[500px]
334+ ${ viewportSize === 'mobile'
335+ ? 'rounded-[3rem] border-[8px] border-slate-800 shadow-2xl'
336+ : viewportSize === 'tablet'
337+ ? 'rounded-[2rem] border-[8px] border-slate-800 shadow-2xl'
338+ : 'rounded-xl border border-gray-200 shadow-xl'
339+ }
340+ ${ viewportSize !== 'desktop' ? 'overflow-hidden' : 'p-6' }
341+ transition-all duration-300
342+ ` }
343+ >
344+ { /* Mobile/Tablet Bar */ }
345+ { viewportSize !== 'desktop' && (
346+ < div className = "h-6 bg-slate-800 w-full absolute top-0 left-0 z-50 flex justify-center items-center" >
347+ < div className = "w-16 h-1 bg-slate-700 rounded-full" > </ div >
310348 </ div >
311349 ) }
350+
351+ < div className = { `h-full w-full ${ viewportSize !== 'desktop' ? 'mt-0 pt-2 bg-white overflow-auto h-[calc(100%-0px)]' : '' } ` } >
352+ { viewportSize !== 'desktop' && < div className = "h-6 w-full flex-shrink-0" > </ div > } { /* Notch spacer */ }
353+
354+ < div className = { viewportSize !== 'desktop' ? 'p-4' : '' } >
355+ { schema && ! jsonError ? (
356+ < SchemaRenderer schema = { schema } />
357+ ) : (
358+ < div className = "text-center py-12 text-muted-foreground flex flex-col items-center justify-center h-full" >
359+ { jsonError ? (
360+ < div className = "space-y-2 p-4 bg-red-50 rounded-lg border border-red-100" >
361+ < p className = "text-red-600 font-semibold flex items-center gap-2" >
362+ < span className = "w-2 h-2 rounded-full bg-red-500" > </ span >
363+ Invalid JSON
364+ </ p >
365+ < p className = "text-xs text-red-500 font-mono text-left" > { jsonError } </ p >
366+ </ div >
367+ ) : (
368+ < div className = "flex flex-col items-center gap-3" >
369+ < div className = "w-8 h-8 border-2 border-indigo-200 border-t-indigo-600 rounded-full animate-spin" > </ div >
370+ < p className = "text-sm text-gray-400" > Rendering...</ p >
371+ </ div >
372+ ) }
373+ </ div >
374+ ) }
375+ </ div >
376+ </ div >
312377 </ div >
313378 </ div >
314379 </ div >
315380 </ div >
316381 ) : (
317382 < div className = "flex h-full overflow-hidden absolute inset-0" >
318383 { /* Code Editor */ }
319- < div className = "w-1/2 h-full border-r flex flex-col relative z-10" >
384+ < div className = "w-1/2 h-full flex flex-col relative z-10 shadow-xl border-r border-[#333] " >
320385 { jsonError && (
321- < div className = "px-4 py-2 bg-red-50 border-b border-red-200 text-red-700 text-sm" >
322- < strong > JSON Error:</ strong > { jsonError }
386+ < div className = "px-4 py-2 bg-red-900/20 border-b border-red-900/50 text-red-400 text-xs font-mono flex items-center gap-2" >
387+ < span className = "w-2 h-2 rounded-full bg-red-500 animate-pulse" > </ span >
388+ < strong > Error:</ strong > { jsonError }
323389 </ div >
324390 ) }
325391
326- < div className = "flex-1 overflow-hidden" >
392+ < div className = "h-9 bg-[#252526] flex items-center px-4 text-xs text-[#969696] select-none border-b border-[#333]" >
393+ < Code2 className = "w-3.5 h-3.5 mr-2" />
394+ < span > schema.json</ span >
395+ </ div >
396+
397+ < div className = "flex-1 overflow-hidden relative bg-[#1e1e1e]" >
327398 < textarea
328399 value = { code }
329400 onChange = { ( e ) => updateCode ( e . target . value ) }
330- className = "w-full h-full p-4 font-mono text-sm resize-none focus:outline-none border-0 bg-background text-foreground "
401+ className = "w-full h-full p-6 font-mono text-sm resize-none focus:outline-none border-0 bg-[#1e1e1e] text-[#d4d4d4] "
331402 spellCheck = { false }
332403 style = { {
333404 tabSize : 2 ,
334- lineHeight : '1.6'
405+ lineHeight : '1.6' ,
406+ fontFamily : '"Menlo", "Monaco", "Courier New", monospace'
335407 } }
336408 />
337409 </ div >
338410 </ div >
339411
340412 { /* Side Preview */ }
341- < div className = "w-1/2 h-full flex flex-col bg-gray-50" >
342- < div className = "flex-1 overflow-auto p-8" >
343- < div className = "max-w-full rounded-lg border shadow-sm bg-background p-6" >
344- { schema && ! jsonError ? (
345- < SchemaRenderer schema = { schema } />
346- ) : (
347- < div className = "text-center py-12 text-muted-foreground" >
348- Invalid Schema
349- </ div >
350- ) }
413+ < div className = "w-1/2 h-full flex flex-col bg-slate-50 relative"
414+ style = { {
415+ backgroundImage : 'radial-gradient(#cbd5e1 1px, transparent 1px)' ,
416+ backgroundSize : '24px 24px'
417+ } }
418+ >
419+ < div className = "flex-1 overflow-auto p-8 flex items-center justify-center" >
420+ < div className = "w-full max-w-xl mx-auto rounded-xl shadow-xl bg-background border ring-1 ring-black/5 overflow-hidden" >
421+ < div className = "h-9 bg-white border-b flex items-center px-3 gap-1.5" >
422+ < div className = "w-3 h-3 rounded-full bg-red-400/80" > </ div >
423+ < div className = "w-3 h-3 rounded-full bg-amber-400/80" > </ div >
424+ < div className = "w-3 h-3 rounded-full bg-green-400/80" > </ div >
425+ </ div >
426+ < div className = "p-6" >
427+ { schema && ! jsonError ? (
428+ < SchemaRenderer schema = { schema } />
429+ ) : (
430+ < div className = "text-center py-12 text-muted-foreground text-sm" >
431+ { jsonError ? 'Waiting for valid JSON...' : 'Rendering...' }
432+ </ div >
433+ ) }
434+ </ div >
351435 </ div >
352436 </ div >
353437 </ div >
0 commit comments