From 32dd4d0eacbd10c97baa57ba7d887fa86a62a78e Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 18 Jan 2026 18:44:07 +0000 Subject: [PATCH 1/3] Update showcase config metadata and loading --- src/components/JSONUIShowcase.tsx | 25 ++++++++++--------------- src/config/ui-examples/showcase.json | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/components/JSONUIShowcase.tsx b/src/components/JSONUIShowcase.tsx index cf2ed96..ae51ab6 100644 --- a/src/components/JSONUIShowcase.tsx +++ b/src/components/JSONUIShowcase.tsx @@ -1,24 +1,11 @@ import { useMemo, useState } from 'react' import showcaseCopy from '@/config/ui-examples/showcase.json' -import dashboardExample from '@/config/ui-examples/dashboard.json' -import formExample from '@/config/ui-examples/form.json' -import tableExample from '@/config/ui-examples/table.json' -import listTableTimelineExample from '@/config/ui-examples/list-table-timeline.json' -import settingsExample from '@/config/ui-examples/settings.json' import { FileCode, ChartBar, ListBullets, Table, Gear, Clock } from '@phosphor-icons/react' import { ShowcaseHeader } from '@/components/json-ui-showcase/ShowcaseHeader' import { ShowcaseTabs } from '@/components/json-ui-showcase/ShowcaseTabs' import { ShowcaseFooter } from '@/components/json-ui-showcase/ShowcaseFooter' import { ShowcaseExample } from '@/components/json-ui-showcase/types' -const exampleConfigs = { - dashboard: dashboardExample, - form: formExample, - table: tableExample, - 'list-table-timeline': listTableTimelineExample, - settings: settingsExample, -} - const exampleIcons = { ChartBar, ListBullets, @@ -27,14 +14,22 @@ const exampleIcons = { Gear, } +const configModules = import.meta.glob('/src/config/ui-examples/*.json', { eager: true }) + +const resolveExampleConfig = (configPath: string) => { + const moduleEntry = configModules[configPath] as { default: ShowcaseExample['config'] } | undefined + + return moduleEntry?.default ?? {} +} + export function JSONUIShowcase() { const [selectedExample, setSelectedExample] = useState(showcaseCopy.defaultExampleKey) const [showJSON, setShowJSON] = useState(false) const examples = useMemo(() => { return showcaseCopy.examples.map((example) => { - const icon = exampleIcons[example.icon as keyof typeof exampleIcons] || FileCode - const config = exampleConfigs[example.configKey as keyof typeof exampleConfigs] + const icon = exampleIcons[example.iconId as keyof typeof exampleIcons] || FileCode + const config = resolveExampleConfig(example.configPath) return { key: example.key, diff --git a/src/config/ui-examples/showcase.json b/src/config/ui-examples/showcase.json index f1b9898..361de6e 100644 --- a/src/config/ui-examples/showcase.json +++ b/src/config/ui-examples/showcase.json @@ -15,36 +15,36 @@ "key": "dashboard", "name": "Dashboard", "description": "Complete dashboard with stats, activity feed, and quick actions", - "icon": "ChartBar", - "configKey": "dashboard" + "iconId": "ChartBar", + "configPath": "/src/config/ui-examples/dashboard.json" }, { "key": "form", "name": "Form", "description": "Dynamic form with validation and data binding", - "icon": "ListBullets", - "configKey": "form" + "iconId": "ListBullets", + "configPath": "/src/config/ui-examples/form.json" }, { "key": "table", "name": "Data Table", "description": "Interactive table with row actions and looping", - "icon": "Table", - "configKey": "table" + "iconId": "Table", + "configPath": "/src/config/ui-examples/table.json" }, { "key": "bindings", "name": "Bindings", "description": "List, table, and timeline bindings with shared data sources", - "icon": "Clock", - "configKey": "list-table-timeline" + "iconId": "Clock", + "configPath": "/src/config/ui-examples/list-table-timeline.json" }, { "key": "settings", "name": "Settings", "description": "Tabbed settings panel with switches and selections", - "icon": "Gear", - "configKey": "settings" + "iconId": "Gear", + "configPath": "/src/config/ui-examples/settings.json" } ], "footer": { From 4cb9c017482d8d075409e6b83f32c16b098c7ed7 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 18 Jan 2026 18:44:59 +0000 Subject: [PATCH 2/3] Add sourceRoots config for component registry --- json-components-registry.json | 8 ++++++ schemas/json-components-registry-schema.json | 9 +++++++ src/lib/json-ui/component-registry.ts | 26 +++++++++++--------- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/json-components-registry.json b/json-components-registry.json index 575f4c0..02deb09 100644 --- a/json-components-registry.json +++ b/json-components-registry.json @@ -12,6 +12,14 @@ "data": "Data display and visualization components", "custom": "Custom domain-specific components" }, + "sourceRoots": { + "atoms": ["@/components/atoms/*.tsx"], + "molecules": ["@/components/molecules/*.tsx"], + "organisms": ["@/components/organisms/*.tsx"], + "ui": ["@/components/ui/**/*.{ts,tsx}"], + "wrappers": ["@/lib/json-ui/wrappers/*.tsx"], + "icons": [] + }, "components": [ { "type": "ActionCard", diff --git a/schemas/json-components-registry-schema.json b/schemas/json-components-registry-schema.json index 9b9d424..a4ebcae 100644 --- a/schemas/json-components-registry-schema.json +++ b/schemas/json-components-registry-schema.json @@ -22,6 +22,15 @@ "type": "string" } }, + "sourceRoots": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, "components": { "type": "array", "items": { diff --git a/src/lib/json-ui/component-registry.ts b/src/lib/json-ui/component-registry.ts index d18b342..1f51c00 100644 --- a/src/lib/json-ui/component-registry.ts +++ b/src/lib/json-ui/component-registry.ts @@ -24,6 +24,7 @@ interface JsonRegistryEntry { interface JsonComponentRegistry { components?: JsonRegistryEntry[] + sourceRoots?: Record } export interface DeprecatedComponentInfo { @@ -32,6 +33,15 @@ export interface DeprecatedComponentInfo { } const jsonRegistry = jsonComponentsRegistry as JsonComponentRegistry +const sourceRoots = jsonRegistry.sourceRoots ?? {} +const moduleMapsBySource = Object.fromEntries( + Object.entries(sourceRoots).map(([source, patterns]) => { + if (!patterns || patterns.length === 0) { + return [source, {}] + } + return [source, import.meta.glob(patterns, { eager: true })] + }) +) as Record> const getRegistryEntryKey = (entry: JsonRegistryEntry): string | undefined => entry.name ?? entry.type @@ -89,17 +99,11 @@ const buildComponentMapFromModules = ( }, {}) } -const atomModules = import.meta.glob('@/components/atoms/*.tsx', { eager: true }) -const moleculeModules = import.meta.glob('@/components/molecules/*.tsx', { eager: true }) -const organismModules = import.meta.glob('@/components/organisms/*.tsx', { eager: true }) -const uiModules = import.meta.glob('@/components/ui/**/*.{ts,tsx}', { eager: true }) -const wrapperModules = import.meta.glob('@/lib/json-ui/wrappers/*.tsx', { eager: true }) - -const atomComponentMap = buildComponentMapFromModules(atomModules) -const moleculeComponentMap = buildComponentMapFromModules(moleculeModules) -const organismComponentMap = buildComponentMapFromModules(organismModules) -const uiComponentMap = buildComponentMapFromModules(uiModules) -const wrapperComponentMap = buildComponentMapFromModules(wrapperModules) +const atomComponentMap = buildComponentMapFromModules(moduleMapsBySource.atoms ?? {}) +const moleculeComponentMap = buildComponentMapFromModules(moduleMapsBySource.molecules ?? {}) +const organismComponentMap = buildComponentMapFromModules(moduleMapsBySource.organisms ?? {}) +const uiComponentMap = buildComponentMapFromModules(moduleMapsBySource.ui ?? {}) +const wrapperComponentMap = buildComponentMapFromModules(moduleMapsBySource.wrappers ?? {}) const iconComponentMap = buildComponentMapFromExports(PhosphorIcons) const sourceAliases: Record> = { From b8dc6f38e6fbb177063e9e1593bfa6b12afb80fd Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 18 Jan 2026 18:46:02 +0000 Subject: [PATCH 3/3] Generate JSON UI component types --- json-components-registry.json | 54 +++++ package.json | 4 +- scripts/generate-json-ui-component-types.ts | 50 ++++ scripts/validate-supported-components.cjs | 12 +- src/types/json-ui-component-types.ts | 249 ++++++++++++++++++++ src/types/json-ui.ts | 29 +-- 6 files changed, 362 insertions(+), 36 deletions(-) create mode 100644 scripts/generate-json-ui-component-types.ts create mode 100644 src/types/json-ui-component-types.ts diff --git a/json-components-registry.json b/json-components-registry.json index 575f4c0..2a2f043 100644 --- a/json-components-registry.json +++ b/json-components-registry.json @@ -13,6 +13,60 @@ "custom": "Custom domain-specific components" }, "components": [ + { + "type": "div", + "name": "div", + "category": "layout", + "canHaveChildren": true, + "description": "Generic block container", + "status": "supported", + "source": "primitive" + }, + { + "type": "section", + "name": "section", + "category": "layout", + "canHaveChildren": true, + "description": "Semantic section container", + "status": "supported", + "source": "primitive" + }, + { + "type": "article", + "name": "article", + "category": "layout", + "canHaveChildren": true, + "description": "Semantic article container", + "status": "supported", + "source": "primitive" + }, + { + "type": "header", + "name": "header", + "category": "layout", + "canHaveChildren": true, + "description": "Semantic header container", + "status": "supported", + "source": "primitive" + }, + { + "type": "footer", + "name": "footer", + "category": "layout", + "canHaveChildren": true, + "description": "Semantic footer container", + "status": "supported", + "source": "primitive" + }, + { + "type": "main", + "name": "main", + "category": "layout", + "canHaveChildren": true, + "description": "Semantic main container", + "status": "supported", + "source": "primitive" + }, { "type": "ActionCard", "name": "ActionCard", diff --git a/package.json b/package.json index 9d34649..298a881 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "scripts": { "dev": "vite", "kill": "fuser -k 5000/tcp", - "prebuild": "mkdir -p /tmp/dist || true", + "predev": "npm run components:generate-types", + "prebuild": "npm run components:generate-types && mkdir -p /tmp/dist || true", "build": "tsc -b --noCheck && vite build", "lint": "eslint . --fix && npm run lint:schemas", "lint:check": "eslint . && npm run lint:schemas", @@ -24,6 +25,7 @@ "pages:generate": "node scripts/generate-page.js", "schemas:validate": "tsx scripts/validate-json-schemas.ts", "components:list": "node scripts/list-json-components.cjs", + "components:generate-types": "tsx scripts/generate-json-ui-component-types.ts", "components:scan": "node scripts/scan-and-update-registry.cjs", "components:validate": "node scripts/validate-supported-components.cjs" }, diff --git a/scripts/generate-json-ui-component-types.ts b/scripts/generate-json-ui-component-types.ts new file mode 100644 index 0000000..590b25f --- /dev/null +++ b/scripts/generate-json-ui-component-types.ts @@ -0,0 +1,50 @@ +import fs from 'fs' +import path from 'path' +import { fileURLToPath } from 'url' + +interface RegistryComponent { + type?: string + name?: string + export?: string +} + +interface RegistryData { + components?: RegistryComponent[] +} + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const rootDir = path.resolve(__dirname, '..') +const registryPath = path.join(rootDir, 'json-components-registry.json') +const outputPath = path.join(rootDir, 'src/types/json-ui-component-types.ts') + +const registryData = JSON.parse(fs.readFileSync(registryPath, 'utf8')) as RegistryData +const components = registryData.components ?? [] + +const seen = new Set() +const componentTypes = components.flatMap((component) => { + const typeName = component.type ?? component.name ?? component.export + if (!typeName || typeof typeName !== 'string') { + throw new Error('Registry component is missing a valid type/name/export entry.') + } + if (seen.has(typeName)) { + return [] + } + seen.add(typeName) + return [typeName] +}) + +const lines = [ + '// This file is auto-generated by scripts/generate-json-ui-component-types.ts.', + '// Do not edit this file directly.', + '', + 'export const jsonUIComponentTypes = [', + ...componentTypes.map((typeName) => ` ${JSON.stringify(typeName)},`), + '] as const', + '', + 'export type JSONUIComponentType = typeof jsonUIComponentTypes[number]', + '', +] + +fs.writeFileSync(outputPath, `${lines.join('\n')}`) + +console.log(`✅ Wrote ${componentTypes.length} component types to ${outputPath}`) diff --git a/scripts/validate-supported-components.cjs b/scripts/validate-supported-components.cjs index 6a4d643..51b58bb 100644 --- a/scripts/validate-supported-components.cjs +++ b/scripts/validate-supported-components.cjs @@ -4,7 +4,7 @@ const path = require('path') const rootDir = path.resolve(__dirname, '..') const registryPath = path.join(rootDir, 'json-components-registry.json') const definitionsPath = path.join(rootDir, 'src/lib/component-definitions.json') -const componentTypesPath = path.join(rootDir, 'src/types/json-ui.ts') +const componentTypesPath = path.join(rootDir, 'src/types/json-ui-component-types.ts') const uiRegistryPath = path.join(rootDir, 'src/lib/json-ui/component-registry.ts') const atomIndexPath = path.join(rootDir, 'src/components/atoms/index.ts') const moleculeIndexPath = path.join(rootDir, 'src/components/molecules/index.ts') @@ -21,16 +21,10 @@ const componentDefinitions = readJson(definitionsPath) const definitionTypes = new Set(componentDefinitions.map((def) => def.type)) const componentTypesContent = readText(componentTypesPath) -const componentTypesStart = componentTypesContent.indexOf('export type ComponentType') -const componentTypesEnd = componentTypesContent.indexOf('export type ActionType') -if (componentTypesStart === -1 || componentTypesEnd === -1) { - throw new Error('Unable to locate ComponentType union in src/types/json-ui.ts') -} -const componentTypesBlock = componentTypesContent.slice(componentTypesStart, componentTypesEnd) const componentTypeSet = new Set() -const componentTypeRegex = /'([^']+)'/g +const componentTypeRegex = /"([^"]+)"/g let match -while ((match = componentTypeRegex.exec(componentTypesBlock)) !== null) { +while ((match = componentTypeRegex.exec(componentTypesContent)) !== null) { componentTypeSet.add(match[1]) } diff --git a/src/types/json-ui-component-types.ts b/src/types/json-ui-component-types.ts new file mode 100644 index 0000000..5e5eb14 --- /dev/null +++ b/src/types/json-ui-component-types.ts @@ -0,0 +1,249 @@ +// This file is auto-generated by scripts/generate-json-ui-component-types.ts. +// Do not edit this file directly. + +export const jsonUIComponentTypes = [ + "div", + "section", + "article", + "header", + "footer", + "main", + "ActionCard", + "AlertDialog", + "Card", + "CodeExplanationDialog", + "CompletionCard", + "ComponentBindingDialog", + "ComponentBindingDialogWrapper", + "Container", + "DataSourceCard", + "DataSourceEditorDialog", + "DataSourceEditorDialogWrapper", + "Dialog", + "Drawer", + "Flex", + "GlowCard", + "Grid", + "HoverCard", + "Modal", + "ResponsiveGrid", + "Section", + "Stack", + "TipsCard", + "TreeCard", + "TreeFormDialog", + "ActionButton", + "Button", + "ButtonGroup", + "Checkbox", + "ConfirmButton", + "CopyButton", + "DatePicker", + "FileUpload", + "FilterInput", + "Form", + "IconButton", + "Input", + "InputOtp", + "NumberInput", + "PasswordInput", + "QuickActionButton", + "Radio", + "RadioGroup", + "RangeSlider", + "Select", + "Slider", + "Switch", + "TextArea", + "Toggle", + "ToggleGroup", + "ToolbarButton", + "ActionIcon", + "Avatar", + "AvatarGroup", + "Badge", + "CircularProgress", + "Code", + "Divider", + "FileIcon", + "Heading", + "HelperText", + "IconText", + "IconWrapper", + "Image", + "Label", + "Progress", + "ProgressBar", + "SchemaCodeViewer", + "Separator", + "Skeleton", + "Spinner", + "Tag", + "Text", + "Textarea", + "TextGradient", + "TextHighlight", + "TreeIcon", + "ArrowLeft", + "ArrowRight", + "Check", + "X", + "Plus", + "Minus", + "Search", + "Filter", + "Download", + "Upload", + "Edit", + "Trash", + "Eye", + "EyeOff", + "ChevronUp", + "ChevronDown", + "ChevronLeft", + "ChevronRight", + "Settings", + "User", + "Bell", + "Mail", + "Calendar", + "Clock", + "Star", + "Heart", + "Share", + "Link", + "Copy", + "Save", + "RefreshCw", + "AlertCircle", + "Info", + "HelpCircle", + "Home", + "Menu", + "MoreVertical", + "MoreHorizontal", + "Breadcrumb", + "ContextMenu", + "DropdownMenu", + "FileTabs", + "Menubar", + "NavigationGroupHeader", + "NavigationItem", + "NavigationMenu", + "TabIcon", + "Tabs", + "Alert", + "CountBadge", + "DataSourceBadge", + "EmptyCanvasState", + "EmptyEditorState", + "EmptyMessage", + "EmptyState", + "EmptyStateIcon", + "ErrorBadge", + "GitHubBuildStatus", + "GitHubBuildStatusWrapper", + "InfoBox", + "LabelWithBadge", + "LoadingFallback", + "LoadingSpinner", + "LoadingState", + "Notification", + "SchemaEditorStatusBar", + "SeedDataStatus", + "StatusBadge", + "StatusIcon", + "Chart", + "DataList", + "DataSourceManager", + "DataTable", + "KeyValue", + "LazyBarChart", + "LazyBarChartWrapper", + "LazyD3BarChart", + "LazyD3BarChartWrapper", + "LazyLineChart", + "LazyLineChartWrapper", + "List", + "ListItem", + "MetricCard", + "MetricDisplay", + "SeedDataManager", + "SeedDataManagerWrapper", + "StatCard", + "Table", + "TableHeader", + "TableBody", + "TableRow", + "TableCell", + "TableHead", + "Timeline", + "TreeListHeader", + "TreeListPanel", + "Accordion", + "ActionBar", + "AppBranding", + "AppHeader", + "AppLogo", + "AspectRatio", + "BindingEditor", + "BindingIndicator", + "CanvasRenderer", + "Carousel", + "Chip", + "Collapsible", + "ColorSwatch", + "Command", + "CommandPalette", + "ComponentPalette", + "ComponentPaletteItem", + "ComponentTree", + "ComponentTreeWrapper", + "ComponentTreeNode", + "DataCard", + "DetailRow", + "Dot", + "EditorActions", + "EditorToolbar", + "InfoPanel", + "JSONUIShowcase", + "Kbd", + "LazyInlineMonacoEditor", + "LazyMonacoEditor", + "LiveIndicator", + "MonacoEditorPanel", + "PageHeader", + "PageHeaderContent", + "Pagination", + "PanelHeader", + "Popover", + "PropertyEditor", + "PropertyEditorField", + "Pulse", + "Rating", + "Resizable", + "SaveIndicator", + "SaveIndicatorWrapper", + "SchemaEditorCanvas", + "SchemaEditorLayout", + "SchemaEditorPropertiesPanel", + "SchemaEditorSidebar", + "SchemaEditorToolbar", + "ScrollArea", + "SearchBar", + "SearchInput", + "Sheet", + "Sidebar", + "Sonner", + "Spacer", + "Sparkle", + "StepIndicator", + "Stepper", + "StorageSettings", + "StorageSettingsWrapper", + "Timestamp", + "ToolbarActions", + "Tooltip", +] as const + +export type JSONUIComponentType = typeof jsonUIComponentTypes[number] diff --git a/src/types/json-ui.ts b/src/types/json-ui.ts index 81f59b8..6fd04c6 100644 --- a/src/types/json-ui.ts +++ b/src/types/json-ui.ts @@ -1,29 +1,6 @@ -export type ComponentType = - | 'div' | 'section' | 'article' | 'header' | 'footer' | 'main' - | 'Button' | 'Card' | 'CardHeader' | 'CardTitle' | 'CardDescription' | 'CardContent' | 'CardFooter' - | 'Input' | 'TextArea' | 'Textarea' | 'Select' | 'Checkbox' | 'Radio' | 'Switch' | 'Slider' | 'NumberInput' | 'DatePicker' | 'FileUpload' - | 'Badge' | 'Progress' | 'Separator' | 'Tabs' | 'TabsContent' | 'TabsList' | 'TabsTrigger' | 'Dialog' - | 'Text' | 'Heading' | 'Label' | 'List' | 'ListItem' | 'Grid' | 'Stack' | 'Flex' | 'Container' - | 'Link' | 'Breadcrumb' | 'Image' | 'Avatar' | 'Code' | 'Tag' | 'Spinner' | 'Skeleton' - | 'CircularProgress' | 'Divider' | 'ProgressBar' - | 'Alert' | 'InfoBox' | 'EmptyState' | 'StatusBadge' - | 'ErrorBadge' | 'Notification' | 'StatusIcon' - | 'Table' | 'TableHeader' | 'TableBody' | 'TableRow' | 'TableCell' | 'TableHead' - | 'KeyValue' | 'StatCard' | 'DataCard' | 'SearchInput' | 'ActionBar' - | 'DataList' | 'DataTable' | 'MetricCard' | 'Timeline' - | 'LazyBarChart' | 'LazyLineChart' | 'LazyD3BarChart' | 'SeedDataManager' - | 'SaveIndicator' | 'StorageSettings' - | 'AppBranding' | 'LabelWithBadge' | 'NavigationGroupHeader' | 'EmptyEditorState' | 'LoadingFallback' | 'LoadingState' - | 'CodeExplanationDialog' | 'ComponentBindingDialog' | 'DataSourceCard' | 'DataSourceEditorDialog' | 'TreeCard' | 'TreeFormDialog' - | 'ToolbarButton' - | 'SchemaCodeViewer' - | 'FileTabs' | 'NavigationItem' | 'NavigationMenu' - | 'EmptyCanvasState' | 'SchemaEditorStatusBar' - | 'DataSourceManager' | 'TreeListHeader' | 'TreeListPanel' - | 'AppHeader' | 'BindingEditor' | 'CanvasRenderer' | 'ComponentPalette' | 'ComponentTree' | 'EditorActions' - | 'EditorToolbar' | 'JSONUIShowcase' | 'LazyInlineMonacoEditor' | 'LazyMonacoEditor' | 'MonacoEditorPanel' - | 'PageHeaderContent' | 'PropertyEditor' | 'SchemaEditorCanvas' | 'SchemaEditorLayout' - | 'SchemaEditorPropertiesPanel' | 'SchemaEditorSidebar' | 'SchemaEditorToolbar' | 'SearchBar' | 'ToolbarActions' +import type { JSONUIComponentType } from './json-ui-component-types' + +export type ComponentType = JSONUIComponentType export interface BreadcrumbItem { label: string