diff --git a/json-components-registry.json b/json-components-registry.json
index 7570256..c57afe6 100644
--- a/json-components-registry.json
+++ b/json-components-registry.json
@@ -2,7 +2,7 @@
"$schema": "./schemas/json-components-registry-schema.json",
"version": "2.0.0",
"description": "Registry of all components in the application",
- "lastUpdated": "2026-01-18T13:03:47Z",
+ "lastUpdated": "2026-01-21T01:57:14.374Z",
"categories": {
"layout": "Layout and container components",
"input": "Form inputs and interactive controls",
@@ -3549,7 +3549,12 @@
"canHaveChildren": true,
"description": "TabIcon component",
"status": "supported",
- "source": "atoms"
+ "source": "atoms",
+ "jsonCompatible": true,
+ "metadata": {
+ "conversionDate": "2026-01-21",
+ "autoGenerated": true
+ }
},
{
"type": "table",
@@ -3880,7 +3885,12 @@
"canHaveChildren": true,
"description": "TipsCard component",
"status": "supported",
- "source": "atoms"
+ "source": "atoms",
+ "jsonCompatible": true,
+ "metadata": {
+ "conversionDate": "2026-01-21",
+ "autoGenerated": true
+ }
},
{
"type": "toggle",
@@ -4165,4 +4175,4 @@
"wrappers": 10
}
}
-}
+}
\ No newline at end of file
diff --git a/src/components/atoms/InfoBox.tsx b/src/components/atoms/InfoBox.tsx
deleted file mode 100644
index c49e752..0000000
--- a/src/components/atoms/InfoBox.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import { cn } from '@/lib/utils'
-import { Info, Warning, CheckCircle, XCircle } from '@phosphor-icons/react'
-
-interface InfoBoxProps {
- type?: 'info' | 'warning' | 'success' | 'error'
- title?: string
- children: React.ReactNode
- className?: string
-}
-
-const iconMap = {
- info: Info,
- warning: Warning,
- success: CheckCircle,
- error: XCircle,
-}
-
-const variantClasses = {
- info: 'bg-blue-500/10 border-blue-500/20 text-blue-700 dark:text-blue-300',
- warning: 'bg-yellow-500/10 border-yellow-500/20 text-yellow-700 dark:text-yellow-300',
- success: 'bg-green-500/10 border-green-500/20 text-green-700 dark:text-green-300',
- error: 'bg-destructive/10 border-destructive/20 text-destructive',
-}
-
-export function InfoBox({ type = 'info', title, children, className }: InfoBoxProps) {
- const Icon = iconMap[type]
-
- return (
-
-
-
- {title &&
{title}
}
-
{children}
-
-
- )
-}
diff --git a/src/components/atoms/KeyValue.tsx b/src/components/atoms/KeyValue.tsx
deleted file mode 100644
index d00d644..0000000
--- a/src/components/atoms/KeyValue.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import { cn } from '@/lib/utils'
-
-interface KeyValueProps {
- label: string
- value: React.ReactNode
- orientation?: 'horizontal' | 'vertical'
- className?: string
- labelClassName?: string
- valueClassName?: string
-}
-
-export function KeyValue({
- label,
- value,
- orientation = 'horizontal',
- className,
- labelClassName,
- valueClassName
-}: KeyValueProps) {
- return (
-
-
- {label}
-
-
- {value}
-
-
- )
-}
diff --git a/src/components/atoms/Label.tsx b/src/components/atoms/Label.tsx
deleted file mode 100644
index 97e897c..0000000
--- a/src/components/atoms/Label.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { ReactNode } from 'react'
-import { cn } from '@/lib/utils'
-
-interface LabelProps {
- children: ReactNode
- htmlFor?: string
- required?: boolean
- className?: string
-}
-
-export function Label({ children, htmlFor, required, className }: LabelProps) {
- return (
-
- )
-}
diff --git a/src/components/atoms/List.tsx b/src/components/atoms/List.tsx
deleted file mode 100644
index 325a980..0000000
--- a/src/components/atoms/List.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import { ReactNode } from 'react'
-
-interface ListProps {
- items: T[]
- renderItem: (item: T, index: number) => ReactNode
- emptyMessage?: string
- className?: string
- itemClassName?: string
-}
-
-export function List({
- items,
- renderItem,
- emptyMessage = 'No items to display',
- className = '',
- itemClassName = ''
-}: ListProps) {
- if (items.length === 0) {
- return (
-
- {emptyMessage}
-
- )
- }
-
- return (
-
- {items.map((item, index) => (
-
- {renderItem(item, index)}
-
- ))}
-
- )
-}
diff --git a/src/components/atoms/ListItem.tsx b/src/components/atoms/ListItem.tsx
deleted file mode 100644
index 6042eef..0000000
--- a/src/components/atoms/ListItem.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import { cn } from '@/lib/utils'
-
-interface ListItemProps {
- icon?: React.ReactNode
- children: React.ReactNode
- onClick?: () => void
- active?: boolean
- className?: string
- endContent?: React.ReactNode
-}
-
-export function ListItem({ icon, children, onClick, active, className, endContent }: ListItemProps) {
- const isInteractive = !!onClick
-
- return (
-
- {icon &&
{icon}
}
-
{children}
- {endContent &&
{endContent}
}
-
- )
-}
diff --git a/src/components/atoms/LiveIndicator.tsx b/src/components/atoms/LiveIndicator.tsx
deleted file mode 100644
index a6ec0a6..0000000
--- a/src/components/atoms/LiveIndicator.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import { cn } from '@/lib/utils'
-
-interface LiveIndicatorProps {
- label?: string
- showLabel?: boolean
- size?: 'sm' | 'md' | 'lg'
- className?: string
-}
-
-export function LiveIndicator({
- label = 'LIVE',
- showLabel = true,
- size = 'md',
- className,
-}: LiveIndicatorProps) {
- const sizeClasses = {
- sm: 'text-xs gap-1.5',
- md: 'text-sm gap-2',
- lg: 'text-base gap-2.5',
- }
-
- const dotSizeClasses = {
- sm: 'w-2 h-2',
- md: 'w-2.5 h-2.5',
- lg: 'w-3 h-3',
- }
-
- return (
-
-
-
-
-
- {showLabel && (
- {label}
- )}
-
- )
-}
diff --git a/src/components/atoms/LoadingSpinner.tsx b/src/components/atoms/LoadingSpinner.tsx
deleted file mode 100644
index 7420ca7..0000000
--- a/src/components/atoms/LoadingSpinner.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-interface LoadingSpinnerProps {
- size?: 'sm' | 'md' | 'lg'
- className?: string
-}
-
-export function LoadingSpinner({ size = 'md', className = '' }: LoadingSpinnerProps) {
- const sizeClasses = {
- sm: 'w-4 h-4 border-2',
- md: 'w-6 h-6 border-2',
- lg: 'w-8 h-8 border-3',
- }
-
- return (
-
- )
-}
diff --git a/src/components/atoms/LoadingState.tsx b/src/components/atoms/LoadingState.tsx
deleted file mode 100644
index 87ab907..0000000
--- a/src/components/atoms/LoadingState.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import { cn } from '@/lib/utils'
-
-export interface LoadingStateProps {
- message?: string
- size?: 'sm' | 'md' | 'lg'
- className?: string
-}
-
-export function LoadingState({
- message = 'Loading...',
- size = 'md',
- className
-}: LoadingStateProps) {
- const sizeClasses = {
- sm: 'w-4 h-4 border-2',
- md: 'w-8 h-8 border-3',
- lg: 'w-12 h-12 border-4',
- }
-
- return (
-
-
- {message && (
-
{message}
- )}
-
- )
-}
diff --git a/src/components/atoms/MetricDisplay.tsx b/src/components/atoms/MetricDisplay.tsx
deleted file mode 100644
index 29549c9..0000000
--- a/src/components/atoms/MetricDisplay.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import { cn } from '@/lib/utils'
-import { TrendUp, TrendDown } from '@phosphor-icons/react'
-
-interface MetricDisplayProps {
- label: string
- value: string | number
- trend?: {
- value: number
- direction: 'up' | 'down'
- }
- icon?: React.ReactNode
- className?: string
- variant?: 'default' | 'primary' | 'accent'
-}
-
-export function MetricDisplay({
- label,
- value,
- trend,
- icon,
- className,
- variant = 'default'
-}: MetricDisplayProps) {
- const variantClasses = {
- default: 'text-foreground',
- primary: 'text-primary',
- accent: 'text-accent',
- }
-
- return (
-
-
- {icon && {icon}}
- {label}
-
-
-
- {value}
-
- {trend && (
-
- {trend.direction === 'up' ? : }
- {Math.abs(trend.value)}%
-
- )}
-
-
- )
-}
diff --git a/src/components/atoms/Modal.tsx b/src/components/atoms/Modal.tsx
deleted file mode 100644
index 2f2b737..0000000
--- a/src/components/atoms/Modal.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-import { X } from '@phosphor-icons/react'
-import { cn } from '@/lib/utils'
-
-interface ModalProps {
- isOpen: boolean
- onClose: () => void
- title?: string
- children: React.ReactNode
- size?: 'sm' | 'md' | 'lg' | 'xl' | 'full'
- showCloseButton?: boolean
- className?: string
-}
-
-export function Modal({
- isOpen,
- onClose,
- title,
- children,
- size = 'md',
- showCloseButton = true,
- className,
-}: ModalProps) {
- if (!isOpen) return null
-
- const sizeStyles = {
- sm: 'max-w-sm',
- md: 'max-w-md',
- lg: 'max-w-lg',
- xl: 'max-w-xl',
- full: 'max-w-full m-4',
- }
-
- return (
-
-
e.stopPropagation()}
- >
- {(title || showCloseButton) && (
-
- {title &&
{title}
}
- {showCloseButton && (
-
- )}
-
- )}
-
{children}
-
-
- )
-}
diff --git a/src/components/atoms/Notification.tsx b/src/components/atoms/Notification.tsx
deleted file mode 100644
index 452ae42..0000000
--- a/src/components/atoms/Notification.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-import { Info, CheckCircle, Warning, XCircle } from '@phosphor-icons/react'
-import { cn } from '@/lib/utils'
-
-interface NotificationProps {
- type: 'info' | 'success' | 'warning' | 'error'
- title: string
- message?: string
- onClose?: () => void
- className?: string
-}
-
-export function Notification({ type, title, message, onClose, className }: NotificationProps) {
- const config = {
- info: {
- icon: Info,
- color: 'text-blue-500',
- bg: 'bg-blue-500/10',
- border: 'border-blue-500/20',
- },
- success: {
- icon: CheckCircle,
- color: 'text-accent',
- bg: 'bg-accent/10',
- border: 'border-accent/20',
- },
- warning: {
- icon: Warning,
- color: 'text-yellow-500',
- bg: 'bg-yellow-500/10',
- border: 'border-yellow-500/20',
- },
- error: {
- icon: XCircle,
- color: 'text-destructive',
- bg: 'bg-destructive/10',
- border: 'border-destructive/20',
- },
- }
-
- const { icon: Icon, color, bg, border } = config[type]
-
- return (
-
-
-
-
{title}
- {message &&
{message}
}
-
- {onClose && (
-
- )}
-
- )
-}
diff --git a/src/components/atoms/NumberInput.tsx b/src/components/atoms/NumberInput.tsx
deleted file mode 100644
index e8ed56f..0000000
--- a/src/components/atoms/NumberInput.tsx
+++ /dev/null
@@ -1,89 +0,0 @@
-import { Input } from '@/components/ui/input'
-import { Button } from '@/components/ui/button'
-import { Minus, Plus } from '@phosphor-icons/react'
-import { cn } from '@/lib/utils'
-
-interface NumberInputProps {
- value: number
- onChange: (value: number) => void
- min?: number
- max?: number
- step?: number
- label?: string
- disabled?: boolean
- className?: string
-}
-
-export function NumberInput({
- value,
- onChange,
- min,
- max,
- step = 1,
- label,
- disabled,
- className,
-}: NumberInputProps) {
- const handleIncrement = () => {
- const newValue = value + step
- if (max === undefined || newValue <= max) {
- onChange(newValue)
- }
- }
-
- const handleDecrement = () => {
- const newValue = value - step
- if (min === undefined || newValue >= min) {
- onChange(newValue)
- }
- }
-
- const handleInputChange = (e: React.ChangeEvent) => {
- const newValue = parseFloat(e.target.value)
- if (!isNaN(newValue)) {
- if ((min === undefined || newValue >= min) && (max === undefined || newValue <= max)) {
- onChange(newValue)
- }
- }
- }
-
- return (
-
- {label && (
-
- )}
-
-
-
-
-
-
- )
-}
diff --git a/src/components/atoms/TabIcon.tsx b/src/components/atoms/TabIcon.tsx
deleted file mode 100644
index 53e5f82..0000000
--- a/src/components/atoms/TabIcon.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-interface TabIconProps {
- icon: React.ReactNode
- variant?: 'default' | 'gradient'
-}
-
-export function TabIcon({ icon, variant = 'default' }: TabIconProps) {
- if (variant === 'gradient') {
- return (
-
- {icon}
-
- )
- }
-
- return <>{icon}>
-}
diff --git a/src/components/atoms/TipsCard.tsx b/src/components/atoms/TipsCard.tsx
deleted file mode 100644
index eaa6794..0000000
--- a/src/components/atoms/TipsCard.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
-import { Warning } from '@phosphor-icons/react'
-
-interface TipsCardProps {
- tips: Array<{ message: string; show: boolean }>
-}
-
-export function TipsCard({ tips }: TipsCardProps) {
- const visibleTips = tips.filter(tip => tip.show)
-
- if (visibleTips.length === 0) return null
-
- return (
-
-
-
-
- Quick Tips
-
-
-
- {visibleTips.map((tip, index) => (
- • {tip.message}
- ))}
-
-
- )
-}
diff --git a/src/components/atoms/index.ts b/src/components/atoms/index.ts
index 9549a67..a74292e 100644
--- a/src/components/atoms/index.ts
+++ b/src/components/atoms/index.ts
@@ -1,4 +1,7 @@
// Auto-generated exports - DO NOT EDIT MANUALLY
+// JSON-based atom imports
+export { InfoBox, KeyValue, Label, List, ListItem, LiveIndicator, LoadingSpinner, LoadingState, MetricDisplay, Modal, Notification, NumberInput } from '@/lib/json-ui/json-components'
+
export { ActionButton } from './ActionButton'
export { ActionCard } from './ActionCard'
export { ActionIcon } from './ActionIcon'
@@ -49,22 +52,10 @@ export { HoverCard } from './HoverCard'
export { IconButton } from './IconButton'
export { IconText } from './IconText'
export { IconWrapper } from './IconWrapper'
-export { InfoBox } from './InfoBox'
export { InfoPanel } from './InfoPanel'
export { Kbd } from './Kbd'
-export { KeyValue } from './KeyValue'
-export { Label } from './Label'
export { Link } from './Link'
-export { List } from './List'
-export { ListItem } from './ListItem'
-export { LiveIndicator } from './LiveIndicator'
-export { LoadingSpinner } from './LoadingSpinner'
-export { LoadingState } from './LoadingState'
export { MetricCard } from './MetricCard'
-export { MetricDisplay } from './MetricDisplay'
-export { Modal } from './Modal'
-export { Notification } from './Notification'
-export { NumberInput } from './NumberInput'
export { BasicPageHeader, BasicPageHeader as PageHeader } from './PageHeader'
export { PanelHeader } from './PanelHeader'
export { ProgressBar } from './ProgressBar'
diff --git a/src/components/json-definitions/binding-indicator.json b/src/components/json-definitions/binding-indicator.json
new file mode 100644
index 0000000..9623774
--- /dev/null
+++ b/src/components/json-definitions/binding-indicator.json
@@ -0,0 +1,74 @@
+{
+ "id": "binding-indicator-root",
+ "type": "TooltipProvider",
+ "children": [
+ {
+ "id": "binding-indicator-tooltip",
+ "type": "Tooltip",
+ "children": [
+ {
+ "id": "binding-indicator-trigger",
+ "type": "TooltipTrigger",
+ "props": {
+ "asChild": true
+ },
+ "children": [
+ {
+ "id": "binding-indicator-content",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": "className",
+ "transform": "(() => { const path = $props.path; const sourceId = $props.sourceId; const baseClass = 'inline-flex items-center gap-1 px-2 py-1 rounded text-xs bg-accent/10 text-accent border border-accent/30'; return baseClass + ' ' + (data || ''); })()"
+ }
+ },
+ "children": [
+ {
+ "id": "binding-indicator-icon",
+ "type": "Icon",
+ "props": {
+ "name": "Link",
+ "weight": "bold",
+ "className": "w-3 h-3"
+ }
+ },
+ {
+ "id": "binding-indicator-text",
+ "type": "span",
+ "props": {
+ "className": "font-mono"
+ },
+ "bindings": {
+ "children": {
+ "source": "sourceId",
+ "transform": "(() => { const path = $props.path; return path ? $props.sourceId + '.' + path : $props.sourceId; })()"
+ }
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "id": "binding-indicator-tooltip-content",
+ "type": "TooltipContent",
+ "children": [
+ {
+ "id": "binding-indicator-tooltip-text",
+ "type": "p",
+ "props": {
+ "className": "text-xs"
+ },
+ "bindings": {
+ "children": {
+ "source": "sourceId",
+ "transform": "(() => { const path = $props.path; const bindingText = path ? $props.sourceId + '.' + path : $props.sourceId; return 'Bound to: ' + bindingText; })()"
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/components/json-definitions/button-group.json b/src/components/json-definitions/button-group.json
new file mode 100644
index 0000000..a5019f6
--- /dev/null
+++ b/src/components/json-definitions/button-group.json
@@ -0,0 +1,18 @@
+{
+ "id": "button-group-root",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": "orientation",
+ "transform": "(() => { const orientation = data || 'horizontal'; const baseClasses = 'inline-flex [&>button]:rounded-none [&>button:not(:last-child)]:border-r-0'; const directionClasses = orientation === 'horizontal' ? 'flex-row [&>button:first-child]:rounded-l-md [&>button:last-child]:rounded-r-md' : 'flex-col [&>button:first-child]:rounded-t-md [&>button:first-child]:rounded-l-none [&>button:last-child]:rounded-b-md [&>button:last-child]:rounded-r-none [&>button:not(:last-child)]:border-b-0 [&>button:not(:last-child)]:border-r'; const userClass = $props.className || ''; return baseClasses + ' ' + directionClasses + ' ' + userClass; })()"
+ }
+ },
+ "children": [
+ {
+ "id": "button-group-content",
+ "bindings": {
+ "children": "children"
+ }
+ }
+ ]
+}
diff --git a/src/components/json-definitions/chip.json b/src/components/json-definitions/chip.json
new file mode 100644
index 0000000..62f056b
--- /dev/null
+++ b/src/components/json-definitions/chip.json
@@ -0,0 +1,49 @@
+{
+ "id": "chip-root",
+ "type": "span",
+ "bindings": {
+ "className": {
+ "source": "variant",
+ "transform": "(() => { const variant = data || 'default'; const variantMap = { default: 'bg-secondary text-secondary-foreground', primary: 'bg-primary text-primary-foreground', accent: 'bg-accent text-accent-foreground', muted: 'bg-muted text-muted-foreground' }; const variantClass = variantMap[variant] || variantMap.default; const size = $props.size || 'md'; const sizeMap = { sm: 'px-2 py-0.5 text-xs', md: 'px-3 py-1 text-sm' }; const sizeClass = sizeMap[size] || sizeMap.md; const userClass = $props.className || ''; return 'inline-flex items-center gap-1 rounded-full font-medium ' + variantClass + ' ' + sizeClass + ' ' + userClass; })()"
+ }
+ },
+ "children": [
+ {
+ "id": "chip-content",
+ "bindings": {
+ "children": "children"
+ }
+ },
+ {
+ "id": "chip-remove-button",
+ "type": "button",
+ "props": {
+ "type": "button",
+ "className": "inline-flex items-center justify-center hover:bg-black/10 rounded-full transition-colors",
+ "aria-label": "Remove"
+ },
+ "bindings": {
+ "onClick": "onRemove"
+ },
+ "conditional": {
+ "if": "onRemove"
+ },
+ "children": [
+ {
+ "id": "chip-remove-icon",
+ "type": "Icon",
+ "bindings": {
+ "name": "X",
+ "props": {
+ "weight": "bold",
+ "className": {
+ "source": "size",
+ "transform": "data === 'sm' ? 'w-3 h-3' : 'w-3.5 h-3.5'"
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/components/json-definitions/circular-progress.json b/src/components/json-definitions/circular-progress.json
new file mode 100644
index 0000000..5033780
--- /dev/null
+++ b/src/components/json-definitions/circular-progress.json
@@ -0,0 +1,111 @@
+{
+ "id": "circular-progress-root",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": "className",
+ "transform": "(() => { const userClass = data || ''; return 'relative inline-flex items-center justify-center ' + userClass; })()"
+ }
+ },
+ "children": [
+ {
+ "id": "circular-progress-svg",
+ "type": "svg",
+ "bindings": {
+ "width": {
+ "source": "size",
+ "transform": "(() => { const sizeMap = { sm: 48, md: 64, lg: 96, xl: 128 }; return sizeMap[data || 'md']; })()"
+ },
+ "height": {
+ "source": "size",
+ "transform": "(() => { const sizeMap = { sm: 48, md: 64, lg: 96, xl: 128 }; return sizeMap[data || 'md']; })()"
+ }
+ },
+ "props": {
+ "className": "transform -rotate-90"
+ },
+ "children": [
+ {
+ "id": "circular-progress-background-circle",
+ "type": "circle",
+ "bindings": {
+ "cx": {
+ "source": "size",
+ "transform": "(() => { const sizeMap = { sm: 24, md: 32, lg: 48, xl: 64 }; return sizeMap[data || 'md']; })()"
+ },
+ "cy": {
+ "source": "size",
+ "transform": "(() => { const sizeMap = { sm: 24, md: 32, lg: 48, xl: 64 }; return sizeMap[data || 'md']; })()"
+ },
+ "r": {
+ "source": "size",
+ "transform": "(() => { const strokeWidth = $props.strokeWidth; const sizeMap = { sm: { dim: 48, stroke: 4 }, md: { dim: 64, stroke: 5 }, lg: { dim: 96, stroke: 6 }, xl: { dim: 128, stroke: 8 } }; const size = sizeMap[data || 'md']; const actualStroke = strokeWidth || size.stroke; return (size.dim - actualStroke) / 2; })()"
+ },
+ "strokeWidth": {
+ "source": "size",
+ "transform": "(() => { const strokeWidth = $props.strokeWidth; const sizeMap = { sm: 4, md: 5, lg: 6, xl: 8 }; return strokeWidth || sizeMap[data || 'md']; })()"
+ }
+ },
+ "props": {
+ "stroke": "currentColor",
+ "fill": "none",
+ "className": "text-muted opacity-20"
+ }
+ },
+ {
+ "id": "circular-progress-active-circle",
+ "type": "circle",
+ "bindings": {
+ "cx": {
+ "source": "size",
+ "transform": "(() => { const sizeMap = { sm: 24, md: 32, lg: 48, xl: 64 }; return sizeMap[data || 'md']; })()"
+ },
+ "cy": {
+ "source": "size",
+ "transform": "(() => { const sizeMap = { sm: 24, md: 32, lg: 48, xl: 64 }; return sizeMap[data || 'md']; })()"
+ },
+ "r": {
+ "source": "size",
+ "transform": "(() => { const strokeWidth = $props.strokeWidth; const sizeMap = { sm: { dim: 48, stroke: 4 }, md: { dim: 64, stroke: 5 }, lg: { dim: 96, stroke: 6 }, xl: { dim: 128, stroke: 8 } }; const size = sizeMap[data || 'md']; const actualStroke = strokeWidth || size.stroke; return (size.dim - actualStroke) / 2; })()"
+ },
+ "strokeWidth": {
+ "source": "size",
+ "transform": "(() => { const strokeWidth = $props.strokeWidth; const sizeMap = { sm: 4, md: 5, lg: 6, xl: 8 }; return strokeWidth || sizeMap[data || 'md']; })()"
+ },
+ "strokeDasharray": {
+ "source": "size",
+ "transform": "(() => { const strokeWidth = $props.strokeWidth; const sizeMap = { sm: { dim: 48, stroke: 4 }, md: { dim: 64, stroke: 5 }, lg: { dim: 96, stroke: 6 }, xl: { dim: 128, stroke: 8 } }; const size = sizeMap[data || 'md']; const actualStroke = strokeWidth || size.stroke; const radius = (size.dim - actualStroke) / 2; return radius * 2 * Math.PI; })()"
+ },
+ "strokeDashoffset": {
+ "source": "value",
+ "transform": "(() => { const value = data; const max = $props.max || 100; const percentage = Math.min((value / max) * 100, 100); const strokeWidth = $props.strokeWidth; const sizeMap = { sm: { dim: 48, stroke: 4 }, md: { dim: 64, stroke: 5 }, lg: { dim: 96, stroke: 6 }, xl: { dim: 128, stroke: 8 } }; const size = sizeMap[$props.size || 'md']; const actualStroke = strokeWidth || size.stroke; const radius = (size.dim - actualStroke) / 2; const circumference = radius * 2 * Math.PI; return circumference - (percentage / 100) * circumference; })()"
+ }
+ },
+ "props": {
+ "stroke": "currentColor",
+ "fill": "none",
+ "strokeLinecap": "round",
+ "className": "text-primary transition-all duration-300"
+ }
+ }
+ ]
+ },
+ {
+ "id": "circular-progress-label",
+ "type": "span",
+ "bindings": {
+ "children": {
+ "source": "value",
+ "transform": "(() => { const value = data; const max = $props.max || 100; const percentage = Math.min((value / max) * 100, 100); return Math.round(percentage) + '%'; })()"
+ },
+ "className": {
+ "source": "size",
+ "transform": "(() => { const sizeMap = { sm: 'text-xs', md: 'text-sm', lg: 'text-base', xl: 'text-lg' }; return 'absolute font-semibold ' + (sizeMap[data || 'md'] || sizeMap.md); })()"
+ }
+ },
+ "conditional": {
+ "if": "showLabel"
+ }
+ }
+ ]
+}
diff --git a/src/components/json-definitions/code.json b/src/components/json-definitions/code.json
new file mode 100644
index 0000000..7ad235e
--- /dev/null
+++ b/src/components/json-definitions/code.json
@@ -0,0 +1,18 @@
+{
+ "id": "code-root",
+ "type": "code",
+ "bindings": {
+ "className": {
+ "source": "inline",
+ "transform": "(() => { if (data === false) { return 'p-4 rounded-lg bg-muted text-foreground font-mono text-sm overflow-x-auto'; } return 'px-1.5 py-0.5 rounded bg-muted text-foreground font-mono text-sm ' + ($props.className || ''); })()"
+ }
+ },
+ "children": [
+ {
+ "id": "code-content",
+ "bindings": {
+ "children": "children"
+ }
+ }
+ ]
+}
diff --git a/src/components/json-definitions/command-palette.json b/src/components/json-definitions/command-palette.json
new file mode 100644
index 0000000..c08f6ad
--- /dev/null
+++ b/src/components/json-definitions/command-palette.json
@@ -0,0 +1,85 @@
+{
+ "id": "command-palette-root",
+ "type": "CommandDialog",
+ "bindings": {
+ "open": "open",
+ "onOpenChange": "onOpenChange"
+ },
+ "children": [
+ {
+ "id": "command-palette-input",
+ "type": "CommandInput",
+ "bindings": {
+ "placeholder": "placeholder"
+ }
+ },
+ {
+ "id": "command-palette-list",
+ "type": "CommandList",
+ "children": [
+ {
+ "id": "command-palette-empty",
+ "type": "CommandEmpty",
+ "bindings": {
+ "children": "emptyMessage"
+ }
+ },
+ {
+ "id": "command-palette-groups",
+ "type": "forEach",
+ "bindings": {
+ "items": "groups",
+ "children": {
+ "id": "command-palette-group",
+ "type": "CommandGroup",
+ "bindings": {
+ "heading": "heading"
+ },
+ "children": [
+ {
+ "id": "command-palette-items",
+ "type": "forEach",
+ "bindings": {
+ "items": "items",
+ "children": {
+ "id": "command-palette-item",
+ "type": "CommandItem",
+ "bindings": {
+ "value": "value",
+ "onSelect": {
+ "transform": "(() => { return () => { data.onSelect?.(); $props.onOpenChange(false); }; })()"
+ }
+ },
+ "children": [
+ {
+ "id": "command-palette-item-icon",
+ "type": "span",
+ "props": {
+ "className": "mr-2"
+ },
+ "bindings": {
+ "children": "icon"
+ },
+ "conditional": {
+ "if": "icon"
+ }
+ },
+ {
+ "id": "command-palette-item-label",
+ "type": "span",
+ "bindings": {
+ "children": "label"
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/components/json-definitions/completion-card.json b/src/components/json-definitions/completion-card.json
new file mode 100644
index 0000000..e43c280
--- /dev/null
+++ b/src/components/json-definitions/completion-card.json
@@ -0,0 +1,118 @@
+{
+ "id": "completion-card-root",
+ "type": "Card",
+ "props": {
+ "className": "bg-gradient-to-br from-primary/10 to-accent/10 border-primary/20"
+ },
+ "children": [
+ {
+ "id": "completion-card-header",
+ "type": "CardHeader",
+ "children": [
+ {
+ "id": "completion-card-title",
+ "type": "CardTitle",
+ "props": {
+ "className": "flex items-center gap-2"
+ },
+ "children": [
+ {
+ "id": "completion-card-icon",
+ "type": "Icon",
+ "props": {
+ "name": "CheckCircle",
+ "weight": "duotone",
+ "className": "text-primary w-6 h-6"
+ }
+ },
+ {
+ "id": "completion-card-title-text",
+ "type": "span",
+ "props": {},
+ "children": [
+ "Project Completeness"
+ ]
+ }
+ ]
+ },
+ {
+ "id": "completion-card-description",
+ "type": "CardDescription",
+ "children": [
+ "Overall progress of your application"
+ ]
+ }
+ ]
+ },
+ {
+ "id": "completion-card-content",
+ "type": "CardContent",
+ "props": {
+ "className": "space-y-4"
+ },
+ "children": [
+ {
+ "id": "completion-card-score-row",
+ "type": "div",
+ "props": {
+ "className": "flex items-center justify-between"
+ },
+ "children": [
+ {
+ "id": "completion-card-score",
+ "type": "span",
+ "props": {
+ "className": "text-4xl font-bold"
+ },
+ "bindings": {
+ "children": "completionScore"
+ }
+ },
+ {
+ "id": "completion-card-badge",
+ "type": "Badge",
+ "bindings": {
+ "variant": {
+ "source": "isReadyToExport",
+ "transform": "data ? 'default' : 'secondary'"
+ },
+ "className": "text-sm"
+ },
+ "children": [
+ {
+ "id": "completion-card-badge-text",
+ "bindings": {
+ "children": {
+ "source": "isReadyToExport",
+ "transform": "data ? 'Ready to Export' : 'In Progress'"
+ }
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "id": "completion-card-progress",
+ "type": "Progress",
+ "bindings": {
+ "value": "completionScore"
+ },
+ "props": {
+ "className": "h-3"
+ }
+ },
+ {
+ "id": "completion-card-message",
+ "type": "p",
+ "props": {
+ "className": "text-sm text-muted-foreground"
+ },
+ "bindings": {
+ "children": "completionMessage"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/components/json-definitions/component-palette-item.json b/src/components/json-definitions/component-palette-item.json
new file mode 100644
index 0000000..d2740ac
--- /dev/null
+++ b/src/components/json-definitions/component-palette-item.json
@@ -0,0 +1,55 @@
+{
+ "id": "component-palette-item-root",
+ "type": "Card",
+ "props": {
+ "draggable": true
+ },
+ "bindings": {
+ "onDragStart": {
+ "transform": "(() => { return (e) => $props.onDragStart($props.component, e); })()"
+ },
+ "className": {
+ "source": "className",
+ "transform": "(() => { const userClass = data || ''; return 'p-3 cursor-move hover:bg-accent/50 hover:border-accent transition-all flex flex-col items-center gap-2 text-center hover:scale-105 active:scale-95 ' + userClass; })()"
+ }
+ },
+ "children": [
+ {
+ "id": "component-palette-item-icon",
+ "type": "Icon",
+ "bindings": {
+ "name": {
+ "transform": "(() => { const iconMap = {}; const componentIcon = $props.component?.icon || 'Cube'; return componentIcon; })()"
+ }
+ },
+ "props": {
+ "weight": "duotone",
+ "className": "w-6 h-6 text-primary"
+ }
+ },
+ {
+ "id": "component-palette-item-label",
+ "type": "span",
+ "props": {
+ "className": "text-xs font-medium text-foreground"
+ },
+ "bindings": {
+ "children": {
+ "transform": "$props.component?.label || 'Component'"
+ }
+ }
+ },
+ {
+ "id": "component-palette-item-type",
+ "type": "span",
+ "props": {
+ "className": "text-[10px] text-muted-foreground"
+ },
+ "bindings": {
+ "children": {
+ "transform": "$props.component?.type || 'unknown'"
+ }
+ }
+ }
+ ]
+}
diff --git a/src/components/json-definitions/confirm-button.json b/src/components/json-definitions/confirm-button.json
new file mode 100644
index 0000000..3b3d5d6
--- /dev/null
+++ b/src/components/json-definitions/confirm-button.json
@@ -0,0 +1,22 @@
+{
+ "id": "confirm-button-root",
+ "type": "Button",
+ "bindings": {
+ "onClick": {
+ "transform": "(() => { return async () => { if (window.confirm($props.confirmText || 'Are you sure?')) { await $props.onConfirm(); } }; })()"
+ },
+ "disabled": "isLoading",
+ "className": "className"
+ },
+ "children": [
+ {
+ "id": "confirm-button-content",
+ "bindings": {
+ "children": {
+ "source": "isLoading",
+ "transform": "data ? 'Loading...' : $props.children"
+ }
+ }
+ }
+ ]
+}
diff --git a/src/components/json-definitions/data-source-badge.json b/src/components/json-definitions/data-source-badge.json
new file mode 100644
index 0000000..0ce85d3
--- /dev/null
+++ b/src/components/json-definitions/data-source-badge.json
@@ -0,0 +1,37 @@
+{
+ "id": "data-source-badge-root",
+ "type": "Badge",
+ "bindings": {
+ "variant": "outline",
+ "className": {
+ "source": "type",
+ "transform": "(() => { const typeMap = { kv: 'flex items-center gap-1 bg-accent/20 text-accent border-accent/30', static: 'flex items-center gap-1 bg-muted text-muted-foreground border-border' }; return (typeMap[data] || typeMap.static) + ' ' + ($props.className || ''); })()"
+ }
+ },
+ "children": [
+ {
+ "id": "data-source-badge-icon",
+ "type": "Icon",
+ "bindings": {
+ "name": {
+ "source": "type",
+ "transform": "data === 'kv' ? 'Database' : 'File'"
+ }
+ },
+ "props": {
+ "weight": "bold",
+ "className": "w-3 h-3"
+ }
+ },
+ {
+ "id": "data-source-badge-text",
+ "type": "span",
+ "bindings": {
+ "children": {
+ "source": "type",
+ "transform": "data === 'kv' ? 'KV Storage' : 'Static'"
+ }
+ }
+ }
+ ]
+}
diff --git a/src/components/json-definitions/date-picker.json b/src/components/json-definitions/date-picker.json
new file mode 100644
index 0000000..0de38e7
--- /dev/null
+++ b/src/components/json-definitions/date-picker.json
@@ -0,0 +1,96 @@
+{
+ "id": "date-picker-wrapper",
+ "type": "Popover",
+ "children": [
+ {
+ "id": "date-picker-trigger",
+ "type": "PopoverTrigger",
+ "bindings": {
+ "asChild": {
+ "source": null,
+ "transform": "true"
+ }
+ },
+ "children": [
+ {
+ "id": "date-picker-button",
+ "type": "Button",
+ "bindings": {
+ "variant": {
+ "source": null,
+ "transform": "'outline'"
+ },
+ "disabled": {
+ "source": "disabled",
+ "transform": "data"
+ },
+ "className": {
+ "source": ["value", "className"],
+ "transform": "const value = data[0]; const className = data[1] || ''; const baseClasses = 'w-full justify-start text-left font-normal'; const valueClass = !value ? 'text-muted-foreground' : ''; return `${baseClasses} ${valueClass} ${className}`.trim()"
+ },
+ "children": [
+ {
+ "id": "date-picker-icon",
+ "type": "CalendarBlank",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'mr-2'"
+ },
+ "size": {
+ "source": null,
+ "transform": "16"
+ }
+ }
+ },
+ {
+ "id": "date-picker-label",
+ "type": "span",
+ "bindings": {
+ "children": {
+ "source": ["value", "placeholder"],
+ "transform": "const value = data[0]; const placeholder = data[1] || 'Pick a date'; return value ? new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric', weekday: 'long' }).format(value) : placeholder"
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "id": "date-picker-content",
+ "type": "PopoverContent",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'w-auto p-0'"
+ },
+ "align": {
+ "source": null,
+ "transform": "'start'"
+ }
+ },
+ "children": [
+ {
+ "id": "date-picker-calendar",
+ "type": "Calendar",
+ "bindings": {
+ "mode": {
+ "source": null,
+ "transform": "'single'"
+ },
+ "selected": {
+ "source": "value",
+ "transform": "data"
+ },
+ "onSelect": {
+ "source": "onChange",
+ "transform": "data"
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/components/json-definitions/detail-row.json b/src/components/json-definitions/detail-row.json
new file mode 100644
index 0000000..0ff9a85
--- /dev/null
+++ b/src/components/json-definitions/detail-row.json
@@ -0,0 +1,66 @@
+{
+ "id": "detail-row-container",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'flex items-center justify-between py-2 border-b border-border last:border-0'"
+ }
+ },
+ "children": [
+ {
+ "id": "detail-row-left",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'flex items-center gap-2'"
+ }
+ },
+ "children": [
+ {
+ "id": "detail-row-icon",
+ "type": "span",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'text-muted-foreground'"
+ },
+ "children": {
+ "source": "icon",
+ "transform": "data"
+ }
+ }
+ },
+ {
+ "id": "detail-row-label",
+ "type": "span",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'text-sm font-medium'"
+ },
+ "children": {
+ "source": "label",
+ "transform": "data"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "id": "detail-row-badge",
+ "type": "Badge",
+ "bindings": {
+ "variant": {
+ "source": null,
+ "transform": "'secondary'"
+ },
+ "children": {
+ "source": "value",
+ "transform": "data"
+ }
+ }
+ }
+ ]
+}
diff --git a/src/components/json-definitions/divider.json b/src/components/json-definitions/divider.json
new file mode 100644
index 0000000..34ff2cd
--- /dev/null
+++ b/src/components/json-definitions/divider.json
@@ -0,0 +1,18 @@
+{
+ "id": "divider",
+ "type": "div",
+ "bindings": {
+ "role": {
+ "source": "decorative",
+ "transform": "data ? 'presentation' : 'separator'"
+ },
+ "aria-orientation": {
+ "source": "orientation",
+ "transform": "data || 'horizontal'"
+ },
+ "className": {
+ "source": ["orientation", "className"],
+ "transform": "const orientation = data[0] || 'horizontal'; const className = data[1] || ''; const baseClass = 'bg-border'; const sizeClass = orientation === 'horizontal' ? 'h-[1px] w-full' : 'w-[1px] h-full'; return `${baseClass} ${sizeClass} ${className}`.trim()"
+ }
+ }
+}
diff --git a/src/components/json-definitions/empty-message.json b/src/components/json-definitions/empty-message.json
new file mode 100644
index 0000000..b868219
--- /dev/null
+++ b/src/components/json-definitions/empty-message.json
@@ -0,0 +1,84 @@
+{
+ "id": "empty-message-container",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": ["className"],
+ "transform": "const className = data[0] || ''; const baseClasses = 'flex flex-col items-center justify-center text-center p-8 rounded-lg border border-dashed bg-muted/20'; return `${baseClasses} ${className}`.trim()"
+ }
+ },
+ "children": [
+ {
+ "id": "empty-message-icon",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'mb-4 text-muted-foreground/50'"
+ },
+ "children": {
+ "source": "icon",
+ "transform": "data"
+ },
+ "_if": {
+ "source": "icon",
+ "transform": "!!data"
+ }
+ }
+ },
+ {
+ "id": "empty-message-title",
+ "type": "h3",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'text-lg font-semibold mb-2'"
+ },
+ "children": {
+ "source": "title",
+ "transform": "data"
+ }
+ }
+ },
+ {
+ "id": "empty-message-description",
+ "type": "p",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'text-sm text-muted-foreground mb-4 max-w-sm'"
+ },
+ "children": {
+ "source": "description",
+ "transform": "data"
+ },
+ "_if": {
+ "source": "description",
+ "transform": "!!data"
+ }
+ }
+ },
+ {
+ "id": "empty-message-button",
+ "type": "Button",
+ "bindings": {
+ "size": {
+ "source": null,
+ "transform": "'sm'"
+ },
+ "onClick": {
+ "source": "action",
+ "transform": "data ? data.onClick : null"
+ },
+ "children": {
+ "source": "action",
+ "transform": "data ? data.label : null"
+ },
+ "_if": {
+ "source": "action",
+ "transform": "!!data"
+ }
+ }
+ }
+ ]
+}
diff --git a/src/components/json-definitions/error-badge.json b/src/components/json-definitions/error-badge.json
new file mode 100644
index 0000000..a6b20b9
--- /dev/null
+++ b/src/components/json-definitions/error-badge.json
@@ -0,0 +1,22 @@
+{
+ "id": "error-badge",
+ "type": "Badge",
+ "bindings": {
+ "variant": {
+ "source": "variant",
+ "transform": "data || 'destructive'"
+ },
+ "className": {
+ "source": ["count", "size"],
+ "transform": "const count = data[0]; const size = data[1] || 'md'; const sizeClasses = { sm: 'h-5 w-5 text-[10px]', md: 'h-6 w-6 text-xs' }; return `${sizeClasses[size]} p-0 flex items-center justify-center absolute -top-1 -right-1`"
+ },
+ "children": {
+ "source": "count",
+ "transform": "data"
+ },
+ "_if": {
+ "source": "count",
+ "transform": "data !== 0"
+ }
+ }
+}
diff --git a/src/components/json-definitions/file-icon.json b/src/components/json-definitions/file-icon.json
new file mode 100644
index 0000000..669b387
--- /dev/null
+++ b/src/components/json-definitions/file-icon.json
@@ -0,0 +1,22 @@
+{
+ "id": "file-icon",
+ "type": "FileIconWrapper",
+ "bindings": {
+ "type": {
+ "source": "type",
+ "transform": "data || 'code'"
+ },
+ "size": {
+ "source": "size",
+ "transform": "data || 20"
+ },
+ "weight": {
+ "source": "weight",
+ "transform": "data || 'regular'"
+ },
+ "className": {
+ "source": "className",
+ "transform": "data || ''"
+ }
+ }
+}
diff --git a/src/components/json-definitions/glow-card.json b/src/components/json-definitions/glow-card.json
new file mode 100644
index 0000000..e346e12
--- /dev/null
+++ b/src/components/json-definitions/glow-card.json
@@ -0,0 +1,18 @@
+{
+ "id": "glow-card",
+ "type": "Card",
+ "bindings": {
+ "onClick": {
+ "source": "onClick",
+ "transform": "data"
+ },
+ "className": {
+ "source": ["glowColor", "intensity", "className", "onClick"],
+ "transform": "const glowColor = data[0] || 'primary'; const intensity = data[1] || 'medium'; const className = data[2] || ''; const hasClick = data[3]; const glowClasses = { primary: { low: 'shadow-primary/10', medium: 'shadow-primary/20 hover:shadow-primary/30', high: 'shadow-primary/30 hover:shadow-primary/50' }, accent: { low: 'shadow-accent/10', medium: 'shadow-accent/20 hover:shadow-accent/30', high: 'shadow-accent/30 hover:shadow-accent/50' }, success: { low: 'shadow-green-500/10', medium: 'shadow-green-500/20 hover:shadow-green-500/30', high: 'shadow-green-500/30 hover:shadow-green-500/50' }, warning: { low: 'shadow-yellow-500/10', medium: 'shadow-yellow-500/20 hover:shadow-yellow-500/30', high: 'shadow-yellow-500/30 hover:shadow-yellow-500/50' }, error: { low: 'shadow-red-500/10', medium: 'shadow-red-500/20 hover:shadow-red-500/30', high: 'shadow-red-500/30 hover:shadow-red-500/50' } }; const baseClasses = 'transition-all duration-300 shadow-lg'; const glow = glowClasses[glowColor][intensity]; const clickClass = hasClick ? 'cursor-pointer hover:scale-[1.02]' : ''; return `${baseClasses} ${glow} ${clickClass} ${className}`.trim()"
+ }
+ },
+ "children": {
+ "source": "children",
+ "transform": "data"
+ }
+}
diff --git a/src/components/json-definitions/helper-text.json b/src/components/json-definitions/helper-text.json
new file mode 100644
index 0000000..15ebfd4
--- /dev/null
+++ b/src/components/json-definitions/helper-text.json
@@ -0,0 +1,14 @@
+{
+ "id": "helper-text",
+ "type": "p",
+ "bindings": {
+ "className": {
+ "source": ["variant", "className"],
+ "transform": "const variant = data[0] || 'default'; const className = data[1] || ''; const baseClass = 'text-xs mt-1'; const variantClasses = { default: 'text-muted-foreground', error: 'text-destructive', success: 'text-green-600' }; return `${baseClass} ${variantClasses[variant]} ${className}`.trim()"
+ },
+ "children": {
+ "source": "children",
+ "transform": "data"
+ }
+ }
+}
diff --git a/src/components/json-definitions/info-box.json b/src/components/json-definitions/info-box.json
new file mode 100644
index 0000000..bc3a197
--- /dev/null
+++ b/src/components/json-definitions/info-box.json
@@ -0,0 +1,70 @@
+{
+ "id": "info-box-container",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": ["type", "className"],
+ "transform": "const type = data[0] || 'info'; const customClass = data[1] || ''; const variantClasses = { info: 'bg-blue-500/10 border-blue-500/20 text-blue-700 dark:text-blue-300', warning: 'bg-yellow-500/10 border-yellow-500/20 text-yellow-700 dark:text-yellow-300', success: 'bg-green-500/10 border-green-500/20 text-green-700 dark:text-green-300', error: 'bg-destructive/10 border-destructive/20 text-destructive' }; return `flex gap-3 p-4 rounded-lg border ${variantClasses[type]} ${customClass}`.trim()"
+ }
+ },
+ "children": [
+ {
+ "id": "info-box-icon",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'flex-shrink-0 mt-0.5'"
+ },
+ "children": {
+ "source": "type",
+ "transform": "const type = data || 'info'; const icons = { info: '', warning: '', success: '', error: '' }; return icons[type] || icons.info"
+ }
+ }
+ },
+ {
+ "id": "info-box-content",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'flex-1 min-w-0'"
+ }
+ },
+ "children": [
+ {
+ "id": "info-box-title",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'font-semibold mb-1'"
+ },
+ "children": {
+ "source": "title",
+ "transform": "data"
+ },
+ "_if": {
+ "source": "title",
+ "transform": "!!data"
+ }
+ }
+ },
+ {
+ "id": "info-box-children",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'text-sm opacity-90'"
+ },
+ "children": {
+ "source": "children",
+ "transform": "data"
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/components/json-definitions/key-value.json b/src/components/json-definitions/key-value.json
new file mode 100644
index 0000000..87e3a58
--- /dev/null
+++ b/src/components/json-definitions/key-value.json
@@ -0,0 +1,40 @@
+{
+ "id": "key-value-container",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": ["orientation", "className"],
+ "transform": "const orientation = data[0] || 'horizontal'; const customClass = data[1] || ''; const orientationClass = orientation === 'vertical' ? 'flex-col' : 'flex-row items-center justify-between'; return `flex gap-2 ${orientationClass} ${customClass}`.trim()"
+ }
+ },
+ "children": [
+ {
+ "id": "key-value-label",
+ "type": "span",
+ "bindings": {
+ "className": {
+ "source": "labelClassName",
+ "transform": "const customClass = data || ''; return `text-sm text-muted-foreground ${customClass}`.trim()"
+ },
+ "children": {
+ "source": "label",
+ "transform": "data"
+ }
+ }
+ },
+ {
+ "id": "key-value-value",
+ "type": "span",
+ "bindings": {
+ "className": {
+ "source": "valueClassName",
+ "transform": "const customClass = data || ''; return `text-sm font-medium ${customClass}`.trim()"
+ },
+ "children": {
+ "source": "value",
+ "transform": "data"
+ }
+ }
+ }
+ ]
+}
diff --git a/src/components/json-definitions/list-item.json b/src/components/json-definitions/list-item.json
new file mode 100644
index 0000000..8a106e7
--- /dev/null
+++ b/src/components/json-definitions/list-item.json
@@ -0,0 +1,74 @@
+{
+ "id": "list-item-container",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": ["onClick", "active", "className"],
+ "transform": "const onClick = data[0]; const active = data[1]; const customClass = data[2] || ''; const isInteractive = !!onClick; const interactiveClass = isInteractive ? 'cursor-pointer hover:bg-accent' : ''; const activeClass = active ? 'bg-accent' : ''; return `flex items-center gap-3 px-3 py-2 rounded-md transition-colors ${interactiveClass} ${activeClass} ${customClass}`.trim()"
+ },
+ "onClick": {
+ "source": "onClick",
+ "transform": "data"
+ },
+ "role": {
+ "source": "onClick",
+ "transform": "data ? 'button' : undefined"
+ },
+ "tabIndex": {
+ "source": "onClick",
+ "transform": "data ? 0 : undefined"
+ }
+ },
+ "children": [
+ {
+ "id": "list-item-icon",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'flex-shrink-0 text-muted-foreground'"
+ },
+ "children": {
+ "source": "icon",
+ "transform": "data"
+ },
+ "_if": {
+ "source": "icon",
+ "transform": "!!data"
+ }
+ }
+ },
+ {
+ "id": "list-item-content",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'flex-1 min-w-0 text-sm'"
+ },
+ "children": {
+ "source": "children",
+ "transform": "data"
+ }
+ }
+ },
+ {
+ "id": "list-item-end-content",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'flex-shrink-0'"
+ },
+ "children": {
+ "source": "endContent",
+ "transform": "data"
+ },
+ "_if": {
+ "source": "endContent",
+ "transform": "!!data"
+ }
+ }
+ }
+ ]
+}
diff --git a/src/components/json-definitions/list.json b/src/components/json-definitions/list.json
new file mode 100644
index 0000000..b3d2374
--- /dev/null
+++ b/src/components/json-definitions/list.json
@@ -0,0 +1,18 @@
+{
+ "id": "list-container",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": ["items", "className"],
+ "transform": "const items = data[0] || []; const customClass = data[1] || ''; if (items.length === 0) return 'text-center text-muted-foreground py-8'; return customClass"
+ },
+ "_if": {
+ "source": "items",
+ "transform": "data && data.length > 0"
+ }
+ },
+ "children": {
+ "source": "items",
+ "transform": "(items) => items && items.map((item, index) => ({ id: `list-item-${index}`, type: 'div', bindings: { className: { source: 'itemClassName', transform: 'data || \"\"' } }, children: [{ id: `list-item-content-${index}`, type: 'div', bindings: { children: { source: 'renderItem', transform: `(fn) => fn && fn(item, ${index})` } } }] }))"
+ }
+}
diff --git a/src/components/json-definitions/live-indicator.json b/src/components/json-definitions/live-indicator.json
new file mode 100644
index 0000000..ed1658a
--- /dev/null
+++ b/src/components/json-definitions/live-indicator.json
@@ -0,0 +1,62 @@
+{
+ "id": "live-indicator-container",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": ["size", "className"],
+ "transform": "const size = data[0] || 'md'; const customClass = data[1] || ''; const sizeClasses = { sm: 'text-xs gap-1.5', md: 'text-sm gap-2', lg: 'text-base gap-2.5' }; return `inline-flex items-center font-medium ${sizeClasses[size]} ${customClass}`.trim()"
+ }
+ },
+ "children": [
+ {
+ "id": "live-indicator-pulse",
+ "type": "span",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'relative flex'"
+ }
+ },
+ "children": [
+ {
+ "id": "live-indicator-ping",
+ "type": "span",
+ "bindings": {
+ "className": {
+ "source": "size",
+ "transform": "const size = data || 'md'; const dotSizeClasses = { sm: 'w-2 h-2', md: 'w-2.5 h-2.5', lg: 'w-3 h-3' }; return `absolute inline-flex rounded-full bg-red-500 opacity-75 animate-ping ${dotSizeClasses[size]}`.trim()"
+ }
+ }
+ },
+ {
+ "id": "live-indicator-dot",
+ "type": "span",
+ "bindings": {
+ "className": {
+ "source": "size",
+ "transform": "const size = data || 'md'; const dotSizeClasses = { sm: 'w-2 h-2', md: 'w-2.5 h-2.5', lg: 'w-3 h-3' }; return `relative inline-flex rounded-full bg-red-500 ${dotSizeClasses[size]}`.trim()"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "id": "live-indicator-label",
+ "type": "span",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'text-red-500 font-bold tracking-wider'"
+ },
+ "children": {
+ "source": "label",
+ "transform": "data"
+ },
+ "_if": {
+ "source": "showLabel",
+ "transform": "data !== false"
+ }
+ }
+ }
+ ]
+}
diff --git a/src/components/json-definitions/loading-spinner.json b/src/components/json-definitions/loading-spinner.json
new file mode 100644
index 0000000..4760935
--- /dev/null
+++ b/src/components/json-definitions/loading-spinner.json
@@ -0,0 +1,18 @@
+{
+ "id": "loading-spinner",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": ["size", "className"],
+ "transform": "const size = data[0] || 'md'; const customClass = data[1] || ''; const sizeClasses = { sm: 'w-4 h-4 border-2', md: 'w-6 h-6 border-2', lg: 'w-8 h-8 border-3' }; return `inline-block ${sizeClasses[size]} border-primary border-t-transparent rounded-full animate-spin ${customClass}`.trim()"
+ },
+ "role": {
+ "source": null,
+ "transform": "'status'"
+ },
+ "aria-label": {
+ "source": null,
+ "transform": "'Loading'"
+ }
+ }
+}
diff --git a/src/components/json-definitions/loading-state.json b/src/components/json-definitions/loading-state.json
new file mode 100644
index 0000000..4664e56
--- /dev/null
+++ b/src/components/json-definitions/loading-state.json
@@ -0,0 +1,40 @@
+{
+ "id": "loading-state-container",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": "className",
+ "transform": "const customClass = data || ''; return `flex flex-col items-center justify-center gap-3 py-8 ${customClass}`.trim()"
+ }
+ },
+ "children": [
+ {
+ "id": "loading-state-spinner",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": "size",
+ "transform": "const size = data || 'md'; const sizeClasses = { sm: 'w-4 h-4 border-2', md: 'w-8 h-8 border-3', lg: 'w-12 h-12 border-4' }; return `border-primary border-t-transparent rounded-full animate-spin ${sizeClasses[size]}`.trim()"
+ }
+ }
+ },
+ {
+ "id": "loading-state-message",
+ "type": "p",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'text-sm text-muted-foreground'"
+ },
+ "children": {
+ "source": "message",
+ "transform": "data"
+ },
+ "_if": {
+ "source": "message",
+ "transform": "!!data"
+ }
+ }
+ }
+ ]
+}
diff --git a/src/components/json-definitions/metric-display.json b/src/components/json-definitions/metric-display.json
new file mode 100644
index 0000000..af439ce
--- /dev/null
+++ b/src/components/json-definitions/metric-display.json
@@ -0,0 +1,96 @@
+{
+ "id": "metric-display-container",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": "className",
+ "transform": "const customClass = data || ''; return `flex flex-col gap-1 ${customClass}`.trim()"
+ }
+ },
+ "children": [
+ {
+ "id": "metric-display-label",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'flex items-center gap-2 text-sm text-muted-foreground'"
+ }
+ },
+ "children": [
+ {
+ "id": "metric-display-icon",
+ "type": "span",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'text-muted-foreground'"
+ },
+ "children": {
+ "source": "icon",
+ "transform": "data"
+ },
+ "_if": {
+ "source": "icon",
+ "transform": "!!data"
+ }
+ }
+ },
+ {
+ "id": "metric-display-label-text",
+ "type": "span",
+ "bindings": {
+ "children": {
+ "source": "label",
+ "transform": "data"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "id": "metric-display-value-container",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'flex items-baseline gap-2'"
+ }
+ },
+ "children": [
+ {
+ "id": "metric-display-value",
+ "type": "span",
+ "bindings": {
+ "className": {
+ "source": "variant",
+ "transform": "const variant = data || 'default'; const variantClasses = { default: 'text-foreground', primary: 'text-primary', accent: 'text-accent' }; return `text-2xl font-bold ${variantClasses[variant]}`.trim()"
+ },
+ "children": {
+ "source": "value",
+ "transform": "data"
+ }
+ }
+ },
+ {
+ "id": "metric-display-trend",
+ "type": "span",
+ "bindings": {
+ "className": {
+ "source": "trend",
+ "transform": "const trend = data; const direction = trend?.direction; const trendClass = direction === 'up' ? 'text-green-600 dark:text-green-400' : 'text-destructive'; return `flex items-center gap-0.5 text-xs font-medium ${trendClass}`.trim()"
+ },
+ "children": {
+ "source": "trend",
+ "transform": "const trend = data; const value = Math.abs(trend?.value || 0); const icon = trend?.direction === 'up' ? '↑' : '↓'; return `${icon} ${value}%`"
+ },
+ "_if": {
+ "source": "trend",
+ "transform": "!!data"
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/components/json-definitions/modal.json b/src/components/json-definitions/modal.json
new file mode 100644
index 0000000..17d4016
--- /dev/null
+++ b/src/components/json-definitions/modal.json
@@ -0,0 +1,122 @@
+{
+ "id": "modal-overlay",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": "isOpen",
+ "transform": "data ? 'fixed inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-sm animate-in fade-in-0' : 'hidden'"
+ },
+ "onClick": {
+ "source": "onClose",
+ "transform": "data"
+ },
+ "_if": {
+ "source": "isOpen",
+ "transform": "data"
+ }
+ },
+ "children": [
+ {
+ "id": "modal-content",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": ["size", "className"],
+ "transform": "const size = data[0] || 'md'; const customClass = data[1] || ''; const sizeStyles = { sm: 'max-w-sm', md: 'max-w-md', lg: 'max-w-lg', xl: 'max-w-xl', full: 'max-w-full m-4' }; return `relative w-full bg-card border border-border rounded-lg shadow-lg animate-in zoom-in-95 ${sizeStyles[size]} ${customClass}`.trim()"
+ },
+ "onClick": {
+ "source": null,
+ "transform": "(e) => e.stopPropagation()"
+ }
+ },
+ "children": [
+ {
+ "id": "modal-header",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'flex items-center justify-between p-6 border-b border-border'"
+ },
+ "_if": {
+ "source": ["title", "showCloseButton"],
+ "transform": "!!data[0] || data[1] !== false"
+ }
+ },
+ "children": [
+ {
+ "id": "modal-title",
+ "type": "h2",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'text-lg font-semibold'"
+ },
+ "children": {
+ "source": "title",
+ "transform": "data"
+ },
+ "_if": {
+ "source": "title",
+ "transform": "!!data"
+ }
+ }
+ },
+ {
+ "id": "modal-close-button",
+ "type": "button",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'ml-auto p-1 rounded-md hover:bg-accent transition-colors'"
+ },
+ "onClick": {
+ "source": "onClose",
+ "transform": "data"
+ },
+ "aria-label": {
+ "source": null,
+ "transform": "'Close modal'"
+ },
+ "_if": {
+ "source": "showCloseButton",
+ "transform": "data !== false"
+ }
+ },
+ "children": [
+ {
+ "id": "modal-close-icon",
+ "type": "span",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'w-5 h-5'"
+ },
+ "children": {
+ "source": null,
+ "transform": "'✕'"
+ }
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "id": "modal-body",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'p-6'"
+ },
+ "children": {
+ "source": "children",
+ "transform": "data"
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/components/json-definitions/notification.json b/src/components/json-definitions/notification.json
new file mode 100644
index 0000000..010df8a
--- /dev/null
+++ b/src/components/json-definitions/notification.json
@@ -0,0 +1,108 @@
+{
+ "id": "notification-container",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": ["type", "className"],
+ "transform": "const type = data[0]; const customClass = data[1] || ''; const config = { info: { bg: 'bg-blue-500/10', border: 'border-blue-500/20', color: 'text-blue-500' }, success: { bg: 'bg-accent/10', border: 'border-accent/20', color: 'text-accent' }, warning: { bg: 'bg-yellow-500/10', border: 'border-yellow-500/20', color: 'text-yellow-500' }, error: { bg: 'bg-destructive/10', border: 'border-destructive/20', color: 'text-destructive' } }; const c = config[type]; return `flex gap-3 p-4 rounded-lg border ${c.bg} ${c.border} ${customClass}`.trim()"
+ }
+ },
+ "children": [
+ {
+ "id": "notification-icon",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": "type",
+ "transform": "const type = data; const colors = { info: 'text-blue-500', success: 'text-accent', warning: 'text-yellow-500', error: 'text-destructive' }; return `w-5 h-5 flex-shrink-0 ${colors[type]}`.trim()"
+ },
+ "children": {
+ "source": "type",
+ "transform": "const type = data; const icons = { info: 'ℹ', success: '✓', warning: '⚠', error: '✕' }; return icons[type]"
+ }
+ }
+ },
+ {
+ "id": "notification-content",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'flex-1 min-w-0'"
+ }
+ },
+ "children": [
+ {
+ "id": "notification-title",
+ "type": "h4",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'font-medium text-sm'"
+ },
+ "children": {
+ "source": "title",
+ "transform": "data"
+ }
+ }
+ },
+ {
+ "id": "notification-message",
+ "type": "p",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'text-sm text-muted-foreground mt-1'"
+ },
+ "children": {
+ "source": "message",
+ "transform": "data"
+ },
+ "_if": {
+ "source": "message",
+ "transform": "!!data"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "id": "notification-close",
+ "type": "button",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'flex-shrink-0 text-muted-foreground hover:text-foreground transition-colors'"
+ },
+ "onClick": {
+ "source": "onClose",
+ "transform": "data"
+ },
+ "aria-label": {
+ "source": null,
+ "transform": "'Close notification'"
+ },
+ "_if": {
+ "source": "onClose",
+ "transform": "!!data"
+ }
+ },
+ "children": [
+ {
+ "id": "notification-close-icon",
+ "type": "span",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'w-4 h-4'"
+ },
+ "children": {
+ "source": null,
+ "transform": "'✕'"
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/components/json-definitions/number-input.json b/src/components/json-definitions/number-input.json
new file mode 100644
index 0000000..594845a
--- /dev/null
+++ b/src/components/json-definitions/number-input.json
@@ -0,0 +1,148 @@
+{
+ "id": "number-input-container",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": "className",
+ "transform": "const customClass = data || ''; return `flex flex-col gap-2 ${customClass}`.trim()"
+ }
+ },
+ "children": [
+ {
+ "id": "number-input-label",
+ "type": "label",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'text-sm font-medium text-foreground'"
+ },
+ "children": {
+ "source": "label",
+ "transform": "data"
+ },
+ "_if": {
+ "source": "label",
+ "transform": "!!data"
+ }
+ }
+ },
+ {
+ "id": "number-input-controls",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'flex items-center gap-2'"
+ }
+ },
+ "children": [
+ {
+ "id": "number-input-decrement",
+ "type": "button",
+ "bindings": {
+ "type": {
+ "source": null,
+ "transform": "'button'"
+ },
+ "className": {
+ "source": ["disabled", "min", "value"],
+ "transform": "const disabled = data[0]; const min = data[1]; const value = data[2]; const isAtMin = min !== undefined && value <= min; return `h-9 w-9 shrink-0 px-3 py-2 rounded-md border border-input bg-background hover:bg-accent hover:text-accent-foreground disabled:opacity-50 disabled:cursor-not-allowed transition-colors ${disabled || isAtMin ? 'opacity-50 cursor-not-allowed' : ''}`.trim()"
+ },
+ "onClick": {
+ "source": null,
+ "transform": "() => { /* handleDecrement */ }"
+ },
+ "disabled": {
+ "source": ["disabled", "min", "value"],
+ "transform": "data[0] || (data[1] !== undefined && data[2] <= data[1])"
+ }
+ },
+ "children": [
+ {
+ "id": "number-input-decrement-icon",
+ "type": "span",
+ "bindings": {
+ "children": {
+ "source": null,
+ "transform": "'−'"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "id": "number-input-field",
+ "type": "input",
+ "bindings": {
+ "type": {
+ "source": null,
+ "transform": "'number'"
+ },
+ "value": {
+ "source": "value",
+ "transform": "data"
+ },
+ "onChange": {
+ "source": null,
+ "transform": "(e) => { /* handleInputChange */ }"
+ },
+ "min": {
+ "source": "min",
+ "transform": "data"
+ },
+ "max": {
+ "source": "max",
+ "transform": "data"
+ },
+ "step": {
+ "source": "step",
+ "transform": "data || 1"
+ },
+ "disabled": {
+ "source": "disabled",
+ "transform": "data || false"
+ },
+ "className": {
+ "source": null,
+ "transform": "'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-center placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 transition-colors'"
+ }
+ }
+ },
+ {
+ "id": "number-input-increment",
+ "type": "button",
+ "bindings": {
+ "type": {
+ "source": null,
+ "transform": "'button'"
+ },
+ "className": {
+ "source": ["disabled", "max", "value"],
+ "transform": "const disabled = data[0]; const max = data[1]; const value = data[2]; const isAtMax = max !== undefined && value >= max; return `h-9 w-9 shrink-0 px-3 py-2 rounded-md border border-input bg-background hover:bg-accent hover:text-accent-foreground disabled:opacity-50 disabled:cursor-not-allowed transition-colors ${disabled || isAtMax ? 'opacity-50 cursor-not-allowed' : ''}`.trim()"
+ },
+ "onClick": {
+ "source": null,
+ "transform": "() => { /* handleIncrement */ }"
+ },
+ "disabled": {
+ "source": ["disabled", "max", "value"],
+ "transform": "data[0] || (data[1] !== undefined && data[2] >= data[1])"
+ }
+ },
+ "children": [
+ {
+ "id": "number-input-increment-icon",
+ "type": "span",
+ "bindings": {
+ "children": {
+ "source": null,
+ "transform": "'+'"
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/components/json-definitions/page-header.json b/src/components/json-definitions/page-header.json
new file mode 100644
index 0000000..951479d
--- /dev/null
+++ b/src/components/json-definitions/page-header.json
@@ -0,0 +1,74 @@
+{
+ "id": "page-header-wrapper",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": "className",
+ "transform": "const baseClass = 'flex items-start justify-between mb-6'; return data ? `${baseClass} ${data}` : baseClass"
+ }
+ },
+ "children": [
+ {
+ "id": "page-header-content",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'space-y-1'"
+ }
+ },
+ "children": [
+ {
+ "id": "page-header-title",
+ "type": "h1",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'text-3xl font-bold tracking-tight'"
+ },
+ "children": {
+ "source": "title",
+ "transform": "data"
+ }
+ }
+ },
+ {
+ "id": "page-header-description",
+ "type": "p",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'text-muted-foreground'"
+ },
+ "children": {
+ "source": "description",
+ "transform": "data"
+ },
+ "_if": {
+ "source": "description",
+ "transform": "!!data"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "id": "page-header-actions",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'flex gap-2'"
+ },
+ "children": {
+ "source": "actions",
+ "transform": "data"
+ },
+ "_if": {
+ "source": "actions",
+ "transform": "!!data"
+ }
+ }
+ }
+ ]
+}
diff --git a/src/components/json-definitions/progress-bar.json b/src/components/json-definitions/progress-bar.json
new file mode 100644
index 0000000..75d093e
--- /dev/null
+++ b/src/components/json-definitions/progress-bar.json
@@ -0,0 +1,72 @@
+{
+ "id": "progress-bar-wrapper",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'w-full'"
+ }
+ },
+ "children": [
+ {
+ "id": "progress-bar-container",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": ["value", "max", "size", "variant", "className"],
+ "transform": "const value = data[0] || 0; const max = data[1] || 100; const size = data[2] || 'md'; const variant = data[3] || 'default'; const className = data[4] || ''; const sizeClasses = { sm: 'h-1', md: 'h-2', lg: 'h-3' }; const variantClasses = { default: 'bg-primary', accent: 'bg-accent', destructive: 'bg-destructive' }; const baseClass = 'relative w-full bg-secondary rounded-full overflow-hidden'; return `${baseClass} ${sizeClasses[size]} ${className}`.trim()"
+ },
+ "role": {
+ "source": null,
+ "transform": "'progressbar'"
+ },
+ "aria-valuenow": {
+ "source": "value",
+ "transform": "data"
+ },
+ "aria-valuemin": {
+ "source": null,
+ "transform": "0"
+ },
+ "aria-valuemax": {
+ "source": "max",
+ "transform": "data || 100"
+ }
+ },
+ "children": [
+ {
+ "id": "progress-bar-fill",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": "variant",
+ "transform": "const variantClasses = { default: 'bg-primary', accent: 'bg-accent', destructive: 'bg-destructive' }; return `h-full transition-all duration-300 ease-out ${variantClasses[data] || variantClasses['default']}`"
+ },
+ "style": {
+ "source": ["value", "max"],
+ "transform": "const value = data[0] || 0; const max = data[1] || 100; const percentage = Math.min(Math.max((value / max) * 100, 0), 100); return { width: `${percentage}%` }"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "id": "progress-bar-label",
+ "type": "span",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'text-xs text-muted-foreground mt-1 block'"
+ },
+ "children": {
+ "source": ["value", "max"],
+ "transform": "const value = data[0] || 0; const max = data[1] || 100; const percentage = Math.min(Math.max((value / max) * 100, 0), 100); return `${Math.round(percentage)}%`"
+ },
+ "_if": {
+ "source": "showLabel",
+ "transform": "!!data"
+ }
+ }
+ }
+ ]
+}
diff --git a/src/components/json-definitions/pulse.json b/src/components/json-definitions/pulse.json
new file mode 100644
index 0000000..e94365e
--- /dev/null
+++ b/src/components/json-definitions/pulse.json
@@ -0,0 +1,32 @@
+{
+ "id": "pulse-wrapper",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": ["variant", "size", "speed", "className"],
+ "transform": "const variant = data[0] || 'primary'; const size = data[1] || 'md'; const speed = data[2] || 'normal'; const className = data[3] || ''; const sizeClasses = { sm: 'w-2 h-2', md: 'w-3 h-3', lg: 'w-4 h-4' }; const variantClasses = { primary: 'bg-primary', accent: 'bg-accent', success: 'bg-green-500', warning: 'bg-yellow-500', error: 'bg-red-500' }; const speedClasses = { slow: 'animate-pulse [animation-duration:3s]', normal: 'animate-pulse', fast: 'animate-pulse [animation-duration:0.5s]' }; const baseClass = 'relative inline-flex'; return `${baseClass} ${className}`.trim()"
+ }
+ },
+ "children": [
+ {
+ "id": "pulse-inner-1",
+ "type": "span",
+ "bindings": {
+ "className": {
+ "source": ["variant", "size", "speed"],
+ "transform": "const variant = data[0] || 'primary'; const size = data[1] || 'md'; const speed = data[2] || 'normal'; const sizeClasses = { sm: 'w-2 h-2', md: 'w-3 h-3', lg: 'w-4 h-4' }; const variantClasses = { primary: 'bg-primary', accent: 'bg-accent', success: 'bg-green-500', warning: 'bg-yellow-500', error: 'bg-red-500' }; const speedClasses = { slow: 'animate-pulse [animation-duration:3s]', normal: 'animate-pulse', fast: 'animate-pulse [animation-duration:0.5s]' }; return `inline-flex rounded-full opacity-75 ${sizeClasses[size]} ${variantClasses[variant]} ${speedClasses[speed]}`"
+ }
+ }
+ },
+ {
+ "id": "pulse-inner-2",
+ "type": "span",
+ "bindings": {
+ "className": {
+ "source": ["variant", "size", "speed"],
+ "transform": "const variant = data[0] || 'primary'; const size = data[1] || 'md'; const speed = data[2] || 'normal'; const sizeClasses = { sm: 'w-2 h-2', md: 'w-3 h-3', lg: 'w-4 h-4' }; const variantClasses = { primary: 'bg-primary', accent: 'bg-accent', success: 'bg-green-500', warning: 'bg-yellow-500', error: 'bg-red-500' }; const speedClasses = { slow: 'animate-pulse [animation-duration:3s]', normal: 'animate-pulse', fast: 'animate-pulse [animation-duration:0.5s]' }; return `absolute inline-flex rounded-full opacity-75 ${sizeClasses[size]} ${variantClasses[variant]} ${speedClasses[speed]}`"
+ }
+ }
+ }
+ ]
+}
diff --git a/src/components/json-definitions/quick-action-button.json b/src/components/json-definitions/quick-action-button.json
new file mode 100644
index 0000000..7d206f6
--- /dev/null
+++ b/src/components/json-definitions/quick-action-button.json
@@ -0,0 +1,74 @@
+{
+ "id": "quick-action-button-card",
+ "type": "Card",
+ "bindings": {
+ "onClick": {
+ "source": ["onClick", "disabled"],
+ "transform": "const onClick = data[0]; const disabled = data[1]; return disabled ? undefined : onClick"
+ },
+ "className": {
+ "source": ["variant", "disabled", "className"],
+ "transform": "const variant = data[0] || 'default'; const disabled = data[1]; const className = data[2] || ''; const variantClasses = { default: 'hover:bg-muted/50 hover:border-border', primary: 'hover:bg-primary/10 hover:border-primary/50', accent: 'hover:bg-accent/10 hover:border-accent/50', muted: 'bg-muted hover:bg-muted/70' }; const baseClass = 'p-6 cursor-pointer transition-all duration-200 flex flex-col items-center justify-center gap-3 text-center hover:scale-105 active:scale-95'; const disabledClass = disabled ? 'opacity-50 cursor-not-allowed hover:scale-100' : ''; return `${baseClass} ${variantClasses[variant]} ${disabledClass} ${className}`.trim()"
+ }
+ },
+ "children": [
+ {
+ "id": "quick-action-button-icon",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": "variant",
+ "transform": "const iconColorClasses = { default: 'text-foreground', primary: 'text-primary', accent: 'text-accent', muted: 'text-muted-foreground' }; return `text-4xl ${iconColorClasses[data] || iconColorClasses['default']}`"
+ },
+ "children": {
+ "source": "icon",
+ "transform": "data"
+ }
+ }
+ },
+ {
+ "id": "quick-action-button-content",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'space-y-1'"
+ }
+ },
+ "children": [
+ {
+ "id": "quick-action-button-label",
+ "type": "h3",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'font-semibold text-foreground'"
+ },
+ "children": {
+ "source": "label",
+ "transform": "data"
+ }
+ }
+ },
+ {
+ "id": "quick-action-button-description",
+ "type": "p",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'text-sm text-muted-foreground'"
+ },
+ "children": {
+ "source": "description",
+ "transform": "data"
+ },
+ "_if": {
+ "source": "description",
+ "transform": "!!data"
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/components/json-definitions/search-input.json b/src/components/json-definitions/search-input.json
new file mode 100644
index 0000000..f1514dd
--- /dev/null
+++ b/src/components/json-definitions/search-input.json
@@ -0,0 +1,46 @@
+{
+ "id": "search-input-wrapper",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": "className",
+ "transform": "data || ''"
+ }
+ },
+ "children": [
+ {
+ "id": "search-input-field",
+ "type": "Input",
+ "bindings": {
+ "type": {
+ "source": null,
+ "transform": "'text'"
+ },
+ "value": {
+ "source": "value",
+ "transform": "data"
+ },
+ "onChange": {
+ "source": "onChange",
+ "transform": "(e) => data(e.target.value)"
+ },
+ "placeholder": {
+ "source": "placeholder",
+ "transform": "data || 'Search...'"
+ },
+ "className": {
+ "source": null,
+ "transform": "''"
+ },
+ "leftIcon": {
+ "source": null,
+ "transform": "{ type: 'MagnifyingGlass', size: 18 }"
+ },
+ "rightIcon": {
+ "source": ["value", "onClear"],
+ "transform": "const value = data[0]; const onClear = data[1]; if (!value) return null; return { type: 'button', onClick: () => { data[1]?.(); }, className: 'text-muted-foreground hover:text-foreground transition-colors', children: { type: 'X', size: 18 } }"
+ }
+ }
+ }
+ ]
+}
diff --git a/src/components/json-definitions/seed-data-status.json b/src/components/json-definitions/seed-data-status.json
new file mode 100644
index 0000000..ad38c79
--- /dev/null
+++ b/src/components/json-definitions/seed-data-status.json
@@ -0,0 +1,126 @@
+{
+ "id": "seed-data-status-card",
+ "type": "Card",
+ "bindings": {
+ "className": {
+ "source": "className",
+ "transform": "data || ''"
+ }
+ },
+ "children": [
+ {
+ "id": "seed-data-status-header",
+ "type": "CardHeader",
+ "children": [
+ {
+ "id": "seed-data-status-title",
+ "type": "CardTitle",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'flex items-center gap-2 text-lg'"
+ }
+ },
+ "children": [
+ {
+ "id": "seed-data-status-icon",
+ "type": "Database",
+ "bindings": {
+ "size": {
+ "source": null,
+ "transform": "20"
+ },
+ "weight": {
+ "source": null,
+ "transform": "'duotone'"
+ }
+ }
+ },
+ {
+ "id": "seed-data-status-text",
+ "type": "span",
+ "bindings": {
+ "children": {
+ "source": null,
+ "transform": "'Seed Data Available'"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "id": "seed-data-status-description",
+ "type": "CardDescription",
+ "bindings": {
+ "children": {
+ "source": null,
+ "transform": "'Pre-configured data ready to load from database'"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "id": "seed-data-status-content",
+ "type": "CardContent",
+ "children": [
+ {
+ "id": "seed-data-status-grid",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'grid grid-cols-2 md:grid-cols-3 gap-3'"
+ }
+ },
+ "children": [
+ {
+ "id": "seed-data-status-item",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'flex items-center justify-between p-3 rounded-lg border border-border bg-muted/50'"
+ }
+ },
+ "children": [
+ {
+ "id": "seed-data-status-label",
+ "type": "span",
+ "bindings": {
+ "className": {
+ "source": null,
+ "transform": "'text-sm font-medium'"
+ },
+ "children": {
+ "source": "label",
+ "transform": "data"
+ }
+ }
+ },
+ {
+ "id": "seed-data-status-badge",
+ "type": "Badge",
+ "bindings": {
+ "variant": {
+ "source": null,
+ "transform": "'secondary'"
+ },
+ "className": {
+ "source": null,
+ "transform": "'ml-2'"
+ },
+ "children": {
+ "source": "count",
+ "transform": "data"
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/components/json-definitions/sparkle.json b/src/components/json-definitions/sparkle.json
new file mode 100644
index 0000000..1891b89
--- /dev/null
+++ b/src/components/json-definitions/sparkle.json
@@ -0,0 +1,18 @@
+{
+ "id": "sparkle-icon",
+ "type": "Sparkle",
+ "bindings": {
+ "size": {
+ "source": "size",
+ "transform": "data || 16"
+ },
+ "weight": {
+ "source": null,
+ "transform": "'fill'"
+ },
+ "className": {
+ "source": ["variant", "animate", "className"],
+ "transform": "const variant = data[0] || 'default'; const animate = data[1]; const className = data[2] || ''; const variantClasses = { default: 'text-foreground', primary: 'text-primary', accent: 'text-accent', gold: 'text-yellow-500' }; const animateClass = animate ? 'animate-pulse' : ''; return `${variantClasses[variant]} ${animateClass} ${className}`.trim()"
+ }
+ }
+}
diff --git a/src/components/json-definitions/tab-icon.json b/src/components/json-definitions/tab-icon.json
new file mode 100644
index 0000000..e7843e7
--- /dev/null
+++ b/src/components/json-definitions/tab-icon.json
@@ -0,0 +1,16 @@
+{
+ "id": "tab-icon",
+ "type": "div",
+ "bindings": {
+ "className": {
+ "source": "variant",
+ "transform": "data === 'gradient' ? 'w-10 h-10 rounded-lg bg-gradient-to-br from-primary/20 to-accent/20 flex items-center justify-center text-primary shrink-0' : ''"
+ }
+ },
+ "children": [
+ {
+ "type": "slot",
+ "name": "icon"
+ }
+ ]
+}
diff --git a/src/components/json-definitions/tips-card.json b/src/components/json-definitions/tips-card.json
new file mode 100644
index 0000000..e1abfc7
--- /dev/null
+++ b/src/components/json-definitions/tips-card.json
@@ -0,0 +1,61 @@
+{
+ "id": "tips-card",
+ "type": "Card",
+ "bindings": {
+ "className": "bg-yellow-500/10 border-yellow-500/20"
+ },
+ "children": [
+ {
+ "id": "tips-card-header",
+ "type": "CardHeader",
+ "children": [
+ {
+ "id": "tips-card-title",
+ "type": "CardTitle",
+ "bindings": {
+ "className": "flex items-center gap-2"
+ },
+ "children": [
+ {
+ "type": "Icon",
+ "bindings": {
+ "name": "warning",
+ "weight": "duotone",
+ "size": 24,
+ "className": "text-yellow-500"
+ }
+ },
+ {
+ "type": "Text",
+ "bindings": {
+ "content": "Quick Tips"
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "id": "tips-card-content",
+ "type": "CardContent",
+ "bindings": {
+ "className": "space-y-2 text-sm"
+ },
+ "children": [
+ {
+ "type": "forEach",
+ "source": "tips",
+ "condition": "item.show",
+ "children": [
+ {
+ "type": "Text",
+ "bindings": {
+ "content": "• {item.message}"
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/lib/json-ui/interfaces/binding-indicator.ts b/src/lib/json-ui/interfaces/binding-indicator.ts
new file mode 100644
index 0000000..45e88a5
--- /dev/null
+++ b/src/lib/json-ui/interfaces/binding-indicator.ts
@@ -0,0 +1,5 @@
+export interface BindingIndicatorProps {
+ sourceId: string
+ path?: string
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/button-group.ts b/src/lib/json-ui/interfaces/button-group.ts
new file mode 100644
index 0000000..d5b120e
--- /dev/null
+++ b/src/lib/json-ui/interfaces/button-group.ts
@@ -0,0 +1,7 @@
+import { ReactNode } from 'react'
+
+export interface ButtonGroupProps {
+ children: ReactNode
+ orientation?: 'horizontal' | 'vertical'
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/chip.ts b/src/lib/json-ui/interfaces/chip.ts
new file mode 100644
index 0000000..9825434
--- /dev/null
+++ b/src/lib/json-ui/interfaces/chip.ts
@@ -0,0 +1,9 @@
+import { ReactNode } from 'react'
+
+export interface ChipProps {
+ children: ReactNode
+ variant?: 'default' | 'primary' | 'accent' | 'muted'
+ size?: 'sm' | 'md'
+ onRemove?: () => void
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/circular-progress.ts b/src/lib/json-ui/interfaces/circular-progress.ts
new file mode 100644
index 0000000..8f28381
--- /dev/null
+++ b/src/lib/json-ui/interfaces/circular-progress.ts
@@ -0,0 +1,8 @@
+export interface CircularProgressProps {
+ value: number
+ max?: number
+ size?: 'sm' | 'md' | 'lg' | 'xl'
+ showLabel?: boolean
+ strokeWidth?: number
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/code.ts b/src/lib/json-ui/interfaces/code.ts
new file mode 100644
index 0000000..6d04ac8
--- /dev/null
+++ b/src/lib/json-ui/interfaces/code.ts
@@ -0,0 +1,7 @@
+import { ReactNode } from 'react'
+
+export interface CodeProps {
+ children: ReactNode
+ inline?: boolean
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/command-palette.ts b/src/lib/json-ui/interfaces/command-palette.ts
new file mode 100644
index 0000000..bb70b0f
--- /dev/null
+++ b/src/lib/json-ui/interfaces/command-palette.ts
@@ -0,0 +1,19 @@
+import { ReactNode } from 'react'
+
+export interface CommandOption {
+ value: string
+ label: string
+ icon?: ReactNode
+ onSelect?: () => void
+}
+
+export interface CommandPaletteProps {
+ open: boolean
+ onOpenChange: (open: boolean) => void
+ placeholder?: string
+ emptyMessage?: string
+ groups: {
+ heading?: string
+ items: CommandOption[]
+ }[]
+}
diff --git a/src/lib/json-ui/interfaces/completion-card.ts b/src/lib/json-ui/interfaces/completion-card.ts
new file mode 100644
index 0000000..3c71473
--- /dev/null
+++ b/src/lib/json-ui/interfaces/completion-card.ts
@@ -0,0 +1,5 @@
+export interface CompletionCardProps {
+ completionScore: number
+ completionMessage: string
+ isReadyToExport: boolean
+}
diff --git a/src/lib/json-ui/interfaces/component-palette-item.ts b/src/lib/json-ui/interfaces/component-palette-item.ts
new file mode 100644
index 0000000..0c35645
--- /dev/null
+++ b/src/lib/json-ui/interfaces/component-palette-item.ts
@@ -0,0 +1,7 @@
+import { ComponentDefinition } from '@/lib/component-definition-types'
+
+export interface ComponentPaletteItemProps {
+ component: ComponentDefinition
+ onDragStart: (component: ComponentDefinition, e: React.DragEvent) => void
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/confirm-button.ts b/src/lib/json-ui/interfaces/confirm-button.ts
new file mode 100644
index 0000000..d31197d
--- /dev/null
+++ b/src/lib/json-ui/interfaces/confirm-button.ts
@@ -0,0 +1,8 @@
+import { ButtonHTMLAttributes, ReactNode } from 'react'
+
+export interface ConfirmButtonProps extends Omit, 'onClick'> {
+ onConfirm: () => void | Promise
+ confirmText?: string
+ isLoading?: boolean
+ children: ReactNode
+}
diff --git a/src/lib/json-ui/interfaces/data-source-badge.ts b/src/lib/json-ui/interfaces/data-source-badge.ts
new file mode 100644
index 0000000..1a5ccfa
--- /dev/null
+++ b/src/lib/json-ui/interfaces/data-source-badge.ts
@@ -0,0 +1,6 @@
+import { DataSourceType } from '@/types/json-ui'
+
+export interface DataSourceBadgeProps {
+ type: DataSourceType
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/data-table.ts b/src/lib/json-ui/interfaces/data-table.ts
new file mode 100644
index 0000000..b5daf63
--- /dev/null
+++ b/src/lib/json-ui/interfaces/data-table.ts
@@ -0,0 +1,17 @@
+import { ReactNode } from 'react'
+
+export interface Column {
+ key: string
+ header: string | ReactNode
+ cell?: (item: T) => ReactNode
+ sortable?: boolean
+ width?: string
+}
+
+export interface DataTableProps {
+ data: T[]
+ columns: Column[]
+ onRowClick?: (item: T) => void
+ emptyMessage?: string
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/date-picker.ts b/src/lib/json-ui/interfaces/date-picker.ts
new file mode 100644
index 0000000..e76bf34
--- /dev/null
+++ b/src/lib/json-ui/interfaces/date-picker.ts
@@ -0,0 +1,7 @@
+export interface DatePickerProps {
+ value?: Date
+ onChange: (date: Date | undefined) => void
+ placeholder?: string
+ disabled?: boolean
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/detail-row.ts b/src/lib/json-ui/interfaces/detail-row.ts
new file mode 100644
index 0000000..cda9881
--- /dev/null
+++ b/src/lib/json-ui/interfaces/detail-row.ts
@@ -0,0 +1,5 @@
+export interface DetailRowProps {
+ icon: React.ReactNode
+ label: string
+ value: number
+}
diff --git a/src/lib/json-ui/interfaces/divider.ts b/src/lib/json-ui/interfaces/divider.ts
new file mode 100644
index 0000000..be58979
--- /dev/null
+++ b/src/lib/json-ui/interfaces/divider.ts
@@ -0,0 +1,5 @@
+export interface DividerProps {
+ orientation?: 'horizontal' | 'vertical'
+ className?: string
+ decorative?: boolean
+}
diff --git a/src/lib/json-ui/interfaces/empty-message.ts b/src/lib/json-ui/interfaces/empty-message.ts
new file mode 100644
index 0000000..41a22d1
--- /dev/null
+++ b/src/lib/json-ui/interfaces/empty-message.ts
@@ -0,0 +1,10 @@
+export interface EmptyMessageProps {
+ icon?: React.ReactNode
+ title: string
+ description?: string
+ action?: {
+ label: string
+ onClick: () => void
+ }
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/error-badge.ts b/src/lib/json-ui/interfaces/error-badge.ts
new file mode 100644
index 0000000..8868052
--- /dev/null
+++ b/src/lib/json-ui/interfaces/error-badge.ts
@@ -0,0 +1,5 @@
+export interface ErrorBadgeProps {
+ count: number
+ variant?: 'default' | 'destructive'
+ size?: 'sm' | 'md'
+}
diff --git a/src/lib/json-ui/interfaces/file-icon.ts b/src/lib/json-ui/interfaces/file-icon.ts
new file mode 100644
index 0000000..409daad
--- /dev/null
+++ b/src/lib/json-ui/interfaces/file-icon.ts
@@ -0,0 +1,6 @@
+export interface FileIconProps {
+ type?: 'code' | 'json' | 'plus'
+ size?: number
+ weight?: 'thin' | 'light' | 'regular' | 'bold' | 'fill' | 'duotone'
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/glow-card.ts b/src/lib/json-ui/interfaces/glow-card.ts
new file mode 100644
index 0000000..2336b96
--- /dev/null
+++ b/src/lib/json-ui/interfaces/glow-card.ts
@@ -0,0 +1,7 @@
+export interface GlowCardProps {
+ children: React.ReactNode
+ glowColor?: 'primary' | 'accent' | 'success' | 'warning' | 'error'
+ intensity?: 'low' | 'medium' | 'high'
+ className?: string
+ onClick?: () => void
+}
diff --git a/src/lib/json-ui/interfaces/helper-text.ts b/src/lib/json-ui/interfaces/helper-text.ts
new file mode 100644
index 0000000..e0d5e0a
--- /dev/null
+++ b/src/lib/json-ui/interfaces/helper-text.ts
@@ -0,0 +1,5 @@
+export interface HelperTextProps {
+ children: React.ReactNode
+ variant?: 'default' | 'error' | 'success'
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/index.ts b/src/lib/json-ui/interfaces/index.ts
index 7384c9a..dd2b2ec 100644
--- a/src/lib/json-ui/interfaces/index.ts
+++ b/src/lib/json-ui/interfaces/index.ts
@@ -1,6 +1,7 @@
export * from './loading-fallback'
export * from './navigation-item'
export * from './page-header-content'
+export * from './page-header'
export * from './save-indicator'
export * from './lazy-bar-chart'
export * from './lazy-line-chart'
@@ -43,23 +44,36 @@ export * from './calendar'
export * from './card'
export * from './checkbox'
export * from './context-menu'
+export * from './date-picker'
+export * from './detail-row'
export * from './dialog'
+export * from './divider'
export * from './drawer'
export * from './dropdown-menu'
+export * from './empty-message'
+export * from './error-badge'
+export * from './file-icon'
export * from './form'
export * from './form-field'
+export * from './glow-card'
export * from './heading'
+export * from './helper-text'
export * from './hover-card'
export * from './icon'
export * from './input-otp'
export * from './label'
export * from './pagination'
export * from './progress'
+export * from './progress-bar'
+export * from './pulse'
+export * from './quick-action-button'
export * from './radio-group'
export * from './range-slider'
export * from './rating'
export * from './scroll-area'
export * from './scroll-area-thumb'
+export * from './search-input'
+export * from './seed-data-status'
export * from './select'
export * from './separator'
export * from './skeleton'
@@ -79,3 +93,16 @@ export * from './timeline'
export * from './timestamp'
export * from './toggle'
export * from './tooltip'
+export * from './tab-icon'
+export * from './tips-card'
+export * from './info-box'
+export * from './key-value'
+export * from './live-indicator'
+export * from './list'
+export * from './list-item'
+export * from './loading-spinner'
+export * from './loading-state'
+export * from './metric-display'
+export * from './modal'
+export * from './notification'
+export * from './number-input'
diff --git a/src/lib/json-ui/interfaces/info-box.ts b/src/lib/json-ui/interfaces/info-box.ts
new file mode 100644
index 0000000..f4dbdc9
--- /dev/null
+++ b/src/lib/json-ui/interfaces/info-box.ts
@@ -0,0 +1,6 @@
+export interface InfoBoxProps {
+ type?: 'info' | 'warning' | 'success' | 'error'
+ title?: string
+ children: React.ReactNode
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/key-value.ts b/src/lib/json-ui/interfaces/key-value.ts
new file mode 100644
index 0000000..6c9c465
--- /dev/null
+++ b/src/lib/json-ui/interfaces/key-value.ts
@@ -0,0 +1,8 @@
+export interface KeyValueProps {
+ label: string
+ value: React.ReactNode
+ orientation?: 'horizontal' | 'vertical'
+ className?: string
+ labelClassName?: string
+ valueClassName?: string
+}
diff --git a/src/lib/json-ui/interfaces/list-item.ts b/src/lib/json-ui/interfaces/list-item.ts
new file mode 100644
index 0000000..f4cff2c
--- /dev/null
+++ b/src/lib/json-ui/interfaces/list-item.ts
@@ -0,0 +1,8 @@
+export interface ListItemProps {
+ icon?: React.ReactNode
+ children: React.ReactNode
+ onClick?: () => void
+ active?: boolean
+ className?: string
+ endContent?: React.ReactNode
+}
diff --git a/src/lib/json-ui/interfaces/list.ts b/src/lib/json-ui/interfaces/list.ts
new file mode 100644
index 0000000..0f919cc
--- /dev/null
+++ b/src/lib/json-ui/interfaces/list.ts
@@ -0,0 +1,7 @@
+export interface ListProps {
+ items: T[]
+ renderItem: (item: T, index: number) => React.ReactNode
+ emptyMessage?: string
+ className?: string
+ itemClassName?: string
+}
diff --git a/src/lib/json-ui/interfaces/live-indicator.ts b/src/lib/json-ui/interfaces/live-indicator.ts
new file mode 100644
index 0000000..23d0673
--- /dev/null
+++ b/src/lib/json-ui/interfaces/live-indicator.ts
@@ -0,0 +1,6 @@
+export interface LiveIndicatorProps {
+ label?: string
+ showLabel?: boolean
+ size?: 'sm' | 'md' | 'lg'
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/loading-spinner.ts b/src/lib/json-ui/interfaces/loading-spinner.ts
new file mode 100644
index 0000000..518ce1e
--- /dev/null
+++ b/src/lib/json-ui/interfaces/loading-spinner.ts
@@ -0,0 +1,4 @@
+export interface LoadingSpinnerProps {
+ size?: 'sm' | 'md' | 'lg'
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/loading-state.ts b/src/lib/json-ui/interfaces/loading-state.ts
new file mode 100644
index 0000000..78b7a44
--- /dev/null
+++ b/src/lib/json-ui/interfaces/loading-state.ts
@@ -0,0 +1,5 @@
+export interface LoadingStateProps {
+ message?: string
+ size?: 'sm' | 'md' | 'lg'
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/metric-display.ts b/src/lib/json-ui/interfaces/metric-display.ts
new file mode 100644
index 0000000..df41a99
--- /dev/null
+++ b/src/lib/json-ui/interfaces/metric-display.ts
@@ -0,0 +1,11 @@
+export interface MetricDisplayProps {
+ label: string
+ value: string | number
+ trend?: {
+ value: number
+ direction: 'up' | 'down'
+ }
+ icon?: React.ReactNode
+ className?: string
+ variant?: 'default' | 'primary' | 'accent'
+}
diff --git a/src/lib/json-ui/interfaces/modal.ts b/src/lib/json-ui/interfaces/modal.ts
new file mode 100644
index 0000000..546d4d3
--- /dev/null
+++ b/src/lib/json-ui/interfaces/modal.ts
@@ -0,0 +1,9 @@
+export interface ModalProps {
+ isOpen: boolean
+ onClose: () => void
+ title?: string
+ children: React.ReactNode
+ size?: 'sm' | 'md' | 'lg' | 'xl' | 'full'
+ showCloseButton?: boolean
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/notification.ts b/src/lib/json-ui/interfaces/notification.ts
new file mode 100644
index 0000000..ec7c40a
--- /dev/null
+++ b/src/lib/json-ui/interfaces/notification.ts
@@ -0,0 +1,7 @@
+export interface NotificationProps {
+ type: 'info' | 'success' | 'warning' | 'error'
+ title: string
+ message?: string
+ onClose?: () => void
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/number-input.ts b/src/lib/json-ui/interfaces/number-input.ts
new file mode 100644
index 0000000..cad3677
--- /dev/null
+++ b/src/lib/json-ui/interfaces/number-input.ts
@@ -0,0 +1,10 @@
+export interface NumberInputProps {
+ value: number
+ onChange: (value: number) => void
+ min?: number
+ max?: number
+ step?: number
+ label?: string
+ disabled?: boolean
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/page-header.ts b/src/lib/json-ui/interfaces/page-header.ts
new file mode 100644
index 0000000..114b732
--- /dev/null
+++ b/src/lib/json-ui/interfaces/page-header.ts
@@ -0,0 +1,6 @@
+export interface PageHeaderProps {
+ title: string
+ description?: string
+ actions?: React.ReactNode
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/progress-bar.ts b/src/lib/json-ui/interfaces/progress-bar.ts
new file mode 100644
index 0000000..ab70b4e
--- /dev/null
+++ b/src/lib/json-ui/interfaces/progress-bar.ts
@@ -0,0 +1,8 @@
+export interface ProgressBarProps {
+ value: number
+ max?: number
+ size?: 'sm' | 'md' | 'lg'
+ variant?: 'default' | 'accent' | 'destructive'
+ showLabel?: boolean
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/pulse.ts b/src/lib/json-ui/interfaces/pulse.ts
new file mode 100644
index 0000000..5ab1f5c
--- /dev/null
+++ b/src/lib/json-ui/interfaces/pulse.ts
@@ -0,0 +1,6 @@
+export interface PulseProps {
+ variant?: 'primary' | 'accent' | 'success' | 'warning' | 'error'
+ size?: 'sm' | 'md' | 'lg'
+ speed?: 'slow' | 'normal' | 'fast'
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/quick-action-button.ts b/src/lib/json-ui/interfaces/quick-action-button.ts
new file mode 100644
index 0000000..899e1bf
--- /dev/null
+++ b/src/lib/json-ui/interfaces/quick-action-button.ts
@@ -0,0 +1,9 @@
+export interface QuickActionButtonProps {
+ icon: React.ReactNode
+ label: string
+ description?: string
+ onClick: () => void
+ variant?: 'default' | 'primary' | 'accent' | 'muted'
+ disabled?: boolean
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/search-input.ts b/src/lib/json-ui/interfaces/search-input.ts
new file mode 100644
index 0000000..568dbcc
--- /dev/null
+++ b/src/lib/json-ui/interfaces/search-input.ts
@@ -0,0 +1,7 @@
+export interface SearchInputProps {
+ value: string
+ onChange: (value: string) => void
+ placeholder?: string
+ onClear?: () => void
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/seed-data-status.ts b/src/lib/json-ui/interfaces/seed-data-status.ts
new file mode 100644
index 0000000..d80ef62
--- /dev/null
+++ b/src/lib/json-ui/interfaces/seed-data-status.ts
@@ -0,0 +1,3 @@
+export interface SeedDataStatusProps {
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/sparkle.ts b/src/lib/json-ui/interfaces/sparkle.ts
new file mode 100644
index 0000000..63e488c
--- /dev/null
+++ b/src/lib/json-ui/interfaces/sparkle.ts
@@ -0,0 +1,6 @@
+export interface SparkleProps {
+ variant?: 'default' | 'primary' | 'accent' | 'gold'
+ size?: number
+ animate?: boolean
+ className?: string
+}
diff --git a/src/lib/json-ui/interfaces/tab-icon.ts b/src/lib/json-ui/interfaces/tab-icon.ts
new file mode 100644
index 0000000..c991d0f
--- /dev/null
+++ b/src/lib/json-ui/interfaces/tab-icon.ts
@@ -0,0 +1,4 @@
+export interface TabIconProps {
+ icon: React.ReactNode
+ variant?: 'default' | 'gradient'
+}
diff --git a/src/lib/json-ui/interfaces/tips-card.ts b/src/lib/json-ui/interfaces/tips-card.ts
new file mode 100644
index 0000000..1c4c1e0
--- /dev/null
+++ b/src/lib/json-ui/interfaces/tips-card.ts
@@ -0,0 +1,3 @@
+export interface TipsCardProps {
+ tips: Array<{ message: string; show: boolean }>
+}
diff --git a/src/lib/json-ui/json-components.ts b/src/lib/json-ui/json-components.ts
index e7c6b80..869d88e 100644
--- a/src/lib/json-ui/json-components.ts
+++ b/src/lib/json-ui/json-components.ts
@@ -9,6 +9,7 @@ import type {
LoadingFallbackProps,
NavigationItemProps,
PageHeaderContentProps,
+ PageHeaderProps,
SaveIndicatorProps,
LazyBarChartProps,
LazyLineChartProps,
@@ -63,15 +64,21 @@ import type {
LabelProps,
PaginationProps,
ProgressProps,
+ ProgressBarProps,
+ PulseProps,
+ QuickActionButtonProps,
RadioGroupProps,
RangeSliderProps,
RatingProps,
ScrollAreaProps,
ScrollAreaThumbProps,
+ SearchInputProps,
+ SeedDataStatusProps,
SelectProps,
SeparatorProps,
SkeletonProps,
SliderProps,
+ SparkleProps,
SpinnerProps,
StatusIconProps,
StepIndicatorProps,
@@ -87,12 +94,26 @@ import type {
TimestampProps,
ToggleProps,
TooltipProps,
+ TabIconProps,
+ TipsCardProps,
+ InfoBoxProps,
+ KeyValueProps,
+ LiveIndicatorProps,
+ ListProps,
+ ListItemProps,
+ LoadingSpinnerProps,
+ LoadingStateProps,
+ MetricDisplayProps,
+ ModalProps,
+ NotificationProps,
+ NumberInputProps,
} from './interfaces'
// Import JSON definitions
import loadingFallbackDef from '@/components/json-definitions/loading-fallback.json'
import navigationItemDef from '@/components/json-definitions/navigation-item.json'
import pageHeaderContentDef from '@/components/json-definitions/page-header-content.json'
+import pageHeaderDef from '@/components/json-definitions/page-header.json'
import componentBindingDialogDef from '@/components/json-definitions/component-binding-dialog.json'
import dataSourceEditorDialogDef from '@/components/json-definitions/data-source-editor-dialog.json'
import githubBuildStatusDef from '@/components/json-definitions/github-build-status.json'
@@ -145,15 +166,21 @@ import inputOtpDef from '@/components/json-definitions/input-otp.json'
import labelDef from '@/components/json-definitions/label.json'
import paginationDef from '@/components/json-definitions/pagination.json'
import progressDef from '@/components/json-definitions/progress.json'
+import progressBarDef from '@/components/json-definitions/progress-bar.json'
+import pulseDef from '@/components/json-definitions/pulse.json'
+import quickActionButtonDef from '@/components/json-definitions/quick-action-button.json'
import radioGroupDef from '@/components/json-definitions/radio-group.json'
import rangeSliderDef from '@/components/json-definitions/range-slider.json'
import ratingDef from '@/components/json-definitions/rating.json'
import scrollAreaDef from '@/components/json-definitions/scroll-area.json'
import scrollAreaThumbDef from '@/components/json-definitions/scroll-area-thumb.json'
+import searchInputDef from '@/components/json-definitions/search-input.json'
+import seedDataStatusDef from '@/components/json-definitions/seed-data-status.json'
import selectDef from '@/components/json-definitions/select.json'
import separatorDef from '@/components/json-definitions/separator.json'
import skeletonDef from '@/components/json-definitions/skeleton.json'
import sliderDef from '@/components/json-definitions/slider.json'
+import sparkleDef from '@/components/json-definitions/sparkle.json'
import spinnerDef from '@/components/json-definitions/spinner.json'
import statusIconDef from '@/components/json-definitions/status-icon.json'
import stepIndicatorDef from '@/components/json-definitions/step-indicator.json'
@@ -169,6 +196,19 @@ import timelineDef from '@/components/json-definitions/timeline.json'
import timestampDef from '@/components/json-definitions/timestamp.json'
import toggleDef from '@/components/json-definitions/toggle.json'
import tooltipDef from '@/components/json-definitions/tooltip.json'
+import tabIconDef from '@/components/json-definitions/tab-icon.json'
+import tipsCardDef from '@/components/json-definitions/tips-card.json'
+import infoBoxDef from '@/components/json-definitions/info-box.json'
+import keyValueDef from '@/components/json-definitions/key-value.json'
+import liveIndicatorDef from '@/components/json-definitions/live-indicator.json'
+import listDef from '@/components/json-definitions/list.json'
+import listItemDef from '@/components/json-definitions/list-item.json'
+import loadingSpinnerDef from '@/components/json-definitions/loading-spinner.json'
+import loadingStateDef from '@/components/json-definitions/loading-state.json'
+import metricDisplayDef from '@/components/json-definitions/metric-display.json'
+import modalDef from '@/components/json-definitions/modal.json'
+import notificationDef from '@/components/json-definitions/notification.json'
+import numberInputDef from '@/components/json-definitions/number-input.json'
// Create pure JSON components (no hooks)
export const LoadingFallback = createJsonComponent(loadingFallbackDef)
@@ -230,6 +270,19 @@ export const Timeline = createJsonComponent(timelineDef)
export const Timestamp = createJsonComponent(timestampDef)
export const Toggle = createJsonComponent(toggleDef)
export const Tooltip = createJsonComponent(tooltipDef)
+export const TabIcon = createJsonComponent(tabIconDef)
+export const TipsCard = createJsonComponent(tipsCardDef)
+export const InfoBox = createJsonComponent(infoBoxDef)
+export const KeyValue = createJsonComponent(keyValueDef)
+export const LiveIndicator = createJsonComponent(liveIndicatorDef)
+export const List = createJsonComponent>(listDef)
+export const ListItem = createJsonComponent(listItemDef)
+export const LoadingSpinner = createJsonComponent(loadingSpinnerDef)
+export const LoadingState = createJsonComponent(loadingStateDef)
+export const MetricDisplay = createJsonComponent(metricDisplayDef)
+export const Modal = createJsonComponent(modalDef)
+export const Notification = createJsonComponent(notificationDef)
+export const NumberInput = createJsonComponent(numberInputDef)
// Create JSON components with hooks
export const SaveIndicator = createJsonComponentWithHooks(saveIndicatorDef, {