Merge branch 'main' into codex/update-component-registry-paths-and-sources

This commit is contained in:
2026-01-18 18:48:07 +00:00
committed by GitHub
10 changed files with 409 additions and 61 deletions

View File

@@ -12,7 +12,69 @@
"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": "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",

View File

@@ -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"
},

View File

@@ -22,6 +22,15 @@
"type": "string"
}
},
"sourceRoots": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
},
"components": {
"type": "array",
"items": {

View File

@@ -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<string>()
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}`)

View File

@@ -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])
}

View File

@@ -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<ShowcaseExample[]>(() => {
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,

View File

@@ -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": {

View File

@@ -24,6 +24,7 @@ interface JsonRegistryEntry {
interface JsonComponentRegistry {
components?: JsonRegistryEntry[]
sourceRoots?: Record<string, string[]>
}
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<string, Record<string, unknown>>
const getRegistryEntryKey = (entry: JsonRegistryEntry): string | undefined =>
entry.name ?? entry.type

View File

@@ -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]

View File

@@ -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