feat: Migrate 4 key organisms to JSON architecture

Migrated EmptyCanvasState, SchemaCodeViewer, ToolbarActions, and AppHeader
from TSX to JSON-based components. These are display-focused organisms with
no complex state management requirements.

Changes:
- Create JSON definitions for 4 organisms in src/components/json-definitions/
- Create TypeScript interfaces in src/lib/json-ui/interfaces/
- Add exports to src/lib/json-ui/json-components.ts
- Update interfaces/index.ts to export new interfaces
- Registry entries already marked as jsonCompatible: true

All organisms are pure JSON components (no custom hooks needed):
- EmptyCanvasState: Display component with optional action buttons
- SchemaCodeViewer: Display component showing JSON schema with tabs
- ToolbarActions: Flex container with conditional toolbar buttons
- AppHeader: Complex header layout with multiple sub-components

Build passing with no errors.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-21 02:28:50 +00:00
parent 53c8a72c0f
commit 4f75409096
10 changed files with 583 additions and 0 deletions

View File

@@ -0,0 +1,146 @@
{
"id": "app-header",
"type": "header",
"bindings": {
"className": { "value": "border-b border-border bg-card" }
},
"children": [
{
"id": "header-stack",
"type": "Stack",
"props": {
"direction": "vertical",
"spacing": "none"
},
"children": [
{
"id": "header-top",
"type": "Stack",
"props": {
"className": "px-4 sm:px-6 py-3 sm:py-4"
},
"children": [
{
"id": "header-flex",
"type": "Flex",
"props": {
"justify": "between",
"align": "center",
"gap": "sm"
},
"children": [
{
"id": "header-left",
"type": "Flex",
"props": {
"align": "center",
"gap": "sm",
"className": "flex-1 min-w-0"
},
"children": [
{
"id": "sidebar-trigger",
"type": "SidebarTrigger"
},
{
"id": "app-branding",
"type": "AppBranding"
},
{
"id": "save-indicator",
"type": "SaveIndicator",
"bindings": {
"lastSaved": { "source": "lastSaved" }
}
}
]
},
{
"id": "header-right",
"type": "Flex",
"props": {
"gap": "xs",
"shrink": true,
"className": "shrink-0"
},
"children": [
{
"id": "project-manager",
"type": "ProjectManager",
"bindings": {
"currentProject": { "source": "currentProject" }
},
"events": {
"onProjectLoad": {
"type": "callback",
"name": "onProjectLoad"
}
}
},
{
"id": "toolbar-actions",
"type": "ToolbarActions",
"bindings": {
"errorCount": { "source": "errorCount" },
"showErrorButton": {
"source": "featureToggles,errorCount",
"transform": "data && data.featureToggles && data.featureToggles.errorRepair && data.errorCount > 0"
}
},
"events": {
"onSearch": {
"type": "callback",
"name": "onSearch"
},
"onShowShortcuts": {
"type": "callback",
"name": "onShowShortcuts"
},
"onGenerateAI": {
"type": "callback",
"name": "onGenerateAI"
},
"onExport": {
"type": "callback",
"name": "onExport"
},
"onPreview": {
"type": "callback",
"name": "onPreview"
},
"onShowErrors": {
"type": "callback",
"name": "onShowErrors"
}
}
}
]
}
]
}
]
},
{
"id": "separator",
"type": "Separator",
"props": {
"className": "opacity-50"
}
},
{
"id": "header-bottom",
"type": "Stack",
"props": {
"className": "px-4 sm:px-6 py-2"
},
"children": [
{
"id": "breadcrumb",
"type": "Breadcrumb"
}
]
}
]
}
]
}

View File

@@ -0,0 +1,94 @@
{
"id": "empty-canvas-state",
"type": "Stack",
"bindings": {
"direction": { "value": "vertical" },
"spacing": { "value": "none" },
"className": { "value": "h-full flex flex-col items-center justify-center p-8 bg-muted/20" }
},
"children": [
{
"id": "empty-state",
"type": "EmptyState",
"props": {
"icon": {
"type": "Icon",
"props": {
"name": "Folder",
"size": 64,
"weight": "duotone"
}
},
"title": "Empty Canvas",
"description": "Start building your UI by dragging components from the left panel, or import an existing schema."
},
"children": [
{
"id": "actions-container",
"type": "Stack",
"props": {
"direction": "horizontal",
"spacing": "md",
"className": "mt-4"
},
"children": [
{
"id": "import-button",
"type": "ActionButton",
"props": {
"icon": {
"type": "Icon",
"props": {
"name": "Folder",
"size": 16
}
},
"label": "Import Schema",
"variant": "outline"
},
"events": {
"onClick": {
"type": "callback",
"name": "onImportSchema"
}
},
"bindings": {
"hidden": {
"source": "!onImportSchema",
"transform": "!data"
}
}
},
{
"id": "add-button",
"type": "ActionButton",
"props": {
"icon": {
"type": "Icon",
"props": {
"name": "Plus",
"size": 16
}
},
"label": "Add Component",
"variant": "default"
},
"events": {
"onClick": {
"type": "callback",
"name": "onAddFirstComponent"
}
},
"bindings": {
"hidden": {
"source": "!onAddFirstComponent",
"transform": "!data"
}
}
}
]
}
]
}
]
}

