Kraft102's picture
fix: sql.js Docker/Alpine compatibility layer for PatternMemory and FailureMemory
5a81b95
/**
* ╔═══════════════════════════════════════════════════════════════════════════╗
* β•‘ SMART COMPONENT RENDERER β•‘
* ║═══════════════════════════════════════════════════════════════════════════║
* β•‘ Dynamic renderer that maps JSON component specifications to React β•‘
* β•‘ Part of the Liquid UI Arsenal β•‘
* β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
*/
import { Suspense, lazy, ComponentType, useMemo } from 'react';
import { Loader2, AlertTriangle } from 'lucide-react';
import { cn } from '@/lib/utils';
// Import all smart components
import { LiveMetricGauge, LiveMetricGaugeProps } from './LiveMetricGauge';
import { CodeDiffViewer, CodeDiffViewerProps } from './CodeDiffViewer';
import { LogStreamViewer, LogStreamViewerProps } from './LogStreamViewer';
import { InteractiveForceGraph, InteractiveForceGraphProps } from './InteractiveForceGraph';
import { KnowledgeGapCard, KnowledgeGapCardProps } from './KnowledgeGapCard';
import { AgentProcessTracker, AgentProcessTrackerProps } from './AgentProcessTracker';
import { IdeaStickyNote, IdeaStickyNoteProps } from './IdeaStickyNote';
// Visual Cortex Components (Diagram Engine)
import { MermaidDiagram, MermaidDiagramProps } from './MermaidDiagram';
import { ArchitectureView, ArchitectureViewProps } from './ArchitectureView';
import { SequenceDiagram, SequenceDiagramProps } from './SequenceDiagram';
import { FlowchartView, FlowchartViewProps } from './FlowchartView';
import { MindMapView, MindMapViewProps } from './MindMapView';
// Component type definitions
export type SmartComponentName =
| 'LiveMetricGauge'
| 'CodeDiffViewer'
| 'LogStreamViewer'
| 'InteractiveForceGraph'
| 'KnowledgeGapCard'
| 'AgentProcessTracker'
| 'IdeaStickyNote'
// Visual Cortex (Diagram Engine)
| 'MermaidDiagram'
| 'ArchitectureView'
| 'SequenceDiagram'
| 'FlowchartView'
| 'MindMapView';
// Props type mapping
export type SmartComponentProps = {
LiveMetricGauge: LiveMetricGaugeProps;
CodeDiffViewer: CodeDiffViewerProps;
LogStreamViewer: LogStreamViewerProps;
InteractiveForceGraph: InteractiveForceGraphProps;
KnowledgeGapCard: KnowledgeGapCardProps;
AgentProcessTracker: AgentProcessTrackerProps;
IdeaStickyNote: IdeaStickyNoteProps;
// Visual Cortex
MermaidDiagram: MermaidDiagramProps;
ArchitectureView: ArchitectureViewProps;
SequenceDiagram: SequenceDiagramProps;
FlowchartView: FlowchartViewProps;
MindMapView: MindMapViewProps;
};
// Component registry
const componentRegistry: Record<SmartComponentName, ComponentType<any>> = {
LiveMetricGauge,
CodeDiffViewer,
LogStreamViewer,
InteractiveForceGraph,
KnowledgeGapCard,
AgentProcessTracker,
IdeaStickyNote,
// Visual Cortex
MermaidDiagram,
ArchitectureView,
SequenceDiagram,
FlowchartView,
MindMapView,
};
// UI_COMPONENT message format from backend
export interface UIComponentMessage {
type: 'UI_COMPONENT';
component: SmartComponentName;
props: Record<string, unknown>;
id?: string;
}
export interface SmartComponentRendererProps {
message: UIComponentMessage;
onError?: (error: Error, componentName: string) => void;
className?: string;
}
// Error boundary fallback
function ComponentError({ componentName, error }: { componentName: string; error?: Error }) {
return (
<div className="flex items-center gap-2 p-4 rounded-lg bg-red-500/10 border border-red-500/30 text-red-400">
<AlertTriangle className="w-4 h-4 flex-shrink-0" />
<div>
<p className="text-sm font-medium">Failed to render: {componentName}</p>
{error && <p className="text-xs text-red-400/70 mt-1">{error.message}</p>}
</div>
</div>
);
}
// Loading state
function ComponentLoading({ componentName }: { componentName: string }) {
return (
<div className="flex items-center gap-2 p-4 rounded-lg bg-muted/30 border border-border/30">
<Loader2 className="w-4 h-4 animate-spin text-primary" />
<span className="text-sm text-muted-foreground">Loading {componentName}...</span>
</div>
);
}
/**
* SmartComponentRenderer - Renders UI components based on JSON specification
*
* Usage:
* ```tsx
* <SmartComponentRenderer
* message={{
* type: 'UI_COMPONENT',
* component: 'LiveMetricGauge',
* props: { label: 'CPU', value: 75, max: 100 }
* }}
* />
* ```
*/
export function SmartComponentRenderer({
message,
onError,
className,
}: SmartComponentRendererProps) {
const { component: componentName, props, id } = message;
// Validate component exists
const Component = componentRegistry[componentName];
if (!Component) {
const error = new Error(`Unknown component: ${componentName}`);
onError?.(error, componentName);
return <ComponentError componentName={componentName} error={error} />;
}
// Render with error boundary
try {
return (
<div
className={cn('smart-component', className)}
data-component={componentName}
data-component-id={id}
>
<Suspense fallback={<ComponentLoading componentName={componentName} />}>
<Component {...props} />
</Suspense>
</div>
);
} catch (error) {
const err = error instanceof Error ? error : new Error(String(error));
onError?.(err, componentName);
return <ComponentError componentName={componentName} error={err} />;
}
}
/**
* Helper to check if a message is a UI_COMPONENT message
*/
export function isUIComponentMessage(message: unknown): message is UIComponentMessage {
if (!message || typeof message !== 'object') return false;
const msg = message as Record<string, unknown>;
return msg.type === 'UI_COMPONENT' && typeof msg.component === 'string';
}
/**
* Parse assistant message content for embedded UI components
* Supports format: ```ui:ComponentName {...props}```
*/
export function parseMessageForComponents(content: string): Array<string | UIComponentMessage> {
const parts: Array<string | UIComponentMessage> = [];
const regex = /```ui:(\w+)\s*([\s\S]*?)```/g;
let lastIndex = 0;
let match;
while ((match = regex.exec(content)) !== null) {
// Add text before the component
if (match.index > lastIndex) {
parts.push(content.slice(lastIndex, match.index));
}
// Parse the component
const componentName = match[1] as SmartComponentName;
let props = {};
try {
if (match[2].trim()) {
props = JSON.parse(match[2].trim());
}
} catch {
// If JSON parsing fails, try to interpret as simple key-value pairs
console.warn(`Failed to parse props for ${componentName}:`, match[2]);
}
parts.push({
type: 'UI_COMPONENT',
component: componentName,
props,
id: `${componentName}-${match.index}`,
});
lastIndex = regex.lastIndex;
}
// Add remaining text
if (lastIndex < content.length) {
parts.push(content.slice(lastIndex));
}
return parts.length > 0 ? parts : [content];
}
/**
* List all available component names
*/
export function getAvailableComponents(): SmartComponentName[] {
return Object.keys(componentRegistry) as SmartComponentName[];
}
/**
* Component metadata for documentation
*/
export const componentMetadata: Record<SmartComponentName, { description: string; category: string }> = {
LiveMetricGauge: {
description: 'Animated gauge for real-time metrics with thresholds',
category: 'Observability',
},
CodeDiffViewer: {
description: 'Side-by-side or unified diff viewer for code changes',
category: 'Builder',
},
LogStreamViewer: {
description: 'Real-time log streaming with filtering and levels',
category: 'Observability',
},
InteractiveForceGraph: {
description: 'Force-directed knowledge graph visualization',
category: 'Knowledge',
},
KnowledgeGapCard: {
description: 'Visual card for knowledge gaps and their status',
category: 'Knowledge',
},
AgentProcessTracker: {
description: 'Real-time visualization of agent task execution',
category: 'Builder',
},
IdeaStickyNote: {
description: 'Playful sticky note for incubated ideas',
category: 'Creative',
},
// Visual Cortex (Diagram Engine)
MermaidDiagram: {
description: 'Render any Mermaid diagram from DSL syntax',
category: 'Visual',
},
ArchitectureView: {
description: 'System architecture visualization with typed nodes',
category: 'Visual',
},
SequenceDiagram: {
description: 'Interaction sequences between actors/systems',
category: 'Visual',
},
FlowchartView: {
description: 'Business process and decision flow visualization',
category: 'Visual',
},
MindMapView: {
description: 'Hierarchical idea visualization and brainstorming',
category: 'Visual',
},
};
export default SmartComponentRenderer;