Merge pull request #140 from johndoe6345789/codex/identify-components-for-hooks-and-context

Add JSON wrappers for hook-dependent components and register them in the registry
This commit is contained in:
2026-01-18 13:05:45 +00:00
committed by GitHub
12 changed files with 1004 additions and 20 deletions

View File

@@ -67,7 +67,20 @@
"description": "ComponentBindingDialog component",
"status": "supported",
"source": "molecules",
"jsonCompatible": true
"jsonCompatible": false,
"wrapperRequired": true,
"wrapperComponent": "ComponentBindingDialogWrapper"
},
{
"type": "ComponentBindingDialogWrapper",
"name": "ComponentBindingDialogWrapper",
"category": "layout",
"canHaveChildren": true,
"description": "JSON wrapper for component binding dialog with props-driven bindings",
"status": "json-compatible",
"source": "wrappers",
"jsonCompatible": true,
"wrapperFor": "ComponentBindingDialog"
},
{
"type": "Container",
@@ -96,7 +109,20 @@
"description": "DataSourceEditorDialog component",
"status": "supported",
"source": "molecules",
"jsonCompatible": true
"jsonCompatible": false,
"wrapperRequired": true,
"wrapperComponent": "DataSourceEditorDialogWrapper"
},
{
"type": "DataSourceEditorDialogWrapper",
"name": "DataSourceEditorDialogWrapper",
"category": "layout",
"canHaveChildren": true,
"description": "JSON wrapper for data source editor dialog with props-driven fields",
"status": "json-compatible",
"source": "wrappers",
"jsonCompatible": true,
"wrapperFor": "DataSourceEditorDialog"
},
{
"type": "Dialog",
@@ -893,7 +919,20 @@
"description": "GitHubBuildStatus component",
"status": "supported",
"source": "molecules",
"jsonCompatible": false
"jsonCompatible": false,
"wrapperRequired": true,
"wrapperComponent": "GitHubBuildStatusWrapper"
},
{
"type": "GitHubBuildStatusWrapper",
"name": "GitHubBuildStatusWrapper",
"category": "feedback",
"canHaveChildren": false,
"description": "JSON wrapper for props-driven GitHub build status summary",
"status": "json-compatible",
"source": "wrappers",
"jsonCompatible": true,
"wrapperFor": "GitHubBuildStatus"
},
{
"type": "InfoBox",
@@ -1039,30 +1078,69 @@
"name": "LazyBarChart",
"category": "data",
"canHaveChildren": true,
"description": "Lazy-loaded Recharts bar chart with runtime library loading",
"status": "supported",
"source": "molecules",
"jsonCompatible": false,
"wrapperRequired": true,
"wrapperComponent": "LazyBarChartWrapper"
},
{
"type": "LazyBarChartWrapper",
"name": "LazyBarChartWrapper",
"category": "data",
"canHaveChildren": true,
"description": "JSON wrapper for a props-driven bar chart (no lazy hooks)",
"status": "json-compatible",
"source": "molecules",
"jsonCompatible": true
"source": "wrappers",
"jsonCompatible": true,
"wrapperFor": "LazyBarChart"
},
{
"type": "LazyD3BarChart",
"name": "LazyD3BarChart",
"category": "data",
"canHaveChildren": true,
"description": "Lazy-loaded D3 bar chart with runtime library loading",
"status": "supported",
"source": "molecules",
"jsonCompatible": false,
"wrapperRequired": true,
"wrapperComponent": "LazyD3BarChartWrapper"
},
{
"type": "LazyD3BarChartWrapper",
"name": "LazyD3BarChartWrapper",
"category": "data",
"canHaveChildren": true,
"description": "JSON wrapper for a simple SVG bar chart (no D3 hooks)",
"status": "json-compatible",
"source": "molecules",
"jsonCompatible": true
"source": "wrappers",
"jsonCompatible": true,
"wrapperFor": "LazyD3BarChart"
},
{
"type": "LazyLineChart",
"name": "LazyLineChart",
"category": "data",
"canHaveChildren": true,
"description": "Lazy-loaded Recharts line chart with runtime library loading",
"status": "supported",
"source": "molecules",
"jsonCompatible": false,
"wrapperRequired": true,
"wrapperComponent": "LazyLineChartWrapper"
},
{
"type": "LazyLineChartWrapper",
"name": "LazyLineChartWrapper",
"category": "data",
"canHaveChildren": true,
"description": "JSON wrapper for a props-driven line chart (no lazy hooks)",
"status": "json-compatible",
"source": "molecules",
"jsonCompatible": true
"source": "wrappers",
"jsonCompatible": true,
"wrapperFor": "LazyLineChart"
},
{
"type": "List",
@@ -1105,10 +1183,23 @@
"name": "SeedDataManager",
"category": "data",
"canHaveChildren": true,
"description": "Seed data management with app-level hook state",
"status": "supported",
"source": "molecules",
"jsonCompatible": false,
"wrapperRequired": true,
"wrapperComponent": "SeedDataManagerWrapper"
},
{
"type": "SeedDataManagerWrapper",
"name": "SeedDataManagerWrapper",
"category": "data",
"canHaveChildren": true,
"description": "JSON wrapper for seed data management with props-driven state",
"status": "json-compatible",
"source": "molecules",
"jsonCompatible": true
"source": "wrappers",
"jsonCompatible": true,
"wrapperFor": "SeedDataManager"
},
{
"type": "StatCard",
@@ -1337,7 +1428,20 @@
"description": "ComponentTree component",
"status": "supported",
"source": "molecules",
"jsonCompatible": true
"jsonCompatible": false,
"wrapperRequired": true,
"wrapperComponent": "ComponentTreeWrapper"
},
{
"type": "ComponentTreeWrapper",
"name": "ComponentTreeWrapper",
"category": "custom",
"canHaveChildren": true,
"description": "JSON wrapper for a props-driven component tree view",
"status": "json-compatible",
"source": "wrappers",
"jsonCompatible": true,
"wrapperFor": "ComponentTree"
},
{
"type": "ComponentTreeNode",
@@ -1564,10 +1668,23 @@
"name": "SaveIndicator",
"category": "custom",
"canHaveChildren": true,
"description": "Save status indicator with hook-driven state",
"status": "supported",
"source": "molecules",
"jsonCompatible": false,
"wrapperRequired": true,
"wrapperComponent": "SaveIndicatorWrapper"
},
{
"type": "SaveIndicatorWrapper",
"name": "SaveIndicatorWrapper",
"category": "custom",
"canHaveChildren": true,
"description": "JSON wrapper for save status indicator with pure-props API",
"status": "json-compatible",
"source": "molecules",
"jsonCompatible": true
"source": "wrappers",
"jsonCompatible": true,
"wrapperFor": "SaveIndicator"
},
{
"type": "SchemaEditorCanvas",
@@ -1715,10 +1832,23 @@
"name": "StorageSettings",
"category": "custom",
"canHaveChildren": true,
"description": "Storage settings controls with hook-driven state",
"status": "supported",
"source": "molecules",
"jsonCompatible": false,
"wrapperRequired": true,
"wrapperComponent": "StorageSettingsWrapper"
},
{
"type": "StorageSettingsWrapper",
"name": "StorageSettingsWrapper",
"category": "custom",
"canHaveChildren": true,
"description": "JSON wrapper for storage settings controls with props-driven state",
"status": "json-compatible",
"source": "molecules",
"jsonCompatible": true
"source": "wrappers",
"jsonCompatible": true,
"wrapperFor": "StorageSettings"
},
{
"type": "Timestamp",

View File

@@ -265,12 +265,20 @@ export function registerComponent(name: string, component: ComponentType<any>) {
uiComponentRegistry[name] = component
}
const resolveWrapperComponent = (type: string): ComponentType<any> | null => {
const entry = registryEntryByType.get(type)
if (entry?.wrapperRequired && entry.wrapperComponent) {
return uiComponentRegistry[entry.wrapperComponent] || null
}
return null
}
export function getUIComponent(type: string): ComponentType<any> | string | null {
return uiComponentRegistry[type] || null
return resolveWrapperComponent(type) ?? uiComponentRegistry[type] ?? null
}
export function hasComponent(type: string): boolean {
return type in uiComponentRegistry
return Boolean(resolveWrapperComponent(type) ?? uiComponentRegistry[type])
}
export function getDeprecatedComponentInfo(type: string): DeprecatedComponentInfo | null {

View File

@@ -205,6 +205,11 @@ export function ComponentRenderer({ component, data, context = {}, state, onEven
return null
}
const resolvedChildren = component.children ?? resolvedProps.children
if (resolvedChildren !== undefined && resolvedChildren !== component.children) {
delete resolvedProps.children
}
if (component.loop) {
const items = resolveDataBinding(component.loop.source, data, context, { state, bindings: context }) || []
const loopChildren = items.map((item: unknown, index: number) => {
@@ -232,7 +237,7 @@ export function ComponentRenderer({ component, data, context = {}, state, onEven
return (
<Fragment key={`${component.id}-${index}`}>
{renderChildren(component.children, loopContext)}
{renderChildren(resolvedChildren as UIComponent[] | string | undefined, loopContext)}
</Fragment>
)
})
@@ -254,5 +259,9 @@ export function ComponentRenderer({ component, data, context = {}, state, onEven
}
}
return createElement(Component, resolvedProps, renderChildren(component.children, context))
return createElement(
Component,
resolvedProps,
renderChildren(resolvedChildren as UIComponent[] | string | undefined, context)
)
}

View File

@@ -0,0 +1,47 @@
import type { ChangeEvent } from 'react'
import { ComponentRenderer } from '@/lib/json-ui/component-renderer'
import { cn } from '@/lib/utils'
import componentBindingDialogDefinition from './definitions/component-binding-dialog.json'
import type { ComponentBindingDialogWrapperProps } from './interfaces'
export function ComponentBindingDialogWrapper({
open = false,
title = 'Component Bindings',
description = 'Connect component props to data sources.',
componentType,
componentId,
bindings = [],
onBindingChange,
onSave,
onCancel,
onOpenChange,
className,
}: ComponentBindingDialogWrapperProps) {
const handleBindingFieldChange = (event: ChangeEvent<HTMLInputElement>) => {
const fieldId = event.currentTarget?.dataset?.fieldId || event.target?.dataset?.fieldId
if (!fieldId) return
onBindingChange?.(fieldId, event.target.value)
}
return (
<ComponentRenderer
component={componentBindingDialogDefinition}
data={{
open,
title,
description,
componentType,
componentId,
bindingFields: bindings,
emptyMessage: 'No bindings configured.',
contentClassName: cn('max-w-2xl', className),
onBindingFieldChange: handleBindingFieldChange,
onSave,
onCancel,
onOpenChange,
cancelLabel: 'Cancel',
saveLabel: 'Save',
}}
/>
)
}

View File

@@ -0,0 +1,55 @@
import { cn } from '@/lib/utils'
import type { UIComponent } from '@/types/json-ui'
import type { ComponentTreeWrapperProps } from './interfaces'
const renderTreeNodes = (
components: UIComponent[],
depth: number,
selectedId: string | null,
onSelect?: (id: string) => void
) => {
return components.map((component) => {
const hasChildren = Array.isArray(component.children) && component.children.length > 0
const isSelected = selectedId === component.id
return (
<div key={component.id}>
<button
type="button"
onClick={() => onSelect?.(component.id)}
className={cn(
'flex w-full items-center gap-2 rounded-md px-2 py-1 text-left text-sm transition-colors',
isSelected ? 'bg-accent/40 text-foreground' : 'hover:bg-muted'
)}
style={{ paddingLeft: `${depth * 16 + 8}px` }}
>
<span className="font-medium">{component.type}</span>
<span className="text-xs text-muted-foreground">{component.id}</span>
</button>
{hasChildren && (
<div className="mt-1">
{renderTreeNodes(component.children as UIComponent[], depth + 1, selectedId, onSelect)}
</div>
)}
</div>
)
})
}
export function ComponentTreeWrapper({
components = [],
selectedId = null,
emptyMessage = 'No components available.',
onSelect,
className,
}: ComponentTreeWrapperProps) {
return (
<div className={cn('space-y-2', className)}>
{components.length === 0 ? (
<p className="text-sm text-muted-foreground">{emptyMessage}</p>
) : (
<div className="space-y-1">{renderTreeNodes(components, 0, selectedId, onSelect)}</div>
)}
</div>
)
}

View File

@@ -0,0 +1,43 @@
import type { ChangeEvent } from 'react'
import { ComponentRenderer } from '@/lib/json-ui/component-renderer'
import { cn } from '@/lib/utils'
import dataSourceEditorDialogDefinition from './definitions/data-source-editor-dialog.json'
import type { DataSourceEditorDialogWrapperProps } from './interfaces'
export function DataSourceEditorDialogWrapper({
open = false,
title = 'Data Source',
description = 'Update data source details and fields.',
fields = [],
onFieldChange,
onSave,
onCancel,
onOpenChange,
className,
}: DataSourceEditorDialogWrapperProps) {
const handleFieldChange = (event: ChangeEvent<HTMLInputElement>) => {
const fieldId = event.currentTarget?.dataset?.fieldId || event.target?.dataset?.fieldId
if (!fieldId) return
onFieldChange?.(fieldId, event.target.value)
}
return (
<ComponentRenderer
component={dataSourceEditorDialogDefinition}
data={{
open,
title,
description,
fields,
emptyMessage: 'No fields configured.',
contentClassName: cn('max-w-2xl', className),
onFieldChange: handleFieldChange,
onSave,
onCancel,
onOpenChange,
cancelLabel: 'Cancel',
saveLabel: 'Save',
}}
/>
)
}

View File

@@ -0,0 +1,64 @@
import { ComponentRenderer } from '@/lib/json-ui/component-renderer'
import { cn } from '@/lib/utils'
import gitHubBuildStatusDefinition from './definitions/github-build-status.json'
import type { GitHubBuildStatusWrapperProps, GitHubBuildStatusWorkflowItem } from './interfaces'
const getWorkflowStatus = (workflow: GitHubBuildStatusWorkflowItem) => {
if (workflow.status === 'completed') {
if (workflow.conclusion === 'success') {
return {
label: 'Success',
className: 'bg-green-500/10 text-green-600 border-green-500/20',
}
}
if (workflow.conclusion === 'failure') {
return { label: 'Failed', className: 'bg-red-500/10 text-red-600 border-red-500/20' }
}
if (workflow.conclusion === 'cancelled') {
return { label: 'Cancelled', className: 'bg-yellow-500/10 text-yellow-600 border-yellow-500/20' }
}
}
return { label: 'Running', className: 'border-blue-500/50 text-blue-500' }
}
export function GitHubBuildStatusWrapper({
title = 'GitHub Build Status',
description = 'Latest workflow runs and status badges.',
workflows = [],
isLoading = false,
errorMessage,
emptyMessage = 'No workflows to display yet.',
footerLinkLabel = 'View on GitHub',
footerLinkUrl,
className,
}: GitHubBuildStatusWrapperProps) {
const normalizedWorkflows = workflows.map((workflow) => {
const status = getWorkflowStatus(workflow)
return {
...workflow,
statusLabel: status.label,
statusClass: status.className,
summaryLine: [workflow.branch, workflow.updatedAt, workflow.event].filter(Boolean).join(' • '),
}
})
return (
<ComponentRenderer
component={gitHubBuildStatusDefinition}
data={{
title,
description,
workflows: normalizedWorkflows,
isLoading,
errorMessage,
emptyMessage,
loadingMessage: 'Loading workflows…',
hasWorkflows: normalizedWorkflows.length > 0,
footerLinkLabel,
footerLinkUrl,
className: cn(className),
}}
/>
)
}

View File

@@ -0,0 +1,200 @@
{
"id": "component-binding-dialog",
"type": "Dialog",
"bindings": {
"open": "open",
"onOpenChange": "onOpenChange"
},
"children": [
{
"id": "component-binding-dialog-content",
"type": "DialogContent",
"bindings": {
"className": "contentClassName"
},
"children": [
{
"id": "component-binding-dialog-header",
"type": "DialogHeader",
"children": [
{
"id": "component-binding-dialog-title",
"type": "DialogTitle",
"bindings": {
"children": "title"
}
},
{
"id": "component-binding-dialog-description",
"type": "DialogDescription",
"bindings": {
"children": "description"
}
}
]
},
{
"id": "component-binding-dialog-info",
"type": "div",
"props": {
"className": "rounded-md border border-border bg-muted/30 p-3 text-sm"
},
"conditional": {
"if": "componentType || componentId"
},
"children": [
{
"id": "component-binding-dialog-type",
"type": "div",
"props": {
"className": "flex items-center gap-2"
},
"conditional": {
"if": "componentType"
},
"children": [
{
"id": "component-binding-dialog-type-label",
"type": "span",
"props": {
"className": "text-muted-foreground"
},
"children": "Component:"
},
{
"id": "component-binding-dialog-type-value",
"type": "span",
"props": {
"className": "font-mono font-medium"
},
"bindings": {
"children": "componentType"
}
}
]
},
{
"id": "component-binding-dialog-id",
"type": "div",
"props": {
"className": "flex items-center gap-2"
},
"conditional": {
"if": "componentId"
},
"children": [
{
"id": "component-binding-dialog-id-label",
"type": "span",
"props": {
"className": "text-muted-foreground"
},
"children": "ID:"
},
{
"id": "component-binding-dialog-id-value",
"type": "span",
"props": {
"className": "font-mono text-xs"
},
"bindings": {
"children": "componentId"
}
}
]
}
]
},
{
"id": "component-binding-dialog-body",
"type": "div",
"props": {
"className": "space-y-4"
},
"children": [
{
"id": "component-binding-dialog-empty",
"type": "p",
"props": {
"className": "text-sm text-muted-foreground"
},
"bindings": {
"children": "emptyMessage"
},
"conditional": {
"if": "!bindingFields || bindingFields.length === 0"
}
},
{
"id": "component-binding-dialog-fields",
"type": "div",
"props": {
"className": "space-y-4"
},
"conditional": {
"if": "bindingFields && bindingFields.length > 0"
},
"loop": {
"source": "bindingFields",
"itemVar": "field"
},
"children": [
{
"id": "component-binding-dialog-field",
"type": "div",
"props": {
"className": "space-y-2"
},
"children": [
{
"id": "component-binding-dialog-field-label",
"type": "Label",
"bindings": {
"children": "field.label"
}
},
{
"id": "component-binding-dialog-field-input",
"type": "Input",
"bindings": {
"value": "field.value",
"placeholder": "field.placeholder",
"onChange": "onBindingFieldChange",
"data-field-id": "field.id"
}
}
]
}
]
}
]
},
{
"id": "component-binding-dialog-footer",
"type": "DialogFooter",
"children": [
{
"id": "component-binding-dialog-cancel",
"type": "Button",
"props": {
"variant": "outline"
},
"bindings": {
"onClick": "onCancel",
"children": "cancelLabel"
}
},
{
"id": "component-binding-dialog-save",
"type": "Button",
"bindings": {
"onClick": "onSave",
"children": "saveLabel"
}
}
]
}
]
}
]
}

View File

@@ -0,0 +1,141 @@
{
"id": "data-source-editor-dialog",
"type": "Dialog",
"bindings": {
"open": "open",
"onOpenChange": "onOpenChange"
},
"children": [
{
"id": "data-source-editor-dialog-content",
"type": "DialogContent",
"bindings": {
"className": "contentClassName"
},
"children": [
{
"id": "data-source-editor-dialog-header",
"type": "DialogHeader",
"children": [
{
"id": "data-source-editor-dialog-title",
"type": "DialogTitle",
"bindings": {
"children": "title"
}
},
{
"id": "data-source-editor-dialog-description",
"type": "DialogDescription",
"bindings": {
"children": "description"
}
}
]
},
{
"id": "data-source-editor-dialog-body",
"type": "div",
"props": {
"className": "space-y-4"
},
"children": [
{
"id": "data-source-editor-dialog-empty",
"type": "p",
"props": {
"className": "text-sm text-muted-foreground"
},
"bindings": {
"children": "emptyMessage"
},
"conditional": {
"if": "!fields || fields.length === 0"
}
},
{
"id": "data-source-editor-dialog-fields",
"type": "div",
"props": {
"className": "space-y-4"
},
"conditional": {
"if": "fields && fields.length > 0"
},
"loop": {
"source": "fields",
"itemVar": "field"
},
"children": [
{
"id": "data-source-editor-dialog-field",
"type": "div",
"props": {
"className": "space-y-2"
},
"children": [
{
"id": "data-source-editor-dialog-field-label",
"type": "Label",
"bindings": {
"children": "field.label"
}
},
{
"id": "data-source-editor-dialog-field-input",
"type": "Input",
"bindings": {
"value": "field.value",
"placeholder": "field.placeholder",
"onChange": "onFieldChange",
"data-field-id": "field.id"
}
},
{
"id": "data-source-editor-dialog-field-helper",
"type": "p",
"props": {
"className": "text-xs text-muted-foreground"
},
"bindings": {
"children": "field.helperText"
},
"conditional": {
"if": "field.helperText"
}
}
]
}
]
}
]
},
{
"id": "data-source-editor-dialog-footer",
"type": "DialogFooter",
"children": [
{
"id": "data-source-editor-dialog-cancel",
"type": "Button",
"props": {
"variant": "outline"
},
"bindings": {
"onClick": "onCancel",
"children": "cancelLabel"
}
},
{
"id": "data-source-editor-dialog-save",
"type": "Button",
"bindings": {
"onClick": "onSave",
"children": "saveLabel"
}
}
]
}
]
}
]
}

View File

@@ -0,0 +1,210 @@
{
"id": "github-build-status-card",
"type": "Card",
"bindings": {
"className": "className"
},
"children": [
{
"id": "github-build-status-header",
"type": "CardHeader",
"children": [
{
"id": "github-build-status-title",
"type": "CardTitle",
"props": {
"className": "flex items-center gap-2"
},
"bindings": {
"children": "title"
}
},
{
"id": "github-build-status-description",
"type": "CardDescription",
"bindings": {
"children": "description"
}
}
]
},
{
"id": "github-build-status-content",
"type": "CardContent",
"props": {
"className": "space-y-4"
},
"children": [
{
"id": "github-build-status-loading",
"type": "p",
"props": {
"className": "text-sm text-muted-foreground"
},
"bindings": {
"children": "loadingMessage"
},
"conditional": {
"if": "isLoading"
}
},
{
"id": "github-build-status-error",
"type": "div",
"props": {
"className": "flex items-center gap-2 text-sm text-red-500"
},
"conditional": {
"if": "errorMessage"
},
"children": [
{
"id": "github-build-status-error-text",
"type": "span",
"bindings": {
"children": "errorMessage"
}
}
]
},
{
"id": "github-build-status-empty",
"type": "p",
"props": {
"className": "text-sm text-muted-foreground"
},
"bindings": {
"children": "emptyMessage"
},
"conditional": {
"if": "!isLoading && !errorMessage && !hasWorkflows"
}
},
{
"id": "github-build-status-list",
"type": "div",
"props": {
"className": "space-y-3"
},
"conditional": {
"if": "hasWorkflows"
},
"loop": {
"source": "workflows",
"itemVar": "workflow"
},
"children": [
{
"id": "github-build-status-item",
"type": "div",
"props": {
"className": "flex items-center justify-between gap-3 rounded-lg border border-border p-3"
},
"children": [
{
"id": "github-build-status-item-info",
"type": "div",
"props": {
"className": "min-w-0"
},
"children": [
{
"id": "github-build-status-item-row",
"type": "div",
"props": {
"className": "flex items-center gap-2"
},
"children": [
{
"id": "github-build-status-item-name",
"type": "p",
"props": {
"className": "text-sm font-medium truncate"
},
"bindings": {
"children": "workflow.name"
}
},
{
"id": "github-build-status-item-badge",
"type": "Badge",
"bindings": {
"className": "workflow.statusClass",
"children": "workflow.statusLabel"
}
}
]
},
{
"id": "github-build-status-item-meta",
"type": "div",
"props": {
"className": "text-xs text-muted-foreground truncate"
},
"bindings": {
"children": "workflow.summaryLine"
}
}
]
},
{
"id": "github-build-status-item-link",
"type": "Button",
"props": {
"variant": "ghost",
"size": "sm",
"asChild": true
},
"conditional": {
"if": "workflow.url"
},
"children": [
{
"id": "github-build-status-item-anchor",
"type": "a",
"bindings": {
"href": "workflow.url"
},
"props": {
"target": "_blank",
"rel": "noopener noreferrer"
},
"children": "View"
}
]
}
]
}
]
},
{
"id": "github-build-status-footer",
"type": "Button",
"props": {
"variant": "outline",
"size": "sm",
"asChild": true,
"className": "w-full"
},
"conditional": {
"if": "footerLinkUrl"
},
"children": [
{
"id": "github-build-status-footer-anchor",
"type": "a",
"bindings": {
"href": "footerLinkUrl",
"children": "footerLinkLabel"
},
"props": {
"target": "_blank",
"rel": "noopener noreferrer"
}
}
]
}
]
}
]
}

View File

@@ -5,3 +5,7 @@ export { LazyLineChartWrapper } from './LazyLineChartWrapper'
export { LazyD3BarChartWrapper } from './LazyD3BarChartWrapper'
export { SeedDataManagerWrapper } from './SeedDataManagerWrapper'
export { StorageSettingsWrapper } from './StorageSettingsWrapper'
export { GitHubBuildStatusWrapper } from './GitHubBuildStatusWrapper'
export { ComponentBindingDialogWrapper } from './ComponentBindingDialogWrapper'
export { DataSourceEditorDialogWrapper } from './DataSourceEditorDialogWrapper'
export { ComponentTreeWrapper } from './ComponentTreeWrapper'

View File

@@ -1,4 +1,5 @@
import type { StorageBackendKey } from '@/components/storage/storageSettingsConfig'
import type { UIComponent } from '@/types/json-ui'
export interface BreadcrumbItem {
label: string
@@ -89,3 +90,75 @@ export interface StorageSettingsWrapperProps {
onExport?: () => void
onImport?: () => void
}
export interface GitHubBuildStatusWorkflowItem {
id: string
name: string
status?: string
conclusion?: string | null
branch?: string
updatedAt?: string
event?: string
url?: string
}
export interface GitHubBuildStatusWrapperProps {
title?: string
description?: string
workflows?: GitHubBuildStatusWorkflowItem[]
isLoading?: boolean
errorMessage?: string
emptyMessage?: string
footerLinkLabel?: string
footerLinkUrl?: string
className?: string
}
export interface ComponentBindingField {
id: string
label: string
value?: string
placeholder?: string
}
export interface ComponentBindingDialogWrapperProps {
open?: boolean
title?: string
description?: string
componentType?: string
componentId?: string
bindings?: ComponentBindingField[]
onBindingChange?: (id: string, value: string) => void
onSave?: () => void
onCancel?: () => void
onOpenChange?: (open: boolean) => void
className?: string
}
export interface DataSourceField {
id: string
label: string
value?: string
placeholder?: string
helperText?: string
}
export interface DataSourceEditorDialogWrapperProps {
open?: boolean
title?: string
description?: string
fields?: DataSourceField[]
onFieldChange?: (id: string, value: string) => void
onSave?: () => void
onCancel?: () => void
onOpenChange?: (open: boolean) => void
className?: string
}
export interface ComponentTreeWrapperProps {
components?: UIComponent[]
selectedId?: string | null
emptyMessage?: string
onSelect?: (id: string) => void
className?: string
}