View File

@@ -0,0 +1,118 @@
{
"id": "schema-code-viewer",
"type": "Stack",
"bindings": {
"direction": { "value": "vertical" },
"spacing": { "value": "none" },
"className": { "value": "h-full flex flex-col bg-card" }
},
"children": [
{
"id": "panel-header",
"type": "PanelHeader",
"props": {
"title": "Schema Output",
"icon": {
"type": "Icon",
"props": {
"name": "Code",
"size": 20,
"weight": "duotone"
}
}
}
},
{
"id": "tabs",
"type": "Tabs",
"props": {
"defaultValue": "json",
"className": "flex-1 flex flex-col"
},
"children": [
{
"id": "tabs-list",
"type": "TabsList",
"props": {
"className": "w-full justify-start px-4 pt-2"
},
"children": [
{
"id": "json-trigger",
"type": "TabsTrigger",
"props": {
"value": "json",
"children": "JSON"
}
},
{
"id": "preview-trigger",
"type": "TabsTrigger",
"props": {
"value": "preview",
"children": "Preview"
}
}
]
},
{
"id": "json-content",
"type": "TabsContent",
"props": {
"value": "json",
"className": "flex-1 m-0 mt-2"
},
"children": [
{
"id": "scroll-area",
"type": "ScrollArea",
"props": {
"className": "h-full"
},
"children": [
{
"id": "code-block",
"type": "Code",
"bindings": {
"children": {
"source": "schema",
"transform": "data ? JSON.stringify(data, null, 2) : ''"
},
"className": { "value": "p-4 text-xs font-mono text-foreground" }
}
}
]
}
]
},
{
"id": "preview-content",
"type": "TabsContent",
"props": {
"value": "preview",
"className": "flex-1 m-0 mt-2"
},
"children": [
{
"id": "preview-text",
"type": "Stack",
"props": {
"className": "p-4"
},
"children": [
{
"id": "preview-message",
"type": "Text",
"props": {
"variant": "muted",
"children": "Live preview coming soon"
}
}
]
}
]
}
]
}
]
}

View File

@@ -0,0 +1,172 @@
{
"id": "toolbar-actions",
"type": "Flex",
"bindings": {
"gap": { "value": "xs" },
"shrink": { "value": true },
"className": { "value": "shrink-0" }
},
"children": [
{
"id": "search-button",
"type": "ToolbarButton",
"props": {
"icon": {
"type": "Icon",
"props": {
"name": "MagnifyingGlass",
"size": 18
}
},
"label": "Search (Ctrl+K)",
"data-search-trigger": true
},
"events": {
"onClick": {
"type": "callback",
"name": "onSearch"
}
}
},
{
"id": "error-button-wrapper",
"type": "Stack",
"bindings": {
"hidden": {
"source": "showErrorButton,errorCount",
"transform": "!(data.showErrorButton && data.errorCount > 0)"
}
},
"children": [
{
"id": "error-button",
"type": "ToolbarButton",
"props": {
"icon": {
"type": "Icon",
"props": {
"name": "Wrench",
"size": 18
}
},
"variant": "outline",
"className": "border-destructive text-destructive hover:bg-destructive hover:text-destructive-foreground relative"
},
"bindings": {
"label": {
"source": "errorCount",
"transform": "data ? `${data} ${data === 1 ? 'Error' : 'Errors'}` : 'Errors'"
}
},
"events": {
"onClick": {
"type": "callback",
"name": "onShowErrors"
}
}
},
{
"id": "error-badge",
"type": "ErrorBadge",
"bindings": {
"count": { "source": "errorCount" }
},
"props": {
"size": "sm"
}
}
]
},
{
"id": "preview-button",
"type": "ToolbarButton",
"bindings": {
"hidden": {
"source": "!onPreview",
"transform": "!data"
}
},
"props": {
"icon": {
"type": "Icon",
"props": {
"name": "Eye",
"size": 18
}
},
"label": "Preview (Ctrl+P)",
"variant": "outline"
},
"events": {
"onClick": {
"type": "callback",
"name": "onPreview"
}
}
},
{
"id": "shortcuts-button",
"type": "ToolbarButton",
"props": {
"icon": {
"type": "Icon",
"props": {
"name": "Keyboard",
"size": 18
}
},
"label": "Keyboard Shortcuts (Ctrl+/)",
"variant": "ghost",
"className": "hidden sm:flex"
},
"events": {
"onClick": {
"type": "callback",
"name": "onShowShortcuts"
}
}
},
{
"id": "ai-button",
"type": "ToolbarButton",
"props": {
"icon": {
"type": "Icon",
"props": {
"name": "Sparkle",
"size": 18,
"weight": "duotone"
}
},
"label": "AI Generate (Ctrl+Shift+G)"
},
"events": {
"onClick": {
"type": "callback",
"name": "onGenerateAI"
}
}
},
{
"id": "export-button",
"type": "ToolbarButton",
"props": {
"icon": {
"type": "Icon",
"props": {
"name": "Download",
"size": 18
}
},
"label": "Export Project (Ctrl+E)",
"variant": "default"
},
"events": {
"onClick": {
"type": "callback",
"name": "onExport"
}
}
}
]
}

