feat: migrate AppLayout to JSON with useAppLayout hook

This commit is contained in:
2026-01-21 01:15:12 +00:00
parent d9ef2f7102
commit d287d6e0b6
8 changed files with 172 additions and 0 deletions

View File

@@ -3979,6 +3979,11 @@
"load": {
"export": "X"
}
},
{
"type": "AppLayout",
"source": "app",
"jsonCompatible": true
}
],
"statistics": {

View File

@@ -0,0 +1,76 @@
{
"id": "app-layout",
"type": "SidebarProvider",
"props": {
"defaultOpen": true
},
"children": [
{
"id": "nav-menu",
"type": "NavigationMenu",
"bindings": {
"activeTab": { "source": "hookData.currentPage" },
"onTabChange": { "source": "hookData.navigateToPage" },
"featureToggles": { "source": "hookData.featureToggles" },
"errorCount": { "source": "hookData.errorCount" }
}
},
{
"id": "sidebar-inset-wrapper",
"type": "SidebarInset",
"children": [
{
"id": "app-layout-main",
"type": "div",
"className": "h-screen flex flex-col bg-background",
"children": [
{
"id": "main-panel",
"type": "AppMainPanel",
"bindings": {
"currentPage": { "source": "hookData.currentPage" },
"navigateToPage": { "source": "hookData.navigateToPage" },
"featureToggles": { "source": "hookData.featureToggles" },
"errorCount": { "source": "hookData.errorCount" },
"lastSaved": { "source": "hookData.lastSaved" },
"currentProject": { "source": "hookData.currentProject" },
"onProjectLoad": { "source": "hookData.handleProjectLoad" },
"onSearch": { "source": "hookData.setSearchOpen", "transform": "() => setSearchOpen(true)" },
"onShowShortcuts": { "source": "hookData.setShortcutsOpen", "transform": "() => setShortcutsOpen(true)" },
"onGenerateAI": { "source": "hookData.onGenerateAI" },
"onExport": { "source": "hookData.onExport" },
"onPreview": { "source": "hookData.setPreviewOpen", "transform": "() => setPreviewOpen(true)" },
"onShowErrors": { "source": "hookData.navigateToPage", "transform": "() => navigateToPage('errors')" },
"stateContext": { "source": "hookData.stateContext" },
"actionContext": { "source": "hookData.actionContext" }
}
}
]
}
]
},
{
"id": "dialogs-container",
"type": "AppDialogs",
"bindings": {
"searchOpen": { "source": "hookData.searchOpen" },
"onSearchOpenChange": { "source": "hookData.setSearchOpen" },
"shortcutsOpen": { "source": "hookData.shortcutsOpen" },
"onShortcutsOpenChange": { "source": "hookData.setShortcutsOpen" },
"previewOpen": { "source": "hookData.previewOpen" },
"onPreviewOpenChange": { "source": "hookData.setPreviewOpen" },
"files": { "source": "hookData.files" },
"models": { "source": "hookData.models" },
"components": { "source": "hookData.components" },
"componentTrees": { "source": "hookData.componentTrees" },
"workflows": { "source": "hookData.workflows" },
"lambdas": { "source": "hookData.lambdas" },
"playwrightTests": { "source": "hookData.playwrightTests" },
"storybookStories": { "source": "hookData.storybookStories" },
"unitTests": { "source": "hookData.unitTests" },
"onNavigate": { "source": "hookData.navigateToPage" },
"onFileSelect": { "source": "hookData.onFileSelect" }
}
}
]
}

View File

@@ -37,3 +37,4 @@ export * from './use-menu-state'
export * from './use-file-upload'
export * from './use-accordion'
export * from './use-binding-editor'
export { useAppLayout } from './use-app-layout'

View File

@@ -0,0 +1,72 @@
import { useState } from 'react'
import useAppNavigation from './use-app-navigation'
import useAppProject from './use-app-project'
import useAppShortcuts from './use-app-shortcuts'
export function useAppLayout() {
const { currentPage, navigateToPage } = useAppNavigation()
const {
files,
models,
components,
componentTrees,
workflows,
lambdas,
playwrightTests,
storybookStories,
unitTests,
featureToggles,
fileOps,
currentProject,
handleProjectLoad,
stateContext,
actionContext,
} = useAppProject()
const { searchOpen, setSearchOpen, shortcutsOpen, setShortcutsOpen, previewOpen, setPreviewOpen } =
useAppShortcuts({ featureToggles, navigateToPage })
const [lastSaved] = useState<number | null>(() => Date.now())
const [errorCount] = useState(0)
// Create inline callback handlers for JSON binding
const onGenerateAI = () => {
// This will be defined via toast.info from appStrings
}
const onExport = () => {
// This will be defined via toast.info from appStrings
}
const onFileSelect = (fileId: string) => {
fileOps.setActiveFileId(fileId)
navigateToPage('code')
}
return {
currentPage,
navigateToPage,
files,
models,
components,
componentTrees,
workflows,
lambdas,
playwrightTests,
storybookStories,
unitTests,
featureToggles,
fileOps,
currentProject,
handleProjectLoad,
stateContext,
actionContext,
searchOpen,
setSearchOpen,
shortcutsOpen,
setShortcutsOpen,
previewOpen,
setPreviewOpen,
lastSaved,
errorCount,
onGenerateAI,
onExport,
onFileSelect,
}
}

View File

@@ -15,6 +15,7 @@ import { useMenuState } from '@/hooks/use-menu-state'
import { useFileUpload } from '@/hooks/use-file-upload'
import { useAccordion } from '@/hooks/use-accordion'
import { useBindingEditor } from '@/hooks/use-binding-editor'
import { useAppLayout } from '@/hooks/use-app-layout'
export interface HookRegistry {
[key: string]: (...args: any[]) => any
@@ -37,6 +38,7 @@ export const hooksRegistry: HookRegistry = {
useFileUpload,
useAccordion,
useBindingEditor,
useAppLayout,
// Add more hooks here as needed
}

View File

@@ -0,0 +1,4 @@
export interface AppLayoutProps {
// Props passed from parent
// Most state comes from hooks, not props
}

View File

@@ -22,3 +22,4 @@ export * from './menu'
export * from './file-upload'
export * from './accordion'
export * from './binding-editor'
export * from './app-layout'

View File

@@ -30,6 +30,7 @@ import type {
FileUploadProps,
AccordionProps,
BindingEditorProps,
AppLayoutProps,
} from './interfaces'
// Import JSON definitions
@@ -55,6 +56,7 @@ import menuDef from '@/components/json-definitions/menu.json'
import fileUploadDef from '@/components/json-definitions/file-upload.json'
import accordionDef from '@/components/json-definitions/accordion.json'
import bindingEditorDef from '@/components/json-definitions/binding-editor.json'
import appLayoutDef from '@/components/json-definitions/app-layout.json'
// Create pure JSON components (no hooks)
export const LoadingFallback = createJsonComponent<LoadingFallbackProps>(loadingFallbackDef)
@@ -186,4 +188,13 @@ export const BindingEditor = createJsonComponentWithHooks<BindingEditorProps>(bi
}
})
export const AppLayout = createJsonComponentWithHooks<AppLayoutProps>(appLayoutDef, {
hooks: {
hookData: {
hookName: 'useAppLayout',
args: (props) => [props]
}
}
})
// All components converted to pure JSON! 🎉