View File

@@ -0,0 +1,17 @@
import type { FeatureToggles, Project } from '@/types/project'
export interface AppHeaderProps {
activeTab: string
onTabChange: (tab: string) => void
featureToggles: FeatureToggles
errorCount: number
lastSaved: number | null
currentProject: Project
onProjectLoad: (project: Project) => void
onSearch: () => void
onShowShortcuts: () => void
onGenerateAI: () => void
onExport: () => void
onPreview?: () => void
onShowErrors: () => void
}

View File

@@ -0,0 +1,4 @@
export interface EmptyCanvasStateProps {
onAddFirstComponent?: () => void
onImportSchema?: () => void
}

View File

@@ -122,3 +122,7 @@ export * from './data-source-card'
export * from './code-explanation-dialog'
export * from './component-palette'
export * from './canvas-renderer'
export * from './empty-canvas-state'
export * from './schema-code-viewer'
export * from './toolbar-actions'
export * from './app-header'

View File

@@ -0,0 +1,6 @@
import type { UIComponent } from '@/types/json-ui'
export interface SchemaCodeViewerProps {
components: UIComponent[]
schema: any
}

View File

@@ -0,0 +1,10 @@
export interface ToolbarActionsProps {
onSearch: () => void
onShowShortcuts: () => void
onGenerateAI: () => void
onExport: () => void
onPreview?: () => void
onShowErrors?: () => void
errorCount?: number
showErrorButton?: boolean
}

View File

@@ -131,6 +131,10 @@ import type {
CodeExplanationDialogProps,
ComponentPaletteProps,
CanvasRendererProps,
EmptyCanvasStateProps,
SchemaCodeViewerProps,
ToolbarActionsProps,
AppHeaderProps,
} from './interfaces'
// Import JSON definitions
@@ -258,6 +262,10 @@ import dataSourceCardDef from '@/components/json-definitions/data-source-card.js
import codeExplanationDialogDef from '@/components/json-definitions/code-explanation-dialog.json'
import componentPaletteDef from '@/components/json-definitions/component-palette.json'
import canvasRendererDef from '@/components/json-definitions/canvas-renderer.json'
import emptyCanvasStateDef from '@/components/json-definitions/empty-canvas-state.json'
import schemaCodeViewerDef from '@/components/json-definitions/schema-code-viewer.json'
import toolbarActionsDef from '@/components/json-definitions/toolbar-actions.json'
import appHeaderDef from '@/components/json-definitions/app-header.json'
// Create pure JSON components (no hooks)
export const BindingIndicator = createJsonComponent<BindingIndicatorProps>(bindingIndicatorDef)
@@ -363,6 +371,10 @@ export const DataSourceCard = createJsonComponent<DataSourceCardProps>(dataSourc
export const CodeExplanationDialog = createJsonComponent<CodeExplanationDialogProps>(codeExplanationDialogDef)
export const ComponentPalette = createJsonComponent<ComponentPaletteProps>(componentPaletteDef)
export const CanvasRenderer = createJsonComponent<CanvasRendererProps>(canvasRendererDef)
export const EmptyCanvasState = createJsonComponent<EmptyCanvasStateProps>(emptyCanvasStateDef)
export const SchemaCodeViewer = createJsonComponent<SchemaCodeViewerProps>(schemaCodeViewerDef)
export const ToolbarActions = createJsonComponent<ToolbarActionsProps>(toolbarActionsDef)
export const AppHeader = createJsonComponent<AppHeaderProps>(appHeaderDef)
// Create JSON components with hooks
export const SaveIndicator = createJsonComponentWithHooks<SaveIndicatorProps>(saveIndicatorDef, {