@burdenoff/microfe-bigconsole 2026.729.1 → 2026.729.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bigconsole/components/DrilldownDashboardRenderer.js +1 -1
- package/dist/bigconsole/components/DrilldownDashboardRenderer.js.map +1 -1
- package/dist/bigconsole/components/builder/inspector/DataBindingSection.js +4 -4
- package/dist/bigconsole/components/builder/inspector/DataBindingSection.js.map +1 -1
- package/dist/bigconsole/components/datasink/CacheTTLSelector.js +5 -5
- package/dist/bigconsole/components/datasink/CacheTTLSelector.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkCard.js +3 -3
- package/dist/bigconsole/components/datasink/DataSinkCard.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js +4 -4
- package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkStatusBadge.js +6 -6
- package/dist/bigconsole/components/datasink/DataSinkStatusBadge.js.map +1 -1
- package/dist/bigconsole/components/datasink/LinkedWorkflowsPanel.js +9 -9
- package/dist/bigconsole/components/datasink/LinkedWorkflowsPanel.js.map +1 -1
- package/dist/bigconsole/components/datasink/RefreshTypeSelector.js +2 -2
- package/dist/bigconsole/components/datasink/RefreshTypeSelector.js.map +1 -1
- package/dist/bigconsole/components/datasink/SetupStateBadge.js +8 -8
- package/dist/bigconsole/components/datasink/SetupStateBadge.js.map +1 -1
- package/dist/bigconsole/components/datasink/WorkflowStatusPanel.js +13 -13
- package/dist/bigconsole/components/datasink/WorkflowStatusPanel.js.map +1 -1
- package/dist/bigconsole/components/parser/DataSinkSelector.js +2 -2
- package/dist/bigconsole/components/parser/DataSinkSelector.js.map +1 -1
- package/dist/bigconsole/components/parser/ExpressionEditor.js +1 -1
- package/dist/bigconsole/components/parser/ExpressionEditor.js.map +1 -1
- package/dist/bigconsole/components/parser/ParserCard.js +1 -1
- package/dist/bigconsole/components/parser/ParserCard.js.map +1 -1
- package/dist/bigconsole/components/parser/ParserSelector.js +2 -2
- package/dist/bigconsole/components/parser/ParserSelector.js.map +1 -1
- package/dist/bigconsole/components/parser/ParserStatusBadge.js +2 -2
- package/dist/bigconsole/components/parser/ParserStatusBadge.js.map +1 -1
- package/dist/bigconsole/components/pipeline/PipelineCard.js +2 -2
- package/dist/bigconsole/components/pipeline/PipelineCard.js.map +1 -1
- package/dist/bigconsole/components/pipeline/PipelineDeleteDialog.js +5 -5
- package/dist/bigconsole/components/pipeline/PipelineDeleteDialog.js.map +1 -1
- package/dist/bigconsole/components/pipeline/PipelineStatusBadge.js +3 -3
- package/dist/bigconsole/components/pipeline/PipelineStatusBadge.js.map +1 -1
- package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js +1 -1
- package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js.map +1 -1
- package/dist/bigconsole/components/widgets/PropertiesPanel.js +3 -3
- package/dist/bigconsole/components/widgets/PropertiesPanel.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetErrorBoundary.js +2 -2
- package/dist/bigconsole/components/widgets/WidgetErrorBoundary.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetMenu.js +1 -1
- package/dist/bigconsole/components/widgets/WidgetMenu.js.map +1 -1
- package/dist/bigconsole/components/widgets/adaptive-card/AdaptiveCardWidget.js +3 -3
- package/dist/bigconsole/components/widgets/adaptive-card/AdaptiveCardWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js +1 -1
- package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js +3 -3
- package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/heatmap/HeatmapWidget.js +52 -32
- package/dist/bigconsole/components/widgets/heatmap/HeatmapWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js +3 -3
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/kpi/KPIWidget.js +1 -1
- package/dist/bigconsole/components/widgets/kpi/KPIWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/kpi-comparison/KPIComparisonWidget.js +2 -2
- package/dist/bigconsole/components/widgets/kpi-comparison/KPIComparisonWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/list/ListItem.js +3 -3
- package/dist/bigconsole/components/widgets/list/ListItem.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/ComparisonBadge.js +1 -1
- package/dist/bigconsole/components/widgets/metric-card/ComparisonBadge.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js +1 -1
- package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js +6 -6
- package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/TrendIndicator.js +1 -1
- package/dist/bigconsole/components/widgets/metric-card/TrendIndicator.js.map +1 -1
- package/dist/bigconsole/components/widgets/progress/ProgressBar.js +4 -4
- package/dist/bigconsole/components/widgets/progress/ProgressBar.js.map +1 -1
- package/dist/bigconsole/components/widgets/progress/ProgressConfig.js +1 -1
- package/dist/bigconsole/components/widgets/progress/ProgressConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/progress/ProgressWidget.js +1 -1
- package/dist/bigconsole/components/widgets/progress/ProgressWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/retention/RetentionWidget.js +2 -2
- package/dist/bigconsole/components/widgets/retention/RetentionWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/states/ErrorState.js +2 -2
- package/dist/bigconsole/components/widgets/states/ErrorState.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableConfig.js +2 -2
- package/dist/bigconsole/components/widgets/table/TableConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/timeline/TimelineWidget.js +1 -1
- package/dist/bigconsole/components/widgets/timeline/TimelineWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/widget-actions/WidgetActions.js +6 -6
- package/dist/bigconsole/components/widgets/widget-actions/WidgetActions.js.map +1 -1
- package/dist/bigconsole/components/workflow/WorkflowCard.js +1 -1
- package/dist/bigconsole/components/workflow/WorkflowCard.js.map +1 -1
- package/dist/bigconsole/components/workflow/WorkflowDeleteDialog.js +5 -5
- package/dist/bigconsole/components/workflow/WorkflowDeleteDialog.js.map +1 -1
- package/dist/bigconsole/components/workflow/WorkflowStatusBadge.js +6 -6
- package/dist/bigconsole/components/workflow/WorkflowStatusBadge.js.map +1 -1
- package/dist/bigconsole/pages/DataSinkBuilderPage.js +17 -17
- package/dist/bigconsole/pages/DataSinkBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinkViewPage.js +2 -2
- package/dist/bigconsole/pages/DataSinkViewPage.js.map +1 -1
- package/dist/bigconsole/pages/ParserBuilderPage.js +24 -24
- package/dist/bigconsole/pages/ParserBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/ParserViewPage.js +8 -8
- package/dist/bigconsole/pages/ParserViewPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelineBuilderPage.js +9 -9
- package/dist/bigconsole/pages/PipelineBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelineViewPage.js +12 -12
- package/dist/bigconsole/pages/PipelineViewPage.js.map +1 -1
- package/dist/bigconsole/pages/WorkflowViewPage.js +6 -6
- package/dist/bigconsole/pages/WorkflowViewPage.js.map +1 -1
- package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js +1 -1
- package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js.map +1 -1
- package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js +2 -2
- package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkflowViewPage.js","names":[],"sources":["../../../src/bigconsole/pages/WorkflowViewPage.tsx"],"sourcesContent":["/**\n * Workflow View Page\n *\n * Page for viewing FluidGrids Workflow details, linked DataSinks,\n * read-only workflow design preview, and real execution with polling.\n */\n\nimport { memo, useState, useEffect, useCallback } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { GlassCard, IllustratedEmptyState, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { useAppNavigate } from '../contexts';\nimport { isNative, nativeCopyText, nativeNotify, nativeToast } from '../../utils/nativeBridge';\nimport { useBigConsole } from '../context';\nimport { formatDistanceToNow } from 'date-fns';\nimport { useWorkflowOperations } from '../hooks/useWorkflowOperations';\nimport { WorkflowStatusBadge } from '../components/workflow/WorkflowStatusBadge';\nimport { WorkflowDeleteDialog } from '../components/workflow/WorkflowDeleteDialog';\nimport type { FluidGridsWorkflow } from '../types';\n\n// FluidGrids FE SDK — read-only embedded builder preview\nimport { WorkflowBuilder } from '@burdenoff/fluidgrids-fe-sdk';\nimport { BOOTSTRAP_RETRY_DELAY_MS } from '../constants';\n\n// ============================================================================\n// ICONS\n// ============================================================================\n\nconst ArrowLeftIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M15 19l-7-7 7-7\" />\n </svg>\n);\n\nconst EditIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\"\n />\n </svg>\n);\n\nconst TrashIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n />\n </svg>\n);\n\nconst PlayIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z\"\n />\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n);\n\nconst CopyIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"\n />\n </svg>\n);\n\nconst CheckIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M5 13l4 4L19 7\" />\n </svg>\n);\n\nconst DatabaseIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4\"\n />\n </svg>\n);\n\nconst ErrorIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n);\n\nconst ChevronDownIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n);\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport const WorkflowViewPage = memo(function WorkflowViewPage() {\n const navigate = useAppNavigate();\n const { workflowId } = useParams<{ workflowId: string }>();\n\n // BigConsole context for apiGatewayUrl and authToken\n const { apiGatewayUrl, authToken, workspaceId } = useBigConsole();\n\n // Local state\n const [workflow, setWorkflow] = useState<FluidGridsWorkflow | null>(null);\n const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);\n const [, setIsDeleting] = useState(false);\n const [isExecuting, setIsExecuting] = useState(false);\n const [webhookCopied, setWebhookCopied] = useState(false);\n const [showDesignPreview, setShowDesignPreview] = useState(false);\n // Track whether initial fetch has completed — prevents \"not found\" flash\n // before data has even been requested (loading starts as false with skipFetch: true)\n const [isReady, setIsReady] = useState(false);\n const [executionResult, setExecutionResult] = useState<{\n success: boolean;\n output: unknown;\n error: string | null;\n executionTimeMs: number;\n runId?: string;\n } | null>(null);\n\n // Operations\n const { loading, error, fetchWorkflow, deleteWorkflow, executeWorkflow, pollWorkflowExecution } =\n useWorkflowOperations({\n skipFetch: true,\n });\n\n // Load workflow on mount.\n // On a full page reload the workspace token may not be issued yet when this\n // effect first runs, causing the gateway to lack workspace context and\n // the resolver to return null. We retry once after a short delay to give\n // the auth bootstrap time to complete.\n useEffect(() => {\n if (!workflowId) {\n setIsReady(true);\n return;\n }\n\n let cancelled = false;\n\n const load = async (attempt: number) => {\n try {\n const result = await fetchWorkflow(workflowId);\n if (cancelled) return;\n\n if (result) {\n setWorkflow(result);\n setIsReady(true);\n } else if (attempt === 0) {\n // First attempt failed — likely workspace token race on full-page load.\n // Retry once after a short delay.\n setTimeout(() => {\n if (!cancelled) load(1);\n }, BOOTSTRAP_RETRY_DELAY_MS);\n } else {\n // Second attempt also failed — genuinely not found\n setIsReady(true);\n }\n } catch {\n // Network error, GraphQL error, etc. — surface the \"not found\" UI\n // rather than spinning forever with no escape hatch.\n if (!cancelled) setIsReady(true);\n }\n };\n\n load(0);\n return () => {\n cancelled = true;\n };\n }, [workflowId, fetchWorkflow]);\n\n // Handlers\n const handleBack = useCallback(() => {\n navigate(`/workflows`);\n }, [navigate]);\n\n const handleEdit = useCallback(() => {\n navigate(`/workflows/${workflowId}/edit`);\n }, [navigate, workflowId]);\n\n const handleDelete = useCallback(async () => {\n if (!workflowId) return;\n setIsDeleting(true);\n try {\n const success = await deleteWorkflow(workflowId);\n if (success) {\n if (isNative()) void nativeNotify('success');\n navigate(`/workflows`);\n } else if (isNative()) {\n void nativeNotify('error');\n }\n } catch {\n if (isNative()) void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n setShowDeleteConfirm(false);\n }\n }, [workflowId, deleteWorkflow, navigate]);\n\n const handleExecute = useCallback(async () => {\n if (!workflowId) return;\n setIsExecuting(true);\n setExecutionResult(null);\n\n const startTime = Date.now();\n\n try {\n const result = await executeWorkflow(workflowId);\n\n if (result) {\n const executionTimeMs = Date.now() - startTime;\n\n // If workflow has external FluidGrids ID, the execution is async\n if (result.externalWorkflowId) {\n setExecutionResult({\n success: true,\n output: {\n externalWorkflowId: result.externalWorkflowId,\n executionCount: result.executionCount,\n status: 'Triggered — polling for completion...',\n },\n error: null,\n executionTimeMs,\n });\n\n // Poll for completion in the background\n pollWorkflowExecution(workflowId, {\n intervalMs: 3000,\n timeoutMs: 120000,\n onProgress: (updated) => {\n setWorkflow(updated);\n },\n }).then((final) => {\n if (final) {\n setWorkflow(final);\n setExecutionResult((prev) =>\n prev\n ? {\n ...prev,\n output: {\n ...((prev.output as Record<string, unknown>) || {}),\n status: final.status === 'error' ? 'Failed' : 'Completed',\n executionCount: final.executionCount,\n lastExecutedAt: final.lastExecutedAt,\n },\n executionTimeMs: Date.now() - startTime,\n }\n : prev\n );\n }\n });\n } else {\n // Local execution (no external FluidGrids workflow)\n setExecutionResult({\n success: true,\n output: {\n executionCount: result.executionCount,\n lastExecutedAt: result.lastExecutedAt,\n status: 'Completed',\n },\n error: null,\n executionTimeMs,\n });\n }\n\n // Refresh workflow\n const updated = await fetchWorkflow(workflowId);\n if (updated) {\n setWorkflow(updated);\n }\n }\n } catch (err) {\n setExecutionResult({\n success: false,\n output: null,\n error: err instanceof Error ? err.message : 'Execution failed',\n executionTimeMs: Date.now() - startTime,\n });\n } finally {\n setIsExecuting(false);\n }\n }, [workflowId, executeWorkflow, fetchWorkflow, pollWorkflowExecution]);\n\n const handleCopyWebhook = useCallback(async () => {\n if (!workflow?.webhookUrl) return;\n try {\n await nativeCopyText(workflow.webhookUrl);\n setWebhookCopied(true);\n if (isNative()) void nativeToast('Webhook copied');\n setTimeout(() => setWebhookCopied(false), 2000);\n } catch {\n // Clipboard not available\n }\n }, [workflow?.webhookUrl]);\n\n const handleViewDataSink = useCallback(\n (dataSinkId: string) => {\n navigate(`/datasinks/${dataSinkId}`);\n },\n [navigate]\n );\n\n // Loading state — show spinner until initial fetch completes OR during a refetch\n if (!isReady || (loading && !workflow)) {\n return (\n <div className=\"p-6 flex items-center justify-center min-h-[400px]\">\n <svg className=\"w-8 h-8 text-action-primary-bg animate-spin\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n </div>\n );\n }\n\n // Error or not found state — only shown AFTER initial fetch completed\n if (error || !workflow) {\n return (\n <div className=\"p-6\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"inline-flex items-center gap-1 text-sm text-text-secondary hover:text-text-primary mb-4\"\n >\n <ArrowLeftIcon className=\"w-4 h-4\" />\n Back to Workflows\n </button>\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"Workflow not found\"\n description={error?.message || 'The workflow you are looking for does not exist.'}\n />\n </div>\n );\n }\n\n const linkedDataSinkCount = workflow.dataSinkLinks?.length || 0;\n\n return (\n <div className=\"p-6 max-w-5xl mx-auto\">\n {/* Header */}\n <div className=\"mb-8\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"inline-flex items-center gap-1 text-sm text-text-secondary hover:text-text-primary mb-4\"\n >\n <ArrowLeftIcon className=\"w-4 h-4\" />\n Back to Workflows\n </button>\n\n <div className=\"flex items-start justify-between\">\n <div>\n <div className=\"flex items-center gap-3 mb-2\">\n <h1 className=\"text-2xl font-bold text-text-primary\">{workflow.name}</h1>\n <WorkflowStatusBadge status={workflow.status} />\n </div>\n <p className=\"text-sm text-text-secondary font-mono\">{workflow.key}</p>\n {workflow.externalWorkflowId && (\n <p className=\"text-xs text-text-tertiary mt-1\">\n FluidGrids ID: <span className=\"font-mono\">{workflow.externalWorkflowId}</span>\n </p>\n )}\n </div>\n\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleExecute}\n disabled={isExecuting}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-state-success hover:bg-state-success/90 rounded-md transition-colors disabled:opacity-50\"\n >\n <PlayIcon className=\"w-4 h-4\" />\n {isExecuting ? 'Executing...' : 'Execute'}\n </button>\n <button\n type=\"button\"\n onClick={handleEdit}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-text-primary bg-bg-sunken hover:bg-border-default rounded-md transition-colors\"\n >\n <EditIcon className=\"w-4 h-4\" />\n Edit\n </button>\n <button\n type=\"button\"\n onClick={() => setShowDeleteConfirm(true)}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-state-error bg-state-error/10 hover:bg-state-error/20 rounded-md transition-colors\"\n >\n <TrashIcon className=\"w-4 h-4\" />\n Delete\n </button>\n </div>\n </div>\n </div>\n\n <PagePurpose className=\"mb-6\">\n A workflow is the visual ETL job that feeds this BigConsole — it fetches, transforms, and delivers data into\n linked data sinks. From here you can run it on demand, inspect its design and execution history, copy its\n webhook trigger, and edit it in the visual builder.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"bigconsole-workflow-view\"\n steps={[\n {\n id: 'execute',\n label: 'Run the workflow',\n description: 'Trigger an execution to pull fresh data now.',\n onClick: handleExecute,\n },\n {\n id: 'design',\n label: 'Inspect the design',\n description: 'Expand the read-only preview to see each step.',\n onClick: () => setShowDesignPreview(true),\n },\n {\n id: 'edit',\n label: 'Edit in the builder',\n description: 'Open the FluidGrids visual builder to change the flow.',\n onClick: handleEdit,\n },\n {\n id: 'sinks',\n label: 'Check the linked data sinks',\n description: 'See where this workflow writes its output.',\n onClick: () => navigate('/datasinks'),\n },\n ]}\n />\n\n {/* Execution Result */}\n {executionResult && (\n <div\n className={`mb-6 p-4 rounded-lg border ${\n executionResult.success\n ? 'bg-state-success/10 border-state-success/20'\n : 'bg-state-error/10 border-state-error/20'\n }`}\n >\n <div className=\"flex items-center justify-between mb-2\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {executionResult.success ? 'Execution Triggered' : 'Execution Failed'}\n </h3>\n <span className=\"text-xs text-text-secondary\">{executionResult.executionTimeMs}ms</span>\n </div>\n {executionResult.error ? (\n <p className=\"text-sm text-state-error\">{executionResult.error}</p>\n ) : (\n <pre className=\"text-xs text-text-secondary font-mono bg-bg-sunken p-2 rounded overflow-auto max-h-[200px]\">\n {JSON.stringify(executionResult.output, null, 2)}\n </pre>\n )}\n {executionResult.runId && (\n <p className=\"text-xs text-text-tertiary mt-2\">\n Run ID: <span className=\"font-mono\">{executionResult.runId}</span>\n </p>\n )}\n </div>\n )}\n\n {/* Info Grid */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-6 mb-8\">\n {/* Description */}\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">Description</h3>\n <p className=\"text-sm text-text-primary\">{workflow.description || 'No description'}</p>\n </div>\n\n {/* Execution Stats — primary emphasis zone */}\n <GlassCard treatment=\"glass\" glow className=\"p-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">Execution Stats</h3>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <p className=\"text-2xl font-bold text-text-primary\">{workflow.executionCount}</p>\n <p className=\"text-xs text-text-secondary\">Total Executions</p>\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {workflow.lastExecutedAt\n ? formatDistanceToNow(new Date(workflow.lastExecutedAt), { addSuffix: true })\n : 'Never'}\n </p>\n <p className=\"text-xs text-text-secondary\">Last Executed</p>\n </div>\n </div>\n </GlassCard>\n </div>\n\n {/* Webhook URL */}\n {workflow.webhookUrl && (\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4 mb-8\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">Webhook URL</h3>\n <div className=\"flex items-center gap-2\">\n <code className=\"flex-1 text-sm text-text-primary font-mono bg-bg-sunken px-3 py-2 rounded overflow-auto\">\n {workflow.webhookUrl}\n </code>\n <button\n type=\"button\"\n onClick={handleCopyWebhook}\n className=\"flex items-center gap-1 px-3 py-2 text-sm text-text-secondary hover:text-text-primary transition-colors\"\n >\n {webhookCopied ? (\n <>\n <CheckIcon className=\"w-4 h-4 text-state-success\" />\n Copied!\n </>\n ) : (\n <>\n <CopyIcon className=\"w-4 h-4\" />\n Copy\n </>\n )}\n </button>\n </div>\n </div>\n )}\n\n {/* Workflow Design Preview (collapsible) */}\n {workflow.externalWorkflowId && (\n <div className=\"bg-bg-surface border border-border-default rounded-lg mb-8 overflow-hidden\">\n <button\n type=\"button\"\n onClick={() => setShowDesignPreview((prev) => !prev)}\n className=\"w-full flex items-center justify-between px-4 py-3 text-sm font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n <span>Workflow Design Preview</span>\n <ChevronDownIcon\n className={`w-4 h-4 text-text-secondary transition-transform ${showDesignPreview ? 'rotate-180' : ''}`}\n />\n </button>\n {showDesignPreview && (\n <div className=\"border-t border-border-default\" style={{ height: '450px' }}>\n <WorkflowBuilder\n apiGatewayUrl={apiGatewayUrl}\n workspaceId={workspaceId || 'default'}\n authToken={authToken}\n workflowId={workflow.externalWorkflowId}\n readonly\n hideHeader\n className=\"h-full\"\n />\n </div>\n )}\n </div>\n )}\n\n {/* Linked DataSinks */}\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4 mb-8\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-4\">Linked DataSinks ({linkedDataSinkCount})</h3>\n {linkedDataSinkCount === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No data sinks linked\"\n description=\"This workflow has no data sinks linked yet — its output isn't being stored.\"\n />\n ) : (\n <div className=\"space-y-2\">\n {workflow.dataSinkLinks?.map((link) => (\n <div key={link.id} className=\"flex items-center justify-between px-3 py-2 bg-bg-sunken rounded-md\">\n <div className=\"flex items-center gap-3\">\n <DatabaseIcon className=\"w-4 h-4 text-text-secondary\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {link.dataSink?.name || link.dataSink?.key || link.dataSinkId}\n </p>\n {link.dataSink?.key && <p className=\"text-xs text-text-secondary font-mono\">{link.dataSink.key}</p>}\n </div>\n </div>\n <div className=\"flex items-center gap-4\">\n <div className=\"flex items-center gap-2\">\n {link.isEnabled ? (\n <span className=\"text-xs text-state-success\">Enabled</span>\n ) : (\n <span className=\"text-xs text-text-secondary\">Disabled</span>\n )}\n <span className=\"text-xs text-text-secondary\">Priority: {link.priority}</span>\n </div>\n {link.dataSink && (\n <button\n type=\"button\"\n onClick={() => handleViewDataSink(link.dataSinkId)}\n className=\"text-xs text-action-primary-bg hover:underline\"\n >\n View\n </button>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Timestamps */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-6\">\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">Created</h3>\n <p className=\"text-sm text-text-primary\">\n {formatDistanceToNow(new Date(workflow.createdAt), { addSuffix: true })}\n </p>\n <p className=\"text-xs text-text-secondary\">by {workflow.createdBy}</p>\n </div>\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">Last Updated</h3>\n <p className=\"text-sm text-text-primary\">\n {formatDistanceToNow(new Date(workflow.updatedAt), { addSuffix: true })}\n </p>\n <p className=\"text-xs text-text-secondary\">by {workflow.updatedBy || workflow.createdBy}</p>\n </div>\n </div>\n\n {/* Delete Confirmation Dialog */}\n {workflow && (\n <WorkflowDeleteDialog\n workflow={workflow}\n isOpen={showDeleteConfirm}\n onClose={() => setShowDeleteConfirm(false)}\n onConfirm={handleDelete}\n />\n )}\n </div>\n );\n});\n\nexport default WorkflowViewPage;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WorkflowViewPage.js","names":[],"sources":["../../../src/bigconsole/pages/WorkflowViewPage.tsx"],"sourcesContent":["/**\n * Workflow View Page\n *\n * Page for viewing FluidGrids Workflow details, linked DataSinks,\n * read-only workflow design preview, and real execution with polling.\n */\n\nimport { memo, useState, useEffect, useCallback } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { GlassCard, IllustratedEmptyState, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { useAppNavigate } from '../contexts';\nimport { isNative, nativeCopyText, nativeNotify, nativeToast } from '../../utils/nativeBridge';\nimport { useBigConsole } from '../context';\nimport { formatDistanceToNow } from 'date-fns';\nimport { useWorkflowOperations } from '../hooks/useWorkflowOperations';\nimport { WorkflowStatusBadge } from '../components/workflow/WorkflowStatusBadge';\nimport { WorkflowDeleteDialog } from '../components/workflow/WorkflowDeleteDialog';\nimport type { FluidGridsWorkflow } from '../types';\n\n// FluidGrids FE SDK — read-only embedded builder preview\nimport { WorkflowBuilder } from '@burdenoff/fluidgrids-fe-sdk';\nimport { BOOTSTRAP_RETRY_DELAY_MS } from '../constants';\n\n// ============================================================================\n// ICONS\n// ============================================================================\n\nconst ArrowLeftIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M15 19l-7-7 7-7\" />\n </svg>\n);\n\nconst EditIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\"\n />\n </svg>\n);\n\nconst TrashIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n />\n </svg>\n);\n\nconst PlayIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z\"\n />\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n);\n\nconst CopyIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"\n />\n </svg>\n);\n\nconst CheckIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M5 13l4 4L19 7\" />\n </svg>\n);\n\nconst DatabaseIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4\"\n />\n </svg>\n);\n\nconst ErrorIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n);\n\nconst ChevronDownIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n);\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport const WorkflowViewPage = memo(function WorkflowViewPage() {\n const navigate = useAppNavigate();\n const { workflowId } = useParams<{ workflowId: string }>();\n\n // BigConsole context for apiGatewayUrl and authToken\n const { apiGatewayUrl, authToken, workspaceId } = useBigConsole();\n\n // Local state\n const [workflow, setWorkflow] = useState<FluidGridsWorkflow | null>(null);\n const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);\n const [, setIsDeleting] = useState(false);\n const [isExecuting, setIsExecuting] = useState(false);\n const [webhookCopied, setWebhookCopied] = useState(false);\n const [showDesignPreview, setShowDesignPreview] = useState(false);\n // Track whether initial fetch has completed — prevents \"not found\" flash\n // before data has even been requested (loading starts as false with skipFetch: true)\n const [isReady, setIsReady] = useState(false);\n const [executionResult, setExecutionResult] = useState<{\n success: boolean;\n output: unknown;\n error: string | null;\n executionTimeMs: number;\n runId?: string;\n } | null>(null);\n\n // Operations\n const { loading, error, fetchWorkflow, deleteWorkflow, executeWorkflow, pollWorkflowExecution } =\n useWorkflowOperations({\n skipFetch: true,\n });\n\n // Load workflow on mount.\n // On a full page reload the workspace token may not be issued yet when this\n // effect first runs, causing the gateway to lack workspace context and\n // the resolver to return null. We retry once after a short delay to give\n // the auth bootstrap time to complete.\n useEffect(() => {\n if (!workflowId) {\n setIsReady(true);\n return;\n }\n\n let cancelled = false;\n\n const load = async (attempt: number) => {\n try {\n const result = await fetchWorkflow(workflowId);\n if (cancelled) return;\n\n if (result) {\n setWorkflow(result);\n setIsReady(true);\n } else if (attempt === 0) {\n // First attempt failed — likely workspace token race on full-page load.\n // Retry once after a short delay.\n setTimeout(() => {\n if (!cancelled) load(1);\n }, BOOTSTRAP_RETRY_DELAY_MS);\n } else {\n // Second attempt also failed — genuinely not found\n setIsReady(true);\n }\n } catch {\n // Network error, GraphQL error, etc. — surface the \"not found\" UI\n // rather than spinning forever with no escape hatch.\n if (!cancelled) setIsReady(true);\n }\n };\n\n load(0);\n return () => {\n cancelled = true;\n };\n }, [workflowId, fetchWorkflow]);\n\n // Handlers\n const handleBack = useCallback(() => {\n navigate(`/workflows`);\n }, [navigate]);\n\n const handleEdit = useCallback(() => {\n navigate(`/workflows/${workflowId}/edit`);\n }, [navigate, workflowId]);\n\n const handleDelete = useCallback(async () => {\n if (!workflowId) return;\n setIsDeleting(true);\n try {\n const success = await deleteWorkflow(workflowId);\n if (success) {\n if (isNative()) void nativeNotify('success');\n navigate(`/workflows`);\n } else if (isNative()) {\n void nativeNotify('error');\n }\n } catch {\n if (isNative()) void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n setShowDeleteConfirm(false);\n }\n }, [workflowId, deleteWorkflow, navigate]);\n\n const handleExecute = useCallback(async () => {\n if (!workflowId) return;\n setIsExecuting(true);\n setExecutionResult(null);\n\n const startTime = Date.now();\n\n try {\n const result = await executeWorkflow(workflowId);\n\n if (result) {\n const executionTimeMs = Date.now() - startTime;\n\n // If workflow has external FluidGrids ID, the execution is async\n if (result.externalWorkflowId) {\n setExecutionResult({\n success: true,\n output: {\n externalWorkflowId: result.externalWorkflowId,\n executionCount: result.executionCount,\n status: 'Triggered — polling for completion...',\n },\n error: null,\n executionTimeMs,\n });\n\n // Poll for completion in the background\n pollWorkflowExecution(workflowId, {\n intervalMs: 3000,\n timeoutMs: 120000,\n onProgress: (updated) => {\n setWorkflow(updated);\n },\n }).then((final) => {\n if (final) {\n setWorkflow(final);\n setExecutionResult((prev) =>\n prev\n ? {\n ...prev,\n output: {\n ...((prev.output as Record<string, unknown>) || {}),\n status: final.status === 'error' ? 'Failed' : 'Completed',\n executionCount: final.executionCount,\n lastExecutedAt: final.lastExecutedAt,\n },\n executionTimeMs: Date.now() - startTime,\n }\n : prev\n );\n }\n });\n } else {\n // Local execution (no external FluidGrids workflow)\n setExecutionResult({\n success: true,\n output: {\n executionCount: result.executionCount,\n lastExecutedAt: result.lastExecutedAt,\n status: 'Completed',\n },\n error: null,\n executionTimeMs,\n });\n }\n\n // Refresh workflow\n const updated = await fetchWorkflow(workflowId);\n if (updated) {\n setWorkflow(updated);\n }\n }\n } catch (err) {\n setExecutionResult({\n success: false,\n output: null,\n error: err instanceof Error ? err.message : 'Execution failed',\n executionTimeMs: Date.now() - startTime,\n });\n } finally {\n setIsExecuting(false);\n }\n }, [workflowId, executeWorkflow, fetchWorkflow, pollWorkflowExecution]);\n\n const handleCopyWebhook = useCallback(async () => {\n if (!workflow?.webhookUrl) return;\n try {\n await nativeCopyText(workflow.webhookUrl);\n setWebhookCopied(true);\n if (isNative()) void nativeToast('Webhook copied');\n setTimeout(() => setWebhookCopied(false), 2000);\n } catch {\n // Clipboard not available\n }\n }, [workflow?.webhookUrl]);\n\n const handleViewDataSink = useCallback(\n (dataSinkId: string) => {\n navigate(`/datasinks/${dataSinkId}`);\n },\n [navigate]\n );\n\n // Loading state — show spinner until initial fetch completes OR during a refetch\n if (!isReady || (loading && !workflow)) {\n return (\n <div className=\"p-6 flex items-center justify-center min-h-[400px]\">\n <svg className=\"w-8 h-8 text-action-primary-bg animate-spin\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n </div>\n );\n }\n\n // Error or not found state — only shown AFTER initial fetch completed\n if (error || !workflow) {\n return (\n <div className=\"p-6\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"inline-flex items-center gap-1 text-sm text-text-secondary hover:text-text-primary mb-4\"\n >\n <ArrowLeftIcon className=\"w-4 h-4\" />\n Back to Workflows\n </button>\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"Workflow not found\"\n description={error?.message || 'The workflow you are looking for does not exist.'}\n />\n </div>\n );\n }\n\n const linkedDataSinkCount = workflow.dataSinkLinks?.length || 0;\n\n return (\n <div className=\"p-6 max-w-5xl mx-auto\">\n {/* Header */}\n <div className=\"mb-8\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"inline-flex items-center gap-1 text-sm text-text-secondary hover:text-text-primary mb-4\"\n >\n <ArrowLeftIcon className=\"w-4 h-4\" />\n Back to Workflows\n </button>\n\n <div className=\"flex items-start justify-between\">\n <div>\n <div className=\"flex items-center gap-3 mb-2\">\n <h1 className=\"text-2xl font-bold text-text-primary\">{workflow.name}</h1>\n <WorkflowStatusBadge status={workflow.status} />\n </div>\n <p className=\"text-sm text-text-secondary font-mono\">{workflow.key}</p>\n {workflow.externalWorkflowId && (\n <p className=\"text-xs text-text-tertiary mt-1\">\n FluidGrids ID: <span className=\"font-mono\">{workflow.externalWorkflowId}</span>\n </p>\n )}\n </div>\n\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleExecute}\n disabled={isExecuting}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-status-success-bg hover:bg-status-success-bg/90 rounded-md transition-colors disabled:opacity-50\"\n >\n <PlayIcon className=\"w-4 h-4\" />\n {isExecuting ? 'Executing...' : 'Execute'}\n </button>\n <button\n type=\"button\"\n onClick={handleEdit}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-text-primary bg-bg-sunken hover:bg-border-default rounded-md transition-colors\"\n >\n <EditIcon className=\"w-4 h-4\" />\n Edit\n </button>\n <button\n type=\"button\"\n onClick={() => setShowDeleteConfirm(true)}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-status-error-text bg-status-error-bg/10 hover:bg-status-error-bg/20 rounded-md transition-colors\"\n >\n <TrashIcon className=\"w-4 h-4\" />\n Delete\n </button>\n </div>\n </div>\n </div>\n\n <PagePurpose className=\"mb-6\">\n A workflow is the visual ETL job that feeds this BigConsole — it fetches, transforms, and delivers data into\n linked data sinks. From here you can run it on demand, inspect its design and execution history, copy its\n webhook trigger, and edit it in the visual builder.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"bigconsole-workflow-view\"\n steps={[\n {\n id: 'execute',\n label: 'Run the workflow',\n description: 'Trigger an execution to pull fresh data now.',\n onClick: handleExecute,\n },\n {\n id: 'design',\n label: 'Inspect the design',\n description: 'Expand the read-only preview to see each step.',\n onClick: () => setShowDesignPreview(true),\n },\n {\n id: 'edit',\n label: 'Edit in the builder',\n description: 'Open the FluidGrids visual builder to change the flow.',\n onClick: handleEdit,\n },\n {\n id: 'sinks',\n label: 'Check the linked data sinks',\n description: 'See where this workflow writes its output.',\n onClick: () => navigate('/datasinks'),\n },\n ]}\n />\n\n {/* Execution Result */}\n {executionResult && (\n <div\n className={`mb-6 p-4 rounded-lg border ${\n executionResult.success\n ? 'bg-status-success-bg/10 border-status-success-border/20'\n : 'bg-status-error-bg/10 border-status-error-border/20'\n }`}\n >\n <div className=\"flex items-center justify-between mb-2\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {executionResult.success ? 'Execution Triggered' : 'Execution Failed'}\n </h3>\n <span className=\"text-xs text-text-secondary\">{executionResult.executionTimeMs}ms</span>\n </div>\n {executionResult.error ? (\n <p className=\"text-sm text-status-error-text\">{executionResult.error}</p>\n ) : (\n <pre className=\"text-xs text-text-secondary font-mono bg-bg-sunken p-2 rounded overflow-auto max-h-[200px]\">\n {JSON.stringify(executionResult.output, null, 2)}\n </pre>\n )}\n {executionResult.runId && (\n <p className=\"text-xs text-text-tertiary mt-2\">\n Run ID: <span className=\"font-mono\">{executionResult.runId}</span>\n </p>\n )}\n </div>\n )}\n\n {/* Info Grid */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-6 mb-8\">\n {/* Description */}\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">Description</h3>\n <p className=\"text-sm text-text-primary\">{workflow.description || 'No description'}</p>\n </div>\n\n {/* Execution Stats — primary emphasis zone */}\n <GlassCard treatment=\"glass\" glow className=\"p-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">Execution Stats</h3>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <p className=\"text-2xl font-bold text-text-primary\">{workflow.executionCount}</p>\n <p className=\"text-xs text-text-secondary\">Total Executions</p>\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {workflow.lastExecutedAt\n ? formatDistanceToNow(new Date(workflow.lastExecutedAt), { addSuffix: true })\n : 'Never'}\n </p>\n <p className=\"text-xs text-text-secondary\">Last Executed</p>\n </div>\n </div>\n </GlassCard>\n </div>\n\n {/* Webhook URL */}\n {workflow.webhookUrl && (\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4 mb-8\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">Webhook URL</h3>\n <div className=\"flex items-center gap-2\">\n <code className=\"flex-1 text-sm text-text-primary font-mono bg-bg-sunken px-3 py-2 rounded overflow-auto\">\n {workflow.webhookUrl}\n </code>\n <button\n type=\"button\"\n onClick={handleCopyWebhook}\n className=\"flex items-center gap-1 px-3 py-2 text-sm text-text-secondary hover:text-text-primary transition-colors\"\n >\n {webhookCopied ? (\n <>\n <CheckIcon className=\"w-4 h-4 text-status-success-text\" />\n Copied!\n </>\n ) : (\n <>\n <CopyIcon className=\"w-4 h-4\" />\n Copy\n </>\n )}\n </button>\n </div>\n </div>\n )}\n\n {/* Workflow Design Preview (collapsible) */}\n {workflow.externalWorkflowId && (\n <div className=\"bg-bg-surface border border-border-default rounded-lg mb-8 overflow-hidden\">\n <button\n type=\"button\"\n onClick={() => setShowDesignPreview((prev) => !prev)}\n className=\"w-full flex items-center justify-between px-4 py-3 text-sm font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n <span>Workflow Design Preview</span>\n <ChevronDownIcon\n className={`w-4 h-4 text-text-secondary transition-transform ${showDesignPreview ? 'rotate-180' : ''}`}\n />\n </button>\n {showDesignPreview && (\n <div className=\"border-t border-border-default\" style={{ height: '450px' }}>\n <WorkflowBuilder\n apiGatewayUrl={apiGatewayUrl}\n workspaceId={workspaceId || 'default'}\n authToken={authToken}\n workflowId={workflow.externalWorkflowId}\n readonly\n hideHeader\n className=\"h-full\"\n />\n </div>\n )}\n </div>\n )}\n\n {/* Linked DataSinks */}\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4 mb-8\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-4\">Linked DataSinks ({linkedDataSinkCount})</h3>\n {linkedDataSinkCount === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No data sinks linked\"\n description=\"This workflow has no data sinks linked yet — its output isn't being stored.\"\n />\n ) : (\n <div className=\"space-y-2\">\n {workflow.dataSinkLinks?.map((link) => (\n <div key={link.id} className=\"flex items-center justify-between px-3 py-2 bg-bg-sunken rounded-md\">\n <div className=\"flex items-center gap-3\">\n <DatabaseIcon className=\"w-4 h-4 text-text-secondary\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {link.dataSink?.name || link.dataSink?.key || link.dataSinkId}\n </p>\n {link.dataSink?.key && <p className=\"text-xs text-text-secondary font-mono\">{link.dataSink.key}</p>}\n </div>\n </div>\n <div className=\"flex items-center gap-4\">\n <div className=\"flex items-center gap-2\">\n {link.isEnabled ? (\n <span className=\"text-xs text-status-success-text\">Enabled</span>\n ) : (\n <span className=\"text-xs text-text-secondary\">Disabled</span>\n )}\n <span className=\"text-xs text-text-secondary\">Priority: {link.priority}</span>\n </div>\n {link.dataSink && (\n <button\n type=\"button\"\n onClick={() => handleViewDataSink(link.dataSinkId)}\n className=\"text-xs text-action-primary-bg hover:underline\"\n >\n View\n </button>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Timestamps */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-6\">\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">Created</h3>\n <p className=\"text-sm text-text-primary\">\n {formatDistanceToNow(new Date(workflow.createdAt), { addSuffix: true })}\n </p>\n <p className=\"text-xs text-text-secondary\">by {workflow.createdBy}</p>\n </div>\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">Last Updated</h3>\n <p className=\"text-sm text-text-primary\">\n {formatDistanceToNow(new Date(workflow.updatedAt), { addSuffix: true })}\n </p>\n <p className=\"text-xs text-text-secondary\">by {workflow.updatedBy || workflow.createdBy}</p>\n </div>\n </div>\n\n {/* Delete Confirmation Dialog */}\n {workflow && (\n <WorkflowDeleteDialog\n workflow={workflow}\n isOpen={showDeleteConfirm}\n onClose={() => setShowDeleteConfirm(false)}\n onConfirm={handleDelete}\n />\n )}\n </div>\n );\n});\n\nexport default WorkflowViewPage;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdaptiveCardRenderer.js","names":[],"sources":["../../../../src/bigconsole/renderers/adapters/AdaptiveCardRenderer.tsx"],"sourcesContent":["/**\n * Adaptive Card Renderer (v2.1)\n *\n * Renders widgets using the official Microsoft Adaptive Cards SDK.\n * Based on patterns from https://github.com/microsoft/AdaptiveCards\n *\n * Supports full template binding with data context including:\n * - Nested object access: ${creator.name}\n * - Array iteration with $data\n * - Conditional rendering with $when\n * - Date formatting: {{DATE(${field},SHORT)}}\n * - All Adaptive Card elements and actions\n * - Markdown rendering in TextBlocks\n *\n * @see https://adaptivecards.io/\n */\n\nimport React, { memo, useEffect, useRef, useCallback, useState } from 'react';\nimport * as AdaptiveCards from 'adaptivecards';\nimport type { RendererProps } from '../types/renderer';\n\n// Type for the templating module\ntype ACDataModule = typeof import('adaptivecards-templating');\n\n// ============================================================================\n// Fallback Template Expansion (when adaptivecards-templating fails)\n// ============================================================================\n\n/**\n * Simple template expansion fallback.\n * Handles basic ${property}, ${object.property} syntax, and $data iteration.\n * Used when adaptivecards-templating library fails to load.\n */\nfunction expandTemplateFallback(\n template: unknown,\n data: Record<string, unknown>,\n rootData?: Record<string, unknown>\n): unknown {\n // Keep reference to root data for nested expansions\n const root = rootData || data;\n\n if (typeof template === 'string') {\n // Replace ${property} patterns\n return template.replace(/\\$\\{([^}]+)\\}/g, (match, path) => {\n const trimmedPath = path.trim();\n // Try current data first, then root data\n let value = getNestedValue(data, trimmedPath);\n if (value === undefined && root !== data) {\n value = getNestedValue(root, trimmedPath);\n }\n return value !== undefined ? String(value) : match;\n });\n }\n\n if (Array.isArray(template)) {\n return template.flatMap((item) => {\n const expanded = expandTemplateFallback(item, data, root);\n return Array.isArray(expanded) ? expanded : [expanded];\n });\n }\n\n if (template && typeof template === 'object') {\n const templateObj = template as Record<string, unknown>;\n\n // Handle $data iteration\n if ('$data' in templateObj) {\n const dataPath = templateObj.$data;\n let arrayData: unknown[] = [];\n\n if (typeof dataPath === 'string') {\n // Extract array path from ${arrayName} pattern\n const match = dataPath.match(/^\\$\\{([^}]+)\\}$/);\n if (match) {\n const arrValue = getNestedValue(data, match[1].trim()) || getNestedValue(root, match[1].trim());\n if (Array.isArray(arrValue)) {\n arrayData = arrValue;\n }\n }\n } else if (Array.isArray(dataPath)) {\n arrayData = dataPath;\n }\n\n if (arrayData.length > 0) {\n // Create a copy of template without $data for each array item\n const templateWithoutData = Object.fromEntries(Object.entries(templateObj).filter(([key]) => key !== '$data'));\n return arrayData.map((item) => {\n const itemData =\n typeof item === 'object' && item !== null ? (item as Record<string, unknown>) : { value: item };\n // Merge item data with root data for nested references\n const mergedData = { ...root, ...itemData };\n return expandTemplateFallback(templateWithoutData, mergedData, root);\n });\n }\n }\n\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(templateObj)) {\n if (key === '$data') continue; // Already handled above\n if (key === '$when') continue; // Skip conditional rendering in fallback\n result[key] = expandTemplateFallback(value, data, root);\n }\n return result;\n }\n\n return template;\n}\n\n/**\n * Get nested property value from object using dot notation.\n * e.g., getNestedValue({a: {b: 1}}, 'a.b') => 1\n */\nfunction getNestedValue(obj: Record<string, unknown>, path: string): unknown {\n const parts = path.split('.');\n let current: unknown = obj;\n\n for (const part of parts) {\n if (current === null || current === undefined) return undefined;\n if (typeof current !== 'object') return undefined;\n current = (current as Record<string, unknown>)[part];\n }\n\n return current;\n}\n\n// ============================================================================\n// Markdown Processing Setup (from official adaptivecards-react)\n// ============================================================================\n\n/**\n * Simple markdown processor for Adaptive Cards.\n * Handles basic markdown syntax: **bold**, *italic*, [links](url), `code`\n */\nconst processMarkdown = (text: string): string => {\n if (!text) return '';\n\n const html = text\n // Escape HTML first\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n // Bold: **text** or __text__\n .replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>')\n .replace(/__(.+?)__/g, '<strong>$1</strong>')\n // Italic: *text* or _text_\n .replace(/\\*(.+?)\\*/g, '<em>$1</em>')\n .replace(/_(.+?)_/g, '<em>$1</em>')\n // Code: `code`\n .replace(\n /`(.+?)`/g,\n '<code style=\"background:#f3f4f6;padding:0.125rem 0.25rem;border-radius:0.25rem;font-family:monospace;\">$1</code>'\n )\n // Links: [text](url)\n .replace(\n /\\[(.+?)\\]\\((.+?)\\)/g,\n '<a href=\"$2\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"color:#2563eb;text-decoration:underline;\">$1</a>'\n )\n // Line breaks\n .replace(/\\n/g, '<br/>');\n\n return html;\n};\n\n// Set up global markdown processor for Adaptive Cards\nif (!AdaptiveCards.AdaptiveCard.onProcessMarkdown) {\n AdaptiveCards.AdaptiveCard.onProcessMarkdown = (text, result) => {\n result.outputHtml = processMarkdown(text);\n result.didProcess = true;\n };\n}\n\n// ============================================================================\n// Host Config - Customize Adaptive Card appearance\n// ============================================================================\n\nconst hostConfig = new AdaptiveCards.HostConfig({\n fontFamily: 'Inter, system-ui, -apple-system, sans-serif',\n containerStyles: {\n default: {\n backgroundColor: '#ffffff',\n foregroundColors: {\n default: {\n default: '#1f2937',\n subtle: '#6b7280',\n },\n accent: {\n default: '#2563eb',\n subtle: '#3b82f6',\n },\n attention: {\n default: '#dc2626',\n subtle: '#ef4444',\n },\n good: {\n default: '#16a34a',\n subtle: '#22c55e',\n },\n warning: {\n default: '#d97706',\n subtle: '#f59e0b',\n },\n light: {\n default: '#9ca3af',\n subtle: '#d1d5db',\n },\n dark: {\n default: '#111827',\n subtle: '#374151',\n },\n },\n },\n emphasis: {\n backgroundColor: '#f3f4f6',\n foregroundColors: {\n default: {\n default: '#1f2937',\n subtle: '#6b7280',\n },\n accent: {\n default: '#2563eb',\n subtle: '#3b82f6',\n },\n attention: {\n default: '#dc2626',\n subtle: '#ef4444',\n },\n good: {\n default: '#16a34a',\n subtle: '#22c55e',\n },\n warning: {\n default: '#d97706',\n subtle: '#f59e0b',\n },\n light: {\n default: '#9ca3af',\n subtle: '#d1d5db',\n },\n dark: {\n default: '#111827',\n subtle: '#374151',\n },\n },\n },\n },\n spacing: {\n small: 4,\n default: 8,\n medium: 12,\n large: 16,\n extraLarge: 24,\n padding: 16,\n },\n separator: {\n lineThickness: 1,\n lineColor: '#e5e7eb',\n },\n imageSizes: {\n small: 40,\n medium: 80,\n large: 160,\n },\n actions: {\n maxActions: 5,\n spacing: AdaptiveCards.Spacing.Default,\n buttonSpacing: 8,\n showCard: {\n actionMode: AdaptiveCards.ShowCardActionMode.Inline,\n inlineTopMargin: 12,\n },\n actionsOrientation: AdaptiveCards.Orientation.Horizontal,\n actionAlignment: AdaptiveCards.ActionAlignment.Left,\n },\n adaptiveCard: {\n allowCustomStyle: true,\n },\n textBlock: {\n headingLevel: 2,\n },\n factSet: {\n title: {\n color: AdaptiveCards.TextColor.Default,\n size: AdaptiveCards.TextSize.Default,\n isSubtle: true,\n weight: AdaptiveCards.TextWeight.Bolder,\n wrap: true,\n },\n value: {\n color: AdaptiveCards.TextColor.Default,\n size: AdaptiveCards.TextSize.Default,\n isSubtle: false,\n weight: AdaptiveCards.TextWeight.Default,\n wrap: true,\n },\n spacing: 8,\n },\n});\n\n// Dark mode host config - duplicating base config with dark overrides\nconst darkHostConfig = new AdaptiveCards.HostConfig({\n fontFamily: 'Inter, system-ui, -apple-system, sans-serif',\n spacing: {\n small: 4,\n default: 8,\n medium: 12,\n large: 16,\n extraLarge: 24,\n padding: 16,\n },\n imageSizes: {\n small: 40,\n medium: 80,\n large: 160,\n },\n actions: {\n maxActions: 5,\n spacing: AdaptiveCards.Spacing.Default,\n buttonSpacing: 8,\n showCard: {\n actionMode: AdaptiveCards.ShowCardActionMode.Inline,\n inlineTopMargin: 12,\n },\n actionsOrientation: AdaptiveCards.Orientation.Horizontal,\n actionAlignment: AdaptiveCards.ActionAlignment.Left,\n },\n adaptiveCard: {\n allowCustomStyle: true,\n },\n textBlock: {\n headingLevel: 2,\n },\n factSet: {\n title: {\n color: AdaptiveCards.TextColor.Default,\n size: AdaptiveCards.TextSize.Default,\n isSubtle: true,\n weight: AdaptiveCards.TextWeight.Bolder,\n wrap: true,\n },\n value: {\n color: AdaptiveCards.TextColor.Default,\n size: AdaptiveCards.TextSize.Default,\n isSubtle: false,\n weight: AdaptiveCards.TextWeight.Default,\n wrap: true,\n },\n spacing: 8,\n },\n containerStyles: {\n default: {\n backgroundColor: '#111827',\n foregroundColors: {\n default: {\n default: '#f9fafb',\n subtle: '#9ca3af',\n },\n accent: {\n default: '#60a5fa',\n subtle: '#3b82f6',\n },\n attention: {\n default: '#f87171',\n subtle: '#ef4444',\n },\n good: {\n default: '#4ade80',\n subtle: '#22c55e',\n },\n warning: {\n default: '#fbbf24',\n subtle: '#f59e0b',\n },\n light: {\n default: '#d1d5db',\n subtle: '#9ca3af',\n },\n dark: {\n default: '#f9fafb',\n subtle: '#e5e7eb',\n },\n },\n },\n emphasis: {\n backgroundColor: '#1f2937',\n foregroundColors: {\n default: {\n default: '#f9fafb',\n subtle: '#9ca3af',\n },\n accent: {\n default: '#60a5fa',\n subtle: '#3b82f6',\n },\n attention: {\n default: '#f87171',\n subtle: '#ef4444',\n },\n good: {\n default: '#4ade80',\n subtle: '#22c55e',\n },\n warning: {\n default: '#fbbf24',\n subtle: '#f59e0b',\n },\n light: {\n default: '#d1d5db',\n subtle: '#9ca3af',\n },\n dark: {\n default: '#f9fafb',\n subtle: '#e5e7eb',\n },\n },\n },\n },\n separator: {\n lineThickness: 1,\n lineColor: '#374151',\n },\n});\n\n// ============================================================================\n// Main Renderer Component\n// ============================================================================\n\nexport const AdaptiveCardRenderer: React.FC<RendererProps> = memo(function AdaptiveCardRenderer({\n widget,\n data,\n template,\n isLoading,\n error,\n onAction,\n}) {\n const containerRef = useRef<HTMLDivElement>(null);\n const cardRef = useRef<AdaptiveCards.AdaptiveCard | null>(null);\n const [renderError, setRenderError] = useState<string | null>(null);\n const [isDarkMode, setIsDarkMode] = useState(false);\n const [templateWarnings, setTemplateWarnings] = useState<string[]>([]);\n const [acDataModule, setAcDataModule] = useState<ACDataModule | null>(null);\n const [templatingError, setTemplatingError] = useState<string | null>(null);\n\n // Load adaptivecards-templating dynamically to avoid Node.js polyfill issues\n useEffect(() => {\n let mounted = true;\n\n import('adaptivecards-templating')\n .then((module) => {\n if (mounted) {\n setAcDataModule(module);\n }\n })\n .catch((err) => {\n if (mounted) {\n setTemplatingError(err.message || 'Failed to load templating library');\n }\n });\n\n return () => {\n mounted = false;\n };\n }, []);\n\n // Detect dark mode\n useEffect(() => {\n const checkDarkMode = () => {\n setIsDarkMode(document.documentElement.classList.contains('dark'));\n };\n checkDarkMode();\n\n // Watch for changes\n const observer = new MutationObserver(checkDarkMode);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['class'],\n });\n\n return () => observer.disconnect();\n }, []);\n\n // Handle action execution\n const handleAction = useCallback(\n (action: AdaptiveCards.Action) => {\n if (action instanceof AdaptiveCards.OpenUrlAction) {\n // Open URL in new tab\n if (action.url) {\n window.open(action.url, '_blank', 'noopener,noreferrer');\n }\n } else if (action instanceof AdaptiveCards.SubmitAction) {\n // Call onAction callback with action data\n if (onAction) {\n onAction(action.title || 'submit', (action.data as Record<string, unknown>) || {});\n }\n } else if (action instanceof AdaptiveCards.ExecuteAction) {\n // Call onAction callback with verb and data\n if (onAction) {\n onAction(action.verb || 'execute', (action.data as Record<string, unknown>) || {});\n }\n } else if (action instanceof AdaptiveCards.ShowCardAction) {\n // ShowCard is handled inline by the SDK\n } else {\n }\n },\n [onAction]\n );\n\n // Render the Adaptive Card\n useEffect(() => {\n if (!containerRef.current) return;\n if (isLoading) return;\n\n // Clear previous content safely\n containerRef.current.innerHTML = '';\n setRenderError(null);\n setTemplateWarnings([]);\n\n // Debug logging\n\n try {\n let cardPayload: object | null = null;\n let warnings: string[] = [];\n\n // Determine the card payload\n if (template) {\n // We have a template - use templating engine if available\n const templateObj = typeof template === 'string' ? JSON.parse(template) : template;\n\n if (acDataModule) {\n try {\n // Create template instance using dynamically loaded module\n const cardTemplate = new acDataModule.Template(templateObj);\n\n // Create evaluation context with data\n // Supports $root for data, $host for host application context\n const context = {\n $root: data || {},\n $host: {\n widgetId: widget?.id,\n widgetTitle: widget?.title,\n theme: isDarkMode ? 'dark' : 'light',\n },\n };\n\n // Expand template with data\n cardPayload = cardTemplate.expand(context);\n\n // Capture any template expansion warnings\n warnings = cardTemplate.getLastTemplateExpansionWarnings();\n if (warnings.length > 0) {\n setTemplateWarnings(warnings);\n }\n } catch (templatingErr) {\n // Templating library threw an error - use fallback\n cardPayload = expandTemplateFallback(templateObj, (data || {}) as Record<string, unknown>) as object;\n warnings.push('Using simplified template expansion (some features may not work)');\n setTemplateWarnings(warnings);\n }\n } else {\n // Templating not available - use fallback expansion\n cardPayload = expandTemplateFallback(templateObj, (data || {}) as Record<string, unknown>) as object;\n if (templatingError) {\n warnings.push(`Using fallback expansion: ${templatingError}`);\n setTemplateWarnings(warnings);\n }\n }\n } else if (data && typeof data === 'object' && 'type' in data && data.type === 'AdaptiveCard') {\n // Data is already an Adaptive Card\n cardPayload = data;\n } else if (data && typeof data === 'object' && 'body' in data) {\n // Data has body property - treat as Adaptive Card\n cardPayload = {\n type: 'AdaptiveCard',\n version: '1.6',\n ...data,\n };\n }\n\n if (!cardPayload) {\n setRenderError('No template or valid Adaptive Card data provided');\n return;\n }\n\n // Create or reuse Adaptive Card instance\n if (!cardRef.current) {\n cardRef.current = new AdaptiveCards.AdaptiveCard();\n }\n const adaptiveCard = cardRef.current;\n\n // Set host config based on theme\n adaptiveCard.hostConfig = isDarkMode ? darkHostConfig : hostConfig;\n\n // Set action handler\n adaptiveCard.onExecuteAction = handleAction;\n\n // Parse the card payload\n adaptiveCard.parse(cardPayload);\n\n // Render to HTML element\n const renderedCard = adaptiveCard.render();\n\n if (renderedCard) {\n // Apply additional styling\n renderedCard.style.height = '100%';\n renderedCard.style.overflow = 'auto';\n\n // Add class for styling consistency\n renderedCard.classList.add('adaptive-card-container');\n\n // Append to container\n containerRef.current.appendChild(renderedCard);\n } else {\n setRenderError('Failed to render Adaptive Card');\n }\n } catch (e) {\n setRenderError((e as Error).message || 'Unknown error rendering card');\n }\n }, [template, data, widget, isLoading, isDarkMode, handleAction, acDataModule, templatingError]);\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 animate-in fade-in duration-200\">\n <div className=\"animate-pulse space-y-3 w-full\">\n <div className=\"h-5 bg-bg-sunken rounded w-3/4\" />\n <div className=\"flex gap-3\">\n <div className=\"h-10 w-10 bg-bg-sunken rounded-full\" />\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-4 bg-bg-sunken rounded w-1/2\" />\n <div className=\"h-3 bg-bg-sunken rounded w-1/3\" />\n </div>\n </div>\n <div className=\"h-4 bg-bg-sunken rounded w-full\" />\n <div className=\"h-4 bg-bg-sunken rounded w-5/6\" />\n <div className=\"grid grid-cols-2 gap-2 mt-4\">\n <div className=\"h-3 bg-bg-sunken rounded\" />\n <div className=\"h-3 bg-bg-sunken rounded\" />\n <div className=\"h-3 bg-bg-sunken rounded\" />\n <div className=\"h-3 bg-bg-sunken rounded\" />\n </div>\n </div>\n </div>\n );\n }\n\n // Error state\n if (error || renderError) {\n return (\n <div className=\"flex items-center justify-center h-full p-4\">\n <div className=\"text-center max-w-sm\">\n <div className=\"w-12 h-12 mx-auto mb-3 rounded-full bg-red-100 dark:bg-red-900/30 flex items-center justify-center\">\n <svg\n className=\"w-6 h-6 text-red-600 dark:text-red-400\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <p className=\"font-medium text-state-error\">Error rendering card</p>\n <p className=\"text-sm text-secondary mt-1\">{error || renderError}</p>\n {templateWarnings.length > 0 && (\n <div className=\"mt-3 p-2 bg-yellow-50 dark:bg-yellow-900/20 rounded text-left\">\n <p className=\"text-xs font-medium text-yellow-700 dark:text-yellow-400 mb-1\">Template Warnings:</p>\n {templateWarnings.map((warning, i) => (\n <p key={i} className=\"text-xs text-yellow-600 dark:text-yellow-300\">\n {warning}\n </p>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n }\n\n // Empty state - no template or data\n if (!template && !data) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 animate-in fade-in duration-200\">\n <div className=\"text-center max-w-sm\">\n <div className=\"w-12 h-12 mx-auto mb-3 rounded-full bg-bg-sunken flex items-center justify-center\">\n <svg className=\"w-6 h-6 text-text-secondary\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z\"\n />\n </svg>\n </div>\n <p className=\"font-medium text-secondary\">No content</p>\n <p className=\"text-sm text-text-secondary mt-1\">\n Configure a template or provide data to display an Adaptive Card\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div\n ref={containerRef}\n className=\"adaptive-card-wrapper h-full overflow-auto\"\n style={{\n // CSS custom properties for further customization\n ['--ac-font-family' as string]: 'Inter, system-ui, -apple-system, sans-serif',\n }}\n />\n );\n});\n\n// ============================================================================\n// Global Styles for Adaptive Cards\n// ============================================================================\n\n// Inject global styles for Adaptive Cards\nconst styleId = 'adaptive-card-global-styles';\nif (typeof document !== 'undefined' && !document.getElementById(styleId)) {\n const style = document.createElement('style');\n style.id = styleId;\n style.textContent = `\n .adaptive-card-wrapper {\n font-family: Inter, system-ui, -apple-system, sans-serif;\n }\n\n .adaptive-card-wrapper .ac-textBlock {\n font-family: inherit;\n }\n\n .adaptive-card-wrapper .ac-image.ac-image-person {\n border-radius: 50%;\n object-fit: cover;\n }\n\n .adaptive-card-wrapper .ac-pushButton {\n font-family: inherit;\n font-weight: 500;\n border-radius: 0.375rem;\n padding: 0.5rem 1rem;\n transition: all 0.15s ease-in-out;\n }\n\n .adaptive-card-wrapper .ac-pushButton:hover {\n filter: brightness(0.95);\n }\n\n .adaptive-card-wrapper .ac-input {\n font-family: inherit;\n border-radius: 0.375rem;\n border: 1px solid #d1d5db;\n padding: 0.5rem 0.75rem;\n }\n\n .dark .adaptive-card-wrapper .ac-input {\n border-color: #4b5563;\n background-color: #1f2937;\n color: #f9fafb;\n }\n\n .adaptive-card-wrapper .ac-input:focus {\n outline: none;\n ring: 2px;\n ring-color: #3b82f6;\n }\n\n .adaptive-card-wrapper .ac-separator {\n margin: 0.5rem 0;\n }\n\n .adaptive-card-wrapper .ac-factSet {\n display: grid;\n grid-template-columns: auto 1fr;\n gap: 0.25rem 1rem;\n }\n\n .adaptive-card-wrapper .ac-fact-title {\n color: #6b7280;\n }\n\n .dark .adaptive-card-wrapper .ac-fact-title {\n color: #9ca3af;\n }\n\n .adaptive-card-wrapper .ac-actionSet {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n margin-top: 1rem;\n padding-top: 1rem;\n border-top: 1px solid #e5e7eb;\n }\n\n .dark .adaptive-card-wrapper .ac-actionSet {\n border-top-color: #374151;\n }\n\n /* Markdown rendered code blocks in dark mode */\n .dark .adaptive-card-wrapper code {\n background: #374151 !important;\n color: #f9fafb !important;\n }\n\n /* Markdown links in dark mode */\n .dark .adaptive-card-wrapper a {\n color: #60a5fa !important;\n }\n\n /* TextBlock with markdown - ensure proper wrapping */\n .adaptive-card-wrapper .ac-textBlock p {\n margin: 0;\n }\n\n .adaptive-card-wrapper .ac-textBlock strong {\n font-weight: 600;\n }\n\n .adaptive-card-wrapper .ac-textBlock em {\n font-style: italic;\n }\n `;\n document.head.appendChild(style);\n}\n\nexport default AdaptiveCardRenderer;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AdaptiveCardRenderer.js","names":[],"sources":["../../../../src/bigconsole/renderers/adapters/AdaptiveCardRenderer.tsx"],"sourcesContent":["/**\n * Adaptive Card Renderer (v2.1)\n *\n * Renders widgets using the official Microsoft Adaptive Cards SDK.\n * Based on patterns from https://github.com/microsoft/AdaptiveCards\n *\n * Supports full template binding with data context including:\n * - Nested object access: ${creator.name}\n * - Array iteration with $data\n * - Conditional rendering with $when\n * - Date formatting: {{DATE(${field},SHORT)}}\n * - All Adaptive Card elements and actions\n * - Markdown rendering in TextBlocks\n *\n * @see https://adaptivecards.io/\n */\n\nimport React, { memo, useEffect, useRef, useCallback, useState } from 'react';\nimport * as AdaptiveCards from 'adaptivecards';\nimport type { RendererProps } from '../types/renderer';\n\n// Type for the templating module\ntype ACDataModule = typeof import('adaptivecards-templating');\n\n// ============================================================================\n// Fallback Template Expansion (when adaptivecards-templating fails)\n// ============================================================================\n\n/**\n * Simple template expansion fallback.\n * Handles basic ${property}, ${object.property} syntax, and $data iteration.\n * Used when adaptivecards-templating library fails to load.\n */\nfunction expandTemplateFallback(\n template: unknown,\n data: Record<string, unknown>,\n rootData?: Record<string, unknown>\n): unknown {\n // Keep reference to root data for nested expansions\n const root = rootData || data;\n\n if (typeof template === 'string') {\n // Replace ${property} patterns\n return template.replace(/\\$\\{([^}]+)\\}/g, (match, path) => {\n const trimmedPath = path.trim();\n // Try current data first, then root data\n let value = getNestedValue(data, trimmedPath);\n if (value === undefined && root !== data) {\n value = getNestedValue(root, trimmedPath);\n }\n return value !== undefined ? String(value) : match;\n });\n }\n\n if (Array.isArray(template)) {\n return template.flatMap((item) => {\n const expanded = expandTemplateFallback(item, data, root);\n return Array.isArray(expanded) ? expanded : [expanded];\n });\n }\n\n if (template && typeof template === 'object') {\n const templateObj = template as Record<string, unknown>;\n\n // Handle $data iteration\n if ('$data' in templateObj) {\n const dataPath = templateObj.$data;\n let arrayData: unknown[] = [];\n\n if (typeof dataPath === 'string') {\n // Extract array path from ${arrayName} pattern\n const match = dataPath.match(/^\\$\\{([^}]+)\\}$/);\n if (match) {\n const arrValue = getNestedValue(data, match[1].trim()) || getNestedValue(root, match[1].trim());\n if (Array.isArray(arrValue)) {\n arrayData = arrValue;\n }\n }\n } else if (Array.isArray(dataPath)) {\n arrayData = dataPath;\n }\n\n if (arrayData.length > 0) {\n // Create a copy of template without $data for each array item\n const templateWithoutData = Object.fromEntries(Object.entries(templateObj).filter(([key]) => key !== '$data'));\n return arrayData.map((item) => {\n const itemData =\n typeof item === 'object' && item !== null ? (item as Record<string, unknown>) : { value: item };\n // Merge item data with root data for nested references\n const mergedData = { ...root, ...itemData };\n return expandTemplateFallback(templateWithoutData, mergedData, root);\n });\n }\n }\n\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(templateObj)) {\n if (key === '$data') continue; // Already handled above\n if (key === '$when') continue; // Skip conditional rendering in fallback\n result[key] = expandTemplateFallback(value, data, root);\n }\n return result;\n }\n\n return template;\n}\n\n/**\n * Get nested property value from object using dot notation.\n * e.g., getNestedValue({a: {b: 1}}, 'a.b') => 1\n */\nfunction getNestedValue(obj: Record<string, unknown>, path: string): unknown {\n const parts = path.split('.');\n let current: unknown = obj;\n\n for (const part of parts) {\n if (current === null || current === undefined) return undefined;\n if (typeof current !== 'object') return undefined;\n current = (current as Record<string, unknown>)[part];\n }\n\n return current;\n}\n\n// ============================================================================\n// Markdown Processing Setup (from official adaptivecards-react)\n// ============================================================================\n\n/**\n * Simple markdown processor for Adaptive Cards.\n * Handles basic markdown syntax: **bold**, *italic*, [links](url), `code`\n */\nconst processMarkdown = (text: string): string => {\n if (!text) return '';\n\n const html = text\n // Escape HTML first\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n // Bold: **text** or __text__\n .replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>')\n .replace(/__(.+?)__/g, '<strong>$1</strong>')\n // Italic: *text* or _text_\n .replace(/\\*(.+?)\\*/g, '<em>$1</em>')\n .replace(/_(.+?)_/g, '<em>$1</em>')\n // Code: `code`\n .replace(\n /`(.+?)`/g,\n '<code style=\"background:#f3f4f6;padding:0.125rem 0.25rem;border-radius:0.25rem;font-family:monospace;\">$1</code>'\n )\n // Links: [text](url)\n .replace(\n /\\[(.+?)\\]\\((.+?)\\)/g,\n '<a href=\"$2\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"color:#2563eb;text-decoration:underline;\">$1</a>'\n )\n // Line breaks\n .replace(/\\n/g, '<br/>');\n\n return html;\n};\n\n// Set up global markdown processor for Adaptive Cards\nif (!AdaptiveCards.AdaptiveCard.onProcessMarkdown) {\n AdaptiveCards.AdaptiveCard.onProcessMarkdown = (text, result) => {\n result.outputHtml = processMarkdown(text);\n result.didProcess = true;\n };\n}\n\n// ============================================================================\n// Host Config - Customize Adaptive Card appearance\n// ============================================================================\n\nconst hostConfig = new AdaptiveCards.HostConfig({\n fontFamily: 'Inter, system-ui, -apple-system, sans-serif',\n containerStyles: {\n default: {\n backgroundColor: '#ffffff',\n foregroundColors: {\n default: {\n default: '#1f2937',\n subtle: '#6b7280',\n },\n accent: {\n default: '#2563eb',\n subtle: '#3b82f6',\n },\n attention: {\n default: '#dc2626',\n subtle: '#ef4444',\n },\n good: {\n default: '#16a34a',\n subtle: '#22c55e',\n },\n warning: {\n default: '#d97706',\n subtle: '#f59e0b',\n },\n light: {\n default: '#9ca3af',\n subtle: '#d1d5db',\n },\n dark: {\n default: '#111827',\n subtle: '#374151',\n },\n },\n },\n emphasis: {\n backgroundColor: '#f3f4f6',\n foregroundColors: {\n default: {\n default: '#1f2937',\n subtle: '#6b7280',\n },\n accent: {\n default: '#2563eb',\n subtle: '#3b82f6',\n },\n attention: {\n default: '#dc2626',\n subtle: '#ef4444',\n },\n good: {\n default: '#16a34a',\n subtle: '#22c55e',\n },\n warning: {\n default: '#d97706',\n subtle: '#f59e0b',\n },\n light: {\n default: '#9ca3af',\n subtle: '#d1d5db',\n },\n dark: {\n default: '#111827',\n subtle: '#374151',\n },\n },\n },\n },\n spacing: {\n small: 4,\n default: 8,\n medium: 12,\n large: 16,\n extraLarge: 24,\n padding: 16,\n },\n separator: {\n lineThickness: 1,\n lineColor: '#e5e7eb',\n },\n imageSizes: {\n small: 40,\n medium: 80,\n large: 160,\n },\n actions: {\n maxActions: 5,\n spacing: AdaptiveCards.Spacing.Default,\n buttonSpacing: 8,\n showCard: {\n actionMode: AdaptiveCards.ShowCardActionMode.Inline,\n inlineTopMargin: 12,\n },\n actionsOrientation: AdaptiveCards.Orientation.Horizontal,\n actionAlignment: AdaptiveCards.ActionAlignment.Left,\n },\n adaptiveCard: {\n allowCustomStyle: true,\n },\n textBlock: {\n headingLevel: 2,\n },\n factSet: {\n title: {\n color: AdaptiveCards.TextColor.Default,\n size: AdaptiveCards.TextSize.Default,\n isSubtle: true,\n weight: AdaptiveCards.TextWeight.Bolder,\n wrap: true,\n },\n value: {\n color: AdaptiveCards.TextColor.Default,\n size: AdaptiveCards.TextSize.Default,\n isSubtle: false,\n weight: AdaptiveCards.TextWeight.Default,\n wrap: true,\n },\n spacing: 8,\n },\n});\n\n// Dark mode host config - duplicating base config with dark overrides\nconst darkHostConfig = new AdaptiveCards.HostConfig({\n fontFamily: 'Inter, system-ui, -apple-system, sans-serif',\n spacing: {\n small: 4,\n default: 8,\n medium: 12,\n large: 16,\n extraLarge: 24,\n padding: 16,\n },\n imageSizes: {\n small: 40,\n medium: 80,\n large: 160,\n },\n actions: {\n maxActions: 5,\n spacing: AdaptiveCards.Spacing.Default,\n buttonSpacing: 8,\n showCard: {\n actionMode: AdaptiveCards.ShowCardActionMode.Inline,\n inlineTopMargin: 12,\n },\n actionsOrientation: AdaptiveCards.Orientation.Horizontal,\n actionAlignment: AdaptiveCards.ActionAlignment.Left,\n },\n adaptiveCard: {\n allowCustomStyle: true,\n },\n textBlock: {\n headingLevel: 2,\n },\n factSet: {\n title: {\n color: AdaptiveCards.TextColor.Default,\n size: AdaptiveCards.TextSize.Default,\n isSubtle: true,\n weight: AdaptiveCards.TextWeight.Bolder,\n wrap: true,\n },\n value: {\n color: AdaptiveCards.TextColor.Default,\n size: AdaptiveCards.TextSize.Default,\n isSubtle: false,\n weight: AdaptiveCards.TextWeight.Default,\n wrap: true,\n },\n spacing: 8,\n },\n containerStyles: {\n default: {\n backgroundColor: '#111827',\n foregroundColors: {\n default: {\n default: '#f9fafb',\n subtle: '#9ca3af',\n },\n accent: {\n default: '#60a5fa',\n subtle: '#3b82f6',\n },\n attention: {\n default: '#f87171',\n subtle: '#ef4444',\n },\n good: {\n default: '#4ade80',\n subtle: '#22c55e',\n },\n warning: {\n default: '#fbbf24',\n subtle: '#f59e0b',\n },\n light: {\n default: '#d1d5db',\n subtle: '#9ca3af',\n },\n dark: {\n default: '#f9fafb',\n subtle: '#e5e7eb',\n },\n },\n },\n emphasis: {\n backgroundColor: '#1f2937',\n foregroundColors: {\n default: {\n default: '#f9fafb',\n subtle: '#9ca3af',\n },\n accent: {\n default: '#60a5fa',\n subtle: '#3b82f6',\n },\n attention: {\n default: '#f87171',\n subtle: '#ef4444',\n },\n good: {\n default: '#4ade80',\n subtle: '#22c55e',\n },\n warning: {\n default: '#fbbf24',\n subtle: '#f59e0b',\n },\n light: {\n default: '#d1d5db',\n subtle: '#9ca3af',\n },\n dark: {\n default: '#f9fafb',\n subtle: '#e5e7eb',\n },\n },\n },\n },\n separator: {\n lineThickness: 1,\n lineColor: '#374151',\n },\n});\n\n// ============================================================================\n// Main Renderer Component\n// ============================================================================\n\nexport const AdaptiveCardRenderer: React.FC<RendererProps> = memo(function AdaptiveCardRenderer({\n widget,\n data,\n template,\n isLoading,\n error,\n onAction,\n}) {\n const containerRef = useRef<HTMLDivElement>(null);\n const cardRef = useRef<AdaptiveCards.AdaptiveCard | null>(null);\n const [renderError, setRenderError] = useState<string | null>(null);\n const [isDarkMode, setIsDarkMode] = useState(false);\n const [templateWarnings, setTemplateWarnings] = useState<string[]>([]);\n const [acDataModule, setAcDataModule] = useState<ACDataModule | null>(null);\n const [templatingError, setTemplatingError] = useState<string | null>(null);\n\n // Load adaptivecards-templating dynamically to avoid Node.js polyfill issues\n useEffect(() => {\n let mounted = true;\n\n import('adaptivecards-templating')\n .then((module) => {\n if (mounted) {\n setAcDataModule(module);\n }\n })\n .catch((err) => {\n if (mounted) {\n setTemplatingError(err.message || 'Failed to load templating library');\n }\n });\n\n return () => {\n mounted = false;\n };\n }, []);\n\n // Detect dark mode\n useEffect(() => {\n const checkDarkMode = () => {\n setIsDarkMode(document.documentElement.classList.contains('dark'));\n };\n checkDarkMode();\n\n // Watch for changes\n const observer = new MutationObserver(checkDarkMode);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['class'],\n });\n\n return () => observer.disconnect();\n }, []);\n\n // Handle action execution\n const handleAction = useCallback(\n (action: AdaptiveCards.Action) => {\n if (action instanceof AdaptiveCards.OpenUrlAction) {\n // Open URL in new tab\n if (action.url) {\n window.open(action.url, '_blank', 'noopener,noreferrer');\n }\n } else if (action instanceof AdaptiveCards.SubmitAction) {\n // Call onAction callback with action data\n if (onAction) {\n onAction(action.title || 'submit', (action.data as Record<string, unknown>) || {});\n }\n } else if (action instanceof AdaptiveCards.ExecuteAction) {\n // Call onAction callback with verb and data\n if (onAction) {\n onAction(action.verb || 'execute', (action.data as Record<string, unknown>) || {});\n }\n } else if (action instanceof AdaptiveCards.ShowCardAction) {\n // ShowCard is handled inline by the SDK\n } else {\n }\n },\n [onAction]\n );\n\n // Render the Adaptive Card\n useEffect(() => {\n if (!containerRef.current) return;\n if (isLoading) return;\n\n // Clear previous content safely\n containerRef.current.innerHTML = '';\n setRenderError(null);\n setTemplateWarnings([]);\n\n // Debug logging\n\n try {\n let cardPayload: object | null = null;\n let warnings: string[] = [];\n\n // Determine the card payload\n if (template) {\n // We have a template - use templating engine if available\n const templateObj = typeof template === 'string' ? JSON.parse(template) : template;\n\n if (acDataModule) {\n try {\n // Create template instance using dynamically loaded module\n const cardTemplate = new acDataModule.Template(templateObj);\n\n // Create evaluation context with data\n // Supports $root for data, $host for host application context\n const context = {\n $root: data || {},\n $host: {\n widgetId: widget?.id,\n widgetTitle: widget?.title,\n theme: isDarkMode ? 'dark' : 'light',\n },\n };\n\n // Expand template with data\n cardPayload = cardTemplate.expand(context);\n\n // Capture any template expansion warnings\n warnings = cardTemplate.getLastTemplateExpansionWarnings();\n if (warnings.length > 0) {\n setTemplateWarnings(warnings);\n }\n } catch (templatingErr) {\n // Templating library threw an error - use fallback\n cardPayload = expandTemplateFallback(templateObj, (data || {}) as Record<string, unknown>) as object;\n warnings.push('Using simplified template expansion (some features may not work)');\n setTemplateWarnings(warnings);\n }\n } else {\n // Templating not available - use fallback expansion\n cardPayload = expandTemplateFallback(templateObj, (data || {}) as Record<string, unknown>) as object;\n if (templatingError) {\n warnings.push(`Using fallback expansion: ${templatingError}`);\n setTemplateWarnings(warnings);\n }\n }\n } else if (data && typeof data === 'object' && 'type' in data && data.type === 'AdaptiveCard') {\n // Data is already an Adaptive Card\n cardPayload = data;\n } else if (data && typeof data === 'object' && 'body' in data) {\n // Data has body property - treat as Adaptive Card\n cardPayload = {\n type: 'AdaptiveCard',\n version: '1.6',\n ...data,\n };\n }\n\n if (!cardPayload) {\n setRenderError('No template or valid Adaptive Card data provided');\n return;\n }\n\n // Create or reuse Adaptive Card instance\n if (!cardRef.current) {\n cardRef.current = new AdaptiveCards.AdaptiveCard();\n }\n const adaptiveCard = cardRef.current;\n\n // Set host config based on theme\n adaptiveCard.hostConfig = isDarkMode ? darkHostConfig : hostConfig;\n\n // Set action handler\n adaptiveCard.onExecuteAction = handleAction;\n\n // Parse the card payload\n adaptiveCard.parse(cardPayload);\n\n // Render to HTML element\n const renderedCard = adaptiveCard.render();\n\n if (renderedCard) {\n // Apply additional styling\n renderedCard.style.height = '100%';\n renderedCard.style.overflow = 'auto';\n\n // Add class for styling consistency\n renderedCard.classList.add('adaptive-card-container');\n\n // Append to container\n containerRef.current.appendChild(renderedCard);\n } else {\n setRenderError('Failed to render Adaptive Card');\n }\n } catch (e) {\n setRenderError((e as Error).message || 'Unknown error rendering card');\n }\n }, [template, data, widget, isLoading, isDarkMode, handleAction, acDataModule, templatingError]);\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 animate-in fade-in duration-200\">\n <div className=\"animate-pulse space-y-3 w-full\">\n <div className=\"h-5 bg-bg-sunken rounded w-3/4\" />\n <div className=\"flex gap-3\">\n <div className=\"h-10 w-10 bg-bg-sunken rounded-full\" />\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-4 bg-bg-sunken rounded w-1/2\" />\n <div className=\"h-3 bg-bg-sunken rounded w-1/3\" />\n </div>\n </div>\n <div className=\"h-4 bg-bg-sunken rounded w-full\" />\n <div className=\"h-4 bg-bg-sunken rounded w-5/6\" />\n <div className=\"grid grid-cols-2 gap-2 mt-4\">\n <div className=\"h-3 bg-bg-sunken rounded\" />\n <div className=\"h-3 bg-bg-sunken rounded\" />\n <div className=\"h-3 bg-bg-sunken rounded\" />\n <div className=\"h-3 bg-bg-sunken rounded\" />\n </div>\n </div>\n </div>\n );\n }\n\n // Error state\n if (error || renderError) {\n return (\n <div className=\"flex items-center justify-center h-full p-4\">\n <div className=\"text-center max-w-sm\">\n <div className=\"w-12 h-12 mx-auto mb-3 rounded-full bg-red-100 dark:bg-red-900/30 flex items-center justify-center\">\n <svg\n className=\"w-6 h-6 text-red-600 dark:text-red-400\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <p className=\"font-medium text-status-error-text\">Error rendering card</p>\n <p className=\"text-sm text-secondary mt-1\">{error || renderError}</p>\n {templateWarnings.length > 0 && (\n <div className=\"mt-3 p-2 bg-yellow-50 dark:bg-yellow-900/20 rounded text-left\">\n <p className=\"text-xs font-medium text-yellow-700 dark:text-yellow-400 mb-1\">Template Warnings:</p>\n {templateWarnings.map((warning, i) => (\n <p key={i} className=\"text-xs text-yellow-600 dark:text-yellow-300\">\n {warning}\n </p>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n }\n\n // Empty state - no template or data\n if (!template && !data) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 animate-in fade-in duration-200\">\n <div className=\"text-center max-w-sm\">\n <div className=\"w-12 h-12 mx-auto mb-3 rounded-full bg-bg-sunken flex items-center justify-center\">\n <svg className=\"w-6 h-6 text-text-secondary\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z\"\n />\n </svg>\n </div>\n <p className=\"font-medium text-secondary\">No content</p>\n <p className=\"text-sm text-text-secondary mt-1\">\n Configure a template or provide data to display an Adaptive Card\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div\n ref={containerRef}\n className=\"adaptive-card-wrapper h-full overflow-auto\"\n style={{\n // CSS custom properties for further customization\n ['--ac-font-family' as string]: 'Inter, system-ui, -apple-system, sans-serif',\n }}\n />\n );\n});\n\n// ============================================================================\n// Global Styles for Adaptive Cards\n// ============================================================================\n\n// Inject global styles for Adaptive Cards\nconst styleId = 'adaptive-card-global-styles';\nif (typeof document !== 'undefined' && !document.getElementById(styleId)) {\n const style = document.createElement('style');\n style.id = styleId;\n style.textContent = `\n .adaptive-card-wrapper {\n font-family: Inter, system-ui, -apple-system, sans-serif;\n }\n\n .adaptive-card-wrapper .ac-textBlock {\n font-family: inherit;\n }\n\n .adaptive-card-wrapper .ac-image.ac-image-person {\n border-radius: 50%;\n object-fit: cover;\n }\n\n .adaptive-card-wrapper .ac-pushButton {\n font-family: inherit;\n font-weight: 500;\n border-radius: 0.375rem;\n padding: 0.5rem 1rem;\n transition: all 0.15s ease-in-out;\n }\n\n .adaptive-card-wrapper .ac-pushButton:hover {\n filter: brightness(0.95);\n }\n\n .adaptive-card-wrapper .ac-input {\n font-family: inherit;\n border-radius: 0.375rem;\n border: 1px solid #d1d5db;\n padding: 0.5rem 0.75rem;\n }\n\n .dark .adaptive-card-wrapper .ac-input {\n border-color: #4b5563;\n background-color: #1f2937;\n color: #f9fafb;\n }\n\n .adaptive-card-wrapper .ac-input:focus {\n outline: none;\n ring: 2px;\n ring-color: #3b82f6;\n }\n\n .adaptive-card-wrapper .ac-separator {\n margin: 0.5rem 0;\n }\n\n .adaptive-card-wrapper .ac-factSet {\n display: grid;\n grid-template-columns: auto 1fr;\n gap: 0.25rem 1rem;\n }\n\n .adaptive-card-wrapper .ac-fact-title {\n color: #6b7280;\n }\n\n .dark .adaptive-card-wrapper .ac-fact-title {\n color: #9ca3af;\n }\n\n .adaptive-card-wrapper .ac-actionSet {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n margin-top: 1rem;\n padding-top: 1rem;\n border-top: 1px solid #e5e7eb;\n }\n\n .dark .adaptive-card-wrapper .ac-actionSet {\n border-top-color: #374151;\n }\n\n /* Markdown rendered code blocks in dark mode */\n .dark .adaptive-card-wrapper code {\n background: #374151 !important;\n color: #f9fafb !important;\n }\n\n /* Markdown links in dark mode */\n .dark .adaptive-card-wrapper a {\n color: #60a5fa !important;\n }\n\n /* TextBlock with markdown - ensure proper wrapping */\n .adaptive-card-wrapper .ac-textBlock p {\n margin: 0;\n }\n\n .adaptive-card-wrapper .ac-textBlock strong {\n font-weight: 600;\n }\n\n .adaptive-card-wrapper .ac-textBlock em {\n font-style: italic;\n }\n `;\n document.head.appendChild(style);\n}\n\nexport default AdaptiveCardRenderer;\n"],"mappings":"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"}
|
|
@@ -65,8 +65,8 @@ function o(t) {
|
|
|
65
65
|
}
|
|
66
66
|
function s(e) {
|
|
67
67
|
switch (e) {
|
|
68
|
-
case "primary": return "bg-
|
|
69
|
-
case "danger": return "bg-
|
|
68
|
+
case "primary": return "bg-status-success-bg hover:bg-status-success-bg/90 text-white transition-colors";
|
|
69
|
+
case "danger": return "bg-status-error-bg hover:bg-status-error-bg/90 text-white transition-colors";
|
|
70
70
|
default: return "bg-bg-sunken hover:bg-bg-sunken/80 text-primary transition-colors";
|
|
71
71
|
}
|
|
72
72
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BlockKitRenderer.js","names":[],"sources":["../../../../src/bigconsole/renderers/adapters/BlockKitRenderer.tsx"],"sourcesContent":["/**\n * Slack Block Kit Renderer (v2.0)\n *\n * Renders widgets using Slack Block Kit format.\n * Supports common block types: section, divider, header, context, image, actions.\n */\n\nimport React, { memo, useCallback, useMemo, type ReactNode } from 'react';\nimport type { RendererProps, BlockKitTemplate, BlockKitElement, BlockKitText } from '../types/renderer';\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Render Block Kit text (plain_text or mrkdwn)\n * Uses React elements instead of dangerouslySetInnerHTML for better security\n */\nfunction renderText(text: BlockKitText | string | undefined): React.ReactNode {\n if (!text) return null;\n\n if (typeof text === 'string') {\n return text;\n }\n\n if (text.type === 'mrkdwn') {\n // Parse mrkdwn and convert to React elements\n const content = text.text || '';\n\n // Parse the markdown into React elements\n const elements: React.ReactNode[] = [];\n let remaining = content;\n let key = 0;\n\n // Process text segment by segment\n while (remaining.length > 0) {\n // Try to match bold: *text*\n const boldMatch = remaining.match(/^\\*([^*]+)\\*/);\n if (boldMatch) {\n elements.push(<strong key={key++}>{boldMatch[1]}</strong>);\n remaining = remaining.slice(boldMatch[0].length);\n continue;\n }\n\n // Try to match italic: _text_\n const italicMatch = remaining.match(/^_([^_]+)_/);\n if (italicMatch) {\n elements.push(<em key={key++}>{italicMatch[1]}</em>);\n remaining = remaining.slice(italicMatch[0].length);\n continue;\n }\n\n // Try to match strike: ~text~\n const strikeMatch = remaining.match(/^~([^~]+)~/);\n if (strikeMatch) {\n elements.push(<del key={key++}>{strikeMatch[1]}</del>);\n remaining = remaining.slice(strikeMatch[0].length);\n continue;\n }\n\n // Try to match code: `text`\n const codeMatch = remaining.match(/^`([^`]+)`/);\n if (codeMatch) {\n elements.push(\n <code key={key++} className=\"bg-bg-sunken px-1 rounded text-sm\">\n {codeMatch[1]}\n </code>\n );\n remaining = remaining.slice(codeMatch[0].length);\n continue;\n }\n\n // Try to match link with text: <url|text>\n const linkTextMatch = remaining.match(/^<([^|>]+)\\|([^>]+)>/);\n if (linkTextMatch) {\n elements.push(\n <a\n key={key++}\n href={linkTextMatch[1]}\n className=\"text-blue-600 dark:text-blue-400 underline\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {linkTextMatch[2]}\n </a>\n );\n remaining = remaining.slice(linkTextMatch[0].length);\n continue;\n }\n\n // Try to match plain link: <url>\n const linkMatch = remaining.match(/^<([^|>]+)>/);\n if (linkMatch) {\n elements.push(\n <a\n key={key++}\n href={linkMatch[1]}\n className=\"text-blue-600 dark:text-blue-400 underline\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {linkMatch[1]}\n </a>\n );\n remaining = remaining.slice(linkMatch[0].length);\n continue;\n }\n\n // No special formatting found at start - extract plain text until next special char\n const plainMatch = remaining.match(/^[^*_~`<]+/);\n if (plainMatch) {\n elements.push(<React.Fragment key={key++}>{plainMatch[0]}</React.Fragment>);\n remaining = remaining.slice(plainMatch[0].length);\n continue;\n }\n\n // Single special char that didn't match a pattern - output as-is\n elements.push(<React.Fragment key={key++}>{remaining[0]}</React.Fragment>);\n remaining = remaining.slice(1);\n }\n\n return <span>{elements}</span>;\n }\n\n // Plain text with optional emoji\n return text.text;\n}\n\n/**\n * Get button style class\n */\nfunction getButtonStyleClass(style?: string): string {\n switch (style) {\n case 'primary':\n return 'bg-state-success hover:bg-state-success/90 text-white transition-colors';\n case 'danger':\n return 'bg-state-error hover:bg-state-error/90 text-white transition-colors';\n default:\n return 'bg-bg-sunken hover:bg-bg-sunken/80 text-primary transition-colors';\n }\n}\n\n// ============================================================================\n// Template Binding\n// ============================================================================\n\nfunction bindDataToTemplate<T>(template: T, data: Record<string, unknown>): T {\n if (template === null || template === undefined) {\n return template;\n }\n\n if (typeof template === 'string') {\n // Replace ${path} with actual values\n return template.replace(/\\$\\{([^}]+)\\}/g, (_, path) => {\n const value = getNestedValue(data, path.trim());\n return value !== undefined ? String(value) : '';\n }) as T;\n }\n\n if (Array.isArray(template)) {\n return template.map((item) => bindDataToTemplate(item, data)) as T;\n }\n\n if (typeof template === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(template)) {\n result[key] = bindDataToTemplate(value, data);\n }\n return result as T;\n }\n\n return template;\n}\n\nfunction getNestedValue(obj: Record<string, unknown>, path: string): unknown {\n const parts = path.split('.');\n let current: unknown = obj;\n\n for (const part of parts) {\n if (current && typeof current === 'object' && part in (current as Record<string, unknown>)) {\n current = (current as Record<string, unknown>)[part];\n } else {\n return undefined;\n }\n }\n\n return current;\n}\n\n// ============================================================================\n// Block Renderers\n// ============================================================================\n\ninterface BlockRendererProps {\n block: BlockKitElement;\n index: number;\n onAction?: (actionId: string, value?: string, data?: Record<string, unknown>) => void;\n}\n\nconst BlockRenderer: React.FC<BlockRendererProps> = memo(function BlockRenderer({ block, index: _index, onAction }) {\n switch (block.type) {\n // Header block\n case 'header':\n return (\n <div className=\"py-2\">\n <h2 className=\"text-lg font-bold text-primary\">{renderText(block.text)}</h2>\n </div>\n );\n\n // Section block\n case 'section':\n return (\n <div className=\"py-2 flex items-start gap-4\">\n <div className=\"flex-1\">\n {block.text && <p className=\"text-primary\">{renderText(block.text)}</p>}\n {block.fields && block.fields.length > 0 && (\n <div className=\"grid grid-cols-2 gap-2 mt-2\">\n {block.fields.map((field, i) => (\n <div key={i} className=\"text-sm\">\n {renderText(field)}\n </div>\n ))}\n </div>\n )}\n </div>\n {block.accessory && (\n <div className=\"flex-shrink-0\">\n <AccessoryRenderer accessory={block.accessory as BlockKitElement} onAction={onAction} />\n </div>\n )}\n </div>\n );\n\n // Divider block\n case 'divider':\n return <hr className=\"my-2 border-default\" />;\n\n // Image block\n case 'image':\n return (\n <div className=\"py-2\">\n {block.title && <p className=\"text-sm text-secondary mb-1\">{renderText(block.title)}</p>}\n <img src={block.image_url} alt={block.alt_text || ''} className=\"max-w-full h-auto rounded\" />\n </div>\n );\n\n // Context block\n case 'context':\n return (\n <div className=\"py-1 flex items-center gap-2 flex-wrap text-sm text-secondary\">\n {block.elements?.map((element, i) => (\n <span key={i}>\n {element.type === 'image' ? (\n <img src={element.image_url} alt={element.alt_text || ''} className=\"w-5 h-5 rounded inline-block\" />\n ) : (\n renderText(element.text as BlockKitText)\n )}\n </span>\n ))}\n </div>\n );\n\n // Actions block\n case 'actions':\n return (\n <div className=\"py-2 flex gap-2 flex-wrap\">\n {block.elements?.map((element, i) => (\n <ActionRenderer key={i} element={element} onAction={onAction} />\n ))}\n </div>\n );\n\n // Input block (simplified)\n case 'input':\n return (\n <div className=\"py-2\">\n {block.label ? (\n <label className=\"block text-sm font-medium text-secondary mb-1\">\n {renderText(block.label as BlockKitText) as ReactNode}\n </label>\n ) : null}\n <input\n type=\"text\"\n className=\"w-full px-3 py-2 border border-default rounded-md bg-surface text-primary transition-colors focus:ring-2 focus:ring-primary/20\"\n placeholder={(() => {\n const element = block.element as Record<string, unknown> | undefined;\n return element?.placeholder ? (renderText(element.placeholder as BlockKitText) as string) : '';\n })()}\n />\n </div>\n );\n\n // Rich text block (simplified)\n case 'rich_text':\n return (\n <div className=\"py-2 text-primary\">\n {block.elements?.map((element, i) => (\n <span key={i}>{renderText(element.text as BlockKitText)}</span>\n ))}\n </div>\n );\n\n default:\n return null;\n }\n});\n\n// ============================================================================\n// Accessory Renderer\n// ============================================================================\n\ninterface AccessoryRendererProps {\n accessory: BlockKitElement;\n onAction?: (actionId: string, value?: string, data?: Record<string, unknown>) => void;\n}\n\nconst AccessoryRenderer: React.FC<AccessoryRendererProps> = memo(function AccessoryRenderer({ accessory, onAction }) {\n switch (accessory.type) {\n case 'image':\n return (\n <img src={accessory.image_url} alt={accessory.alt_text || ''} className=\"w-12 h-12 rounded object-cover\" />\n );\n\n case 'button':\n return (\n <button\n type=\"button\"\n onClick={() => onAction?.(accessory.action_id || '', accessory.value)}\n className={`px-3 py-1.5 text-sm font-medium rounded ${getButtonStyleClass(accessory.style)}`}\n >\n {renderText(accessory.text)}\n </button>\n );\n\n case 'overflow':\n return (\n <button type=\"button\" className=\"p-1 text-secondary hover:text-primary transition-colors\">\n <svg className=\"w-5 h-5\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path d=\"M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z\" />\n </svg>\n </button>\n );\n\n default:\n return null;\n }\n});\n\n// ============================================================================\n// Action Renderer\n// ============================================================================\n\ninterface ActionRendererProps {\n element: BlockKitElement;\n onAction?: (actionId: string, value?: string, data?: Record<string, unknown>) => void;\n}\n\nconst ActionRenderer: React.FC<ActionRendererProps> = memo(function ActionRenderer({ element, onAction }) {\n const handleClick = useCallback(() => {\n if (element.action_id) {\n onAction?.(element.action_id, element.value);\n }\n if (element.url) {\n window.open(element.url, '_blank');\n }\n }, [element, onAction]);\n\n switch (element.type) {\n case 'button':\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n className={`px-4 py-2 text-sm font-medium rounded ${getButtonStyleClass(element.style)}`}\n >\n {renderText(element.text)}\n </button>\n );\n\n case 'static_select':\n return (\n <select\n className=\"px-3 py-2 text-sm border border-default rounded bg-surface text-primary transition-colors focus:ring-2 focus:ring-primary/20\"\n onChange={(e) => onAction?.(element.action_id || '', e.target.value)}\n >\n {element.placeholder ? (\n <option value=\"\">{renderText(element.placeholder as BlockKitText) as ReactNode}</option>\n ) : null}\n {(element.options as Array<{ text: BlockKitText; value: string }>)?.map((option, i) => (\n <option key={i} value={option.value}>\n {renderText(option.text)}\n </option>\n ))}\n </select>\n );\n\n case 'datepicker':\n return (\n <input\n type=\"date\"\n className=\"px-3 py-2 text-sm border border-default rounded bg-surface text-primary transition-colors focus:ring-2 focus:ring-primary/20\"\n onChange={(e) => onAction?.(element.action_id || '', e.target.value)}\n />\n );\n\n case 'checkboxes':\n return (\n <div className=\"space-y-1\">\n {(element.options as Array<{ text: BlockKitText; value: string }>)?.map((option, i) => (\n <label key={i} className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n className=\"rounded border-default transition-colors\"\n onChange={(e) => onAction?.(element.action_id || '', option.value, { checked: e.target.checked })}\n />\n {renderText(option.text)}\n </label>\n ))}\n </div>\n );\n\n case 'radio_buttons':\n return (\n <div className=\"space-y-1\">\n {(element.options as Array<{ text: BlockKitText; value: string }>)?.map((option, i) => (\n <label key={i} className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"radio\"\n name={element.action_id}\n className=\"border-default transition-colors\"\n onChange={() => onAction?.(element.action_id || '', option.value)}\n />\n {renderText(option.text)}\n </label>\n ))}\n </div>\n );\n\n default:\n return null;\n }\n});\n\n// ============================================================================\n// Auto-generate BlockKit blocks from arbitrary data\n// ============================================================================\n\nfunction generateBlocksFromData(data: Record<string, unknown>): BlockKitElement[] {\n const blocks: BlockKitElement[] = [];\n\n for (const [key, value] of Object.entries(data)) {\n if (value === null || value === undefined) continue;\n\n // Handle arrays\n if (Array.isArray(value)) {\n // Add header for the array\n blocks.push({\n type: 'header',\n text: {\n type: 'plain_text',\n text: formatKey(key),\n emoji: true,\n },\n });\n\n // Add each item\n value.forEach((item, index) => {\n if (typeof item === 'object' && item !== null) {\n // Object items - render as section with fields\n const fields: BlockKitText[] = [];\n for (const [itemKey, itemValue] of Object.entries(item)) {\n if (itemValue !== null && itemValue !== undefined) {\n fields.push({\n type: 'mrkdwn',\n text: `*${formatKey(itemKey)}:*\\n${formatValue(itemValue)}`,\n });\n }\n }\n if (fields.length > 0) {\n blocks.push({\n type: 'section',\n fields: fields.slice(0, 10), // BlockKit max 10 fields\n });\n }\n } else {\n // Primitive items - render as simple section\n blocks.push({\n type: 'section',\n text: {\n type: 'mrkdwn',\n text: `${index + 1}. ${formatValue(item)}`,\n },\n });\n }\n\n // Add divider between items (except last)\n if (index < value.length - 1) {\n blocks.push({ type: 'divider' });\n }\n });\n }\n // Handle objects\n else if (typeof value === 'object') {\n blocks.push({\n type: 'header',\n text: {\n type: 'plain_text',\n text: formatKey(key),\n emoji: true,\n },\n });\n\n const fields: BlockKitText[] = [];\n for (const [objKey, objValue] of Object.entries(value as Record<string, unknown>)) {\n if (objValue !== null && objValue !== undefined) {\n fields.push({\n type: 'mrkdwn',\n text: `*${formatKey(objKey)}:*\\n${formatValue(objValue)}`,\n });\n }\n }\n if (fields.length > 0) {\n blocks.push({\n type: 'section',\n fields: fields.slice(0, 10),\n });\n }\n }\n // Handle primitives\n else {\n blocks.push({\n type: 'section',\n text: {\n type: 'mrkdwn',\n text: `*${formatKey(key)}:* ${formatValue(value)}`,\n },\n });\n }\n }\n\n return blocks;\n}\n\nfunction formatKey(key: string): string {\n // Convert camelCase or snake_case to Title Case\n return key\n .replace(/_/g, ' ')\n .replace(/([A-Z])/g, ' $1')\n .replace(/^./, (str) => str.toUpperCase())\n .trim();\n}\n\nfunction formatValue(value: unknown): string {\n if (value === null || value === undefined) return '-';\n if (typeof value === 'boolean') return value ? 'Yes' : 'No';\n if (typeof value === 'object') return JSON.stringify(value);\n return String(value);\n}\n\n// ============================================================================\n// Main Renderer Component\n// ============================================================================\n\nexport const BlockKitRenderer: React.FC<RendererProps> = memo(function BlockKitRenderer({\n widget,\n data,\n template,\n isLoading,\n error,\n onDrilldown: _onDrilldown,\n onAction,\n}) {\n // Debug logging\n\n // Parse and bind template\n const boundTemplate = useMemo(() => {\n // 1. If template has blocks, bind data and use it\n if (template && 'blocks' in (template as Record<string, unknown>)) {\n return bindDataToTemplate(template as unknown as BlockKitTemplate, data);\n }\n\n // 2. If data has blocks, use it directly as BlockKit format\n if (data && 'blocks' in data) {\n return data as unknown as BlockKitTemplate;\n }\n\n // 3. If we have any data, auto-generate blocks from it\n if (data && Object.keys(data).length > 0) {\n const generatedBlocks = generateBlocksFromData(data);\n if (generatedBlocks.length > 0) {\n return { blocks: generatedBlocks } as BlockKitTemplate;\n }\n }\n\n // 4. If template exists but no blocks, try to bind and check again\n if (template) {\n const bound = bindDataToTemplate(template as unknown as BlockKitTemplate, data);\n if (bound?.blocks?.length) {\n return bound;\n }\n }\n\n return null;\n }, [template, data]);\n\n // Handle action clicks\n const handleAction = useCallback(\n (actionId: string, value?: string, actionData?: Record<string, unknown>) => {\n if (onAction) {\n onAction(actionId, { value, ...actionData });\n }\n },\n [onAction]\n );\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 animate-in fade-in duration-200\">\n <div className=\"animate-pulse space-y-2 w-full\">\n <div className=\"h-4 bg-bg-sunken rounded w-3/4\" />\n <div className=\"h-4 bg-bg-sunken rounded w-1/2\" />\n <div className=\"h-4 bg-bg-sunken rounded w-5/6\" />\n </div>\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 text-red-600 dark:text-red-400\">\n <div className=\"text-center\">\n <p className=\"font-medium\">Error rendering blocks</p>\n <p className=\"text-sm mt-1\">{error}</p>\n </div>\n </div>\n );\n }\n\n // Empty state\n if (!boundTemplate?.blocks?.length) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 text-secondary animate-in fade-in duration-200\">\n <div className=\"text-center\">\n <p>No content to display</p>\n <p className=\"text-sm mt-1\">Configure blocks or provide data</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full p-4 bg-surface\">\n {/* Render blocks */}\n {boundTemplate.blocks.map((block, i) => (\n <BlockRenderer key={i} block={block} index={i} onAction={handleAction} />\n ))}\n\n {/* Render attachments */}\n {boundTemplate.attachments?.map((attachment, i) => (\n <div\n key={`attachment-${i}`}\n className=\"mt-4 border-l-4 pl-3\"\n style={{ borderLeftColor: attachment.color || 'var(--color-border)' }}\n >\n {attachment.blocks?.map((block, j) => (\n <BlockRenderer key={j} block={block} index={j} onAction={handleAction} />\n ))}\n {attachment.fallback && !attachment.blocks && <p className=\"text-sm text-secondary\">{attachment.fallback}</p>}\n </div>\n ))}\n </div>\n );\n});\n\nexport default BlockKitRenderer;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BlockKitRenderer.js","names":[],"sources":["../../../../src/bigconsole/renderers/adapters/BlockKitRenderer.tsx"],"sourcesContent":["/**\n * Slack Block Kit Renderer (v2.0)\n *\n * Renders widgets using Slack Block Kit format.\n * Supports common block types: section, divider, header, context, image, actions.\n */\n\nimport React, { memo, useCallback, useMemo, type ReactNode } from 'react';\nimport type { RendererProps, BlockKitTemplate, BlockKitElement, BlockKitText } from '../types/renderer';\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Render Block Kit text (plain_text or mrkdwn)\n * Uses React elements instead of dangerouslySetInnerHTML for better security\n */\nfunction renderText(text: BlockKitText | string | undefined): React.ReactNode {\n if (!text) return null;\n\n if (typeof text === 'string') {\n return text;\n }\n\n if (text.type === 'mrkdwn') {\n // Parse mrkdwn and convert to React elements\n const content = text.text || '';\n\n // Parse the markdown into React elements\n const elements: React.ReactNode[] = [];\n let remaining = content;\n let key = 0;\n\n // Process text segment by segment\n while (remaining.length > 0) {\n // Try to match bold: *text*\n const boldMatch = remaining.match(/^\\*([^*]+)\\*/);\n if (boldMatch) {\n elements.push(<strong key={key++}>{boldMatch[1]}</strong>);\n remaining = remaining.slice(boldMatch[0].length);\n continue;\n }\n\n // Try to match italic: _text_\n const italicMatch = remaining.match(/^_([^_]+)_/);\n if (italicMatch) {\n elements.push(<em key={key++}>{italicMatch[1]}</em>);\n remaining = remaining.slice(italicMatch[0].length);\n continue;\n }\n\n // Try to match strike: ~text~\n const strikeMatch = remaining.match(/^~([^~]+)~/);\n if (strikeMatch) {\n elements.push(<del key={key++}>{strikeMatch[1]}</del>);\n remaining = remaining.slice(strikeMatch[0].length);\n continue;\n }\n\n // Try to match code: `text`\n const codeMatch = remaining.match(/^`([^`]+)`/);\n if (codeMatch) {\n elements.push(\n <code key={key++} className=\"bg-bg-sunken px-1 rounded text-sm\">\n {codeMatch[1]}\n </code>\n );\n remaining = remaining.slice(codeMatch[0].length);\n continue;\n }\n\n // Try to match link with text: <url|text>\n const linkTextMatch = remaining.match(/^<([^|>]+)\\|([^>]+)>/);\n if (linkTextMatch) {\n elements.push(\n <a\n key={key++}\n href={linkTextMatch[1]}\n className=\"text-blue-600 dark:text-blue-400 underline\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {linkTextMatch[2]}\n </a>\n );\n remaining = remaining.slice(linkTextMatch[0].length);\n continue;\n }\n\n // Try to match plain link: <url>\n const linkMatch = remaining.match(/^<([^|>]+)>/);\n if (linkMatch) {\n elements.push(\n <a\n key={key++}\n href={linkMatch[1]}\n className=\"text-blue-600 dark:text-blue-400 underline\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {linkMatch[1]}\n </a>\n );\n remaining = remaining.slice(linkMatch[0].length);\n continue;\n }\n\n // No special formatting found at start - extract plain text until next special char\n const plainMatch = remaining.match(/^[^*_~`<]+/);\n if (plainMatch) {\n elements.push(<React.Fragment key={key++}>{plainMatch[0]}</React.Fragment>);\n remaining = remaining.slice(plainMatch[0].length);\n continue;\n }\n\n // Single special char that didn't match a pattern - output as-is\n elements.push(<React.Fragment key={key++}>{remaining[0]}</React.Fragment>);\n remaining = remaining.slice(1);\n }\n\n return <span>{elements}</span>;\n }\n\n // Plain text with optional emoji\n return text.text;\n}\n\n/**\n * Get button style class\n */\nfunction getButtonStyleClass(style?: string): string {\n switch (style) {\n case 'primary':\n return 'bg-status-success-bg hover:bg-status-success-bg/90 text-white transition-colors';\n case 'danger':\n return 'bg-status-error-bg hover:bg-status-error-bg/90 text-white transition-colors';\n default:\n return 'bg-bg-sunken hover:bg-bg-sunken/80 text-primary transition-colors';\n }\n}\n\n// ============================================================================\n// Template Binding\n// ============================================================================\n\nfunction bindDataToTemplate<T>(template: T, data: Record<string, unknown>): T {\n if (template === null || template === undefined) {\n return template;\n }\n\n if (typeof template === 'string') {\n // Replace ${path} with actual values\n return template.replace(/\\$\\{([^}]+)\\}/g, (_, path) => {\n const value = getNestedValue(data, path.trim());\n return value !== undefined ? String(value) : '';\n }) as T;\n }\n\n if (Array.isArray(template)) {\n return template.map((item) => bindDataToTemplate(item, data)) as T;\n }\n\n if (typeof template === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(template)) {\n result[key] = bindDataToTemplate(value, data);\n }\n return result as T;\n }\n\n return template;\n}\n\nfunction getNestedValue(obj: Record<string, unknown>, path: string): unknown {\n const parts = path.split('.');\n let current: unknown = obj;\n\n for (const part of parts) {\n if (current && typeof current === 'object' && part in (current as Record<string, unknown>)) {\n current = (current as Record<string, unknown>)[part];\n } else {\n return undefined;\n }\n }\n\n return current;\n}\n\n// ============================================================================\n// Block Renderers\n// ============================================================================\n\ninterface BlockRendererProps {\n block: BlockKitElement;\n index: number;\n onAction?: (actionId: string, value?: string, data?: Record<string, unknown>) => void;\n}\n\nconst BlockRenderer: React.FC<BlockRendererProps> = memo(function BlockRenderer({ block, index: _index, onAction }) {\n switch (block.type) {\n // Header block\n case 'header':\n return (\n <div className=\"py-2\">\n <h2 className=\"text-lg font-bold text-primary\">{renderText(block.text)}</h2>\n </div>\n );\n\n // Section block\n case 'section':\n return (\n <div className=\"py-2 flex items-start gap-4\">\n <div className=\"flex-1\">\n {block.text && <p className=\"text-primary\">{renderText(block.text)}</p>}\n {block.fields && block.fields.length > 0 && (\n <div className=\"grid grid-cols-2 gap-2 mt-2\">\n {block.fields.map((field, i) => (\n <div key={i} className=\"text-sm\">\n {renderText(field)}\n </div>\n ))}\n </div>\n )}\n </div>\n {block.accessory && (\n <div className=\"flex-shrink-0\">\n <AccessoryRenderer accessory={block.accessory as BlockKitElement} onAction={onAction} />\n </div>\n )}\n </div>\n );\n\n // Divider block\n case 'divider':\n return <hr className=\"my-2 border-default\" />;\n\n // Image block\n case 'image':\n return (\n <div className=\"py-2\">\n {block.title && <p className=\"text-sm text-secondary mb-1\">{renderText(block.title)}</p>}\n <img src={block.image_url} alt={block.alt_text || ''} className=\"max-w-full h-auto rounded\" />\n </div>\n );\n\n // Context block\n case 'context':\n return (\n <div className=\"py-1 flex items-center gap-2 flex-wrap text-sm text-secondary\">\n {block.elements?.map((element, i) => (\n <span key={i}>\n {element.type === 'image' ? (\n <img src={element.image_url} alt={element.alt_text || ''} className=\"w-5 h-5 rounded inline-block\" />\n ) : (\n renderText(element.text as BlockKitText)\n )}\n </span>\n ))}\n </div>\n );\n\n // Actions block\n case 'actions':\n return (\n <div className=\"py-2 flex gap-2 flex-wrap\">\n {block.elements?.map((element, i) => (\n <ActionRenderer key={i} element={element} onAction={onAction} />\n ))}\n </div>\n );\n\n // Input block (simplified)\n case 'input':\n return (\n <div className=\"py-2\">\n {block.label ? (\n <label className=\"block text-sm font-medium text-secondary mb-1\">\n {renderText(block.label as BlockKitText) as ReactNode}\n </label>\n ) : null}\n <input\n type=\"text\"\n className=\"w-full px-3 py-2 border border-default rounded-md bg-surface text-primary transition-colors focus:ring-2 focus:ring-primary/20\"\n placeholder={(() => {\n const element = block.element as Record<string, unknown> | undefined;\n return element?.placeholder ? (renderText(element.placeholder as BlockKitText) as string) : '';\n })()}\n />\n </div>\n );\n\n // Rich text block (simplified)\n case 'rich_text':\n return (\n <div className=\"py-2 text-primary\">\n {block.elements?.map((element, i) => (\n <span key={i}>{renderText(element.text as BlockKitText)}</span>\n ))}\n </div>\n );\n\n default:\n return null;\n }\n});\n\n// ============================================================================\n// Accessory Renderer\n// ============================================================================\n\ninterface AccessoryRendererProps {\n accessory: BlockKitElement;\n onAction?: (actionId: string, value?: string, data?: Record<string, unknown>) => void;\n}\n\nconst AccessoryRenderer: React.FC<AccessoryRendererProps> = memo(function AccessoryRenderer({ accessory, onAction }) {\n switch (accessory.type) {\n case 'image':\n return (\n <img src={accessory.image_url} alt={accessory.alt_text || ''} className=\"w-12 h-12 rounded object-cover\" />\n );\n\n case 'button':\n return (\n <button\n type=\"button\"\n onClick={() => onAction?.(accessory.action_id || '', accessory.value)}\n className={`px-3 py-1.5 text-sm font-medium rounded ${getButtonStyleClass(accessory.style)}`}\n >\n {renderText(accessory.text)}\n </button>\n );\n\n case 'overflow':\n return (\n <button type=\"button\" className=\"p-1 text-secondary hover:text-primary transition-colors\">\n <svg className=\"w-5 h-5\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path d=\"M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z\" />\n </svg>\n </button>\n );\n\n default:\n return null;\n }\n});\n\n// ============================================================================\n// Action Renderer\n// ============================================================================\n\ninterface ActionRendererProps {\n element: BlockKitElement;\n onAction?: (actionId: string, value?: string, data?: Record<string, unknown>) => void;\n}\n\nconst ActionRenderer: React.FC<ActionRendererProps> = memo(function ActionRenderer({ element, onAction }) {\n const handleClick = useCallback(() => {\n if (element.action_id) {\n onAction?.(element.action_id, element.value);\n }\n if (element.url) {\n window.open(element.url, '_blank');\n }\n }, [element, onAction]);\n\n switch (element.type) {\n case 'button':\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n className={`px-4 py-2 text-sm font-medium rounded ${getButtonStyleClass(element.style)}`}\n >\n {renderText(element.text)}\n </button>\n );\n\n case 'static_select':\n return (\n <select\n className=\"px-3 py-2 text-sm border border-default rounded bg-surface text-primary transition-colors focus:ring-2 focus:ring-primary/20\"\n onChange={(e) => onAction?.(element.action_id || '', e.target.value)}\n >\n {element.placeholder ? (\n <option value=\"\">{renderText(element.placeholder as BlockKitText) as ReactNode}</option>\n ) : null}\n {(element.options as Array<{ text: BlockKitText; value: string }>)?.map((option, i) => (\n <option key={i} value={option.value}>\n {renderText(option.text)}\n </option>\n ))}\n </select>\n );\n\n case 'datepicker':\n return (\n <input\n type=\"date\"\n className=\"px-3 py-2 text-sm border border-default rounded bg-surface text-primary transition-colors focus:ring-2 focus:ring-primary/20\"\n onChange={(e) => onAction?.(element.action_id || '', e.target.value)}\n />\n );\n\n case 'checkboxes':\n return (\n <div className=\"space-y-1\">\n {(element.options as Array<{ text: BlockKitText; value: string }>)?.map((option, i) => (\n <label key={i} className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n className=\"rounded border-default transition-colors\"\n onChange={(e) => onAction?.(element.action_id || '', option.value, { checked: e.target.checked })}\n />\n {renderText(option.text)}\n </label>\n ))}\n </div>\n );\n\n case 'radio_buttons':\n return (\n <div className=\"space-y-1\">\n {(element.options as Array<{ text: BlockKitText; value: string }>)?.map((option, i) => (\n <label key={i} className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"radio\"\n name={element.action_id}\n className=\"border-default transition-colors\"\n onChange={() => onAction?.(element.action_id || '', option.value)}\n />\n {renderText(option.text)}\n </label>\n ))}\n </div>\n );\n\n default:\n return null;\n }\n});\n\n// ============================================================================\n// Auto-generate BlockKit blocks from arbitrary data\n// ============================================================================\n\nfunction generateBlocksFromData(data: Record<string, unknown>): BlockKitElement[] {\n const blocks: BlockKitElement[] = [];\n\n for (const [key, value] of Object.entries(data)) {\n if (value === null || value === undefined) continue;\n\n // Handle arrays\n if (Array.isArray(value)) {\n // Add header for the array\n blocks.push({\n type: 'header',\n text: {\n type: 'plain_text',\n text: formatKey(key),\n emoji: true,\n },\n });\n\n // Add each item\n value.forEach((item, index) => {\n if (typeof item === 'object' && item !== null) {\n // Object items - render as section with fields\n const fields: BlockKitText[] = [];\n for (const [itemKey, itemValue] of Object.entries(item)) {\n if (itemValue !== null && itemValue !== undefined) {\n fields.push({\n type: 'mrkdwn',\n text: `*${formatKey(itemKey)}:*\\n${formatValue(itemValue)}`,\n });\n }\n }\n if (fields.length > 0) {\n blocks.push({\n type: 'section',\n fields: fields.slice(0, 10), // BlockKit max 10 fields\n });\n }\n } else {\n // Primitive items - render as simple section\n blocks.push({\n type: 'section',\n text: {\n type: 'mrkdwn',\n text: `${index + 1}. ${formatValue(item)}`,\n },\n });\n }\n\n // Add divider between items (except last)\n if (index < value.length - 1) {\n blocks.push({ type: 'divider' });\n }\n });\n }\n // Handle objects\n else if (typeof value === 'object') {\n blocks.push({\n type: 'header',\n text: {\n type: 'plain_text',\n text: formatKey(key),\n emoji: true,\n },\n });\n\n const fields: BlockKitText[] = [];\n for (const [objKey, objValue] of Object.entries(value as Record<string, unknown>)) {\n if (objValue !== null && objValue !== undefined) {\n fields.push({\n type: 'mrkdwn',\n text: `*${formatKey(objKey)}:*\\n${formatValue(objValue)}`,\n });\n }\n }\n if (fields.length > 0) {\n blocks.push({\n type: 'section',\n fields: fields.slice(0, 10),\n });\n }\n }\n // Handle primitives\n else {\n blocks.push({\n type: 'section',\n text: {\n type: 'mrkdwn',\n text: `*${formatKey(key)}:* ${formatValue(value)}`,\n },\n });\n }\n }\n\n return blocks;\n}\n\nfunction formatKey(key: string): string {\n // Convert camelCase or snake_case to Title Case\n return key\n .replace(/_/g, ' ')\n .replace(/([A-Z])/g, ' $1')\n .replace(/^./, (str) => str.toUpperCase())\n .trim();\n}\n\nfunction formatValue(value: unknown): string {\n if (value === null || value === undefined) return '-';\n if (typeof value === 'boolean') return value ? 'Yes' : 'No';\n if (typeof value === 'object') return JSON.stringify(value);\n return String(value);\n}\n\n// ============================================================================\n// Main Renderer Component\n// ============================================================================\n\nexport const BlockKitRenderer: React.FC<RendererProps> = memo(function BlockKitRenderer({\n widget,\n data,\n template,\n isLoading,\n error,\n onDrilldown: _onDrilldown,\n onAction,\n}) {\n // Debug logging\n\n // Parse and bind template\n const boundTemplate = useMemo(() => {\n // 1. If template has blocks, bind data and use it\n if (template && 'blocks' in (template as Record<string, unknown>)) {\n return bindDataToTemplate(template as unknown as BlockKitTemplate, data);\n }\n\n // 2. If data has blocks, use it directly as BlockKit format\n if (data && 'blocks' in data) {\n return data as unknown as BlockKitTemplate;\n }\n\n // 3. If we have any data, auto-generate blocks from it\n if (data && Object.keys(data).length > 0) {\n const generatedBlocks = generateBlocksFromData(data);\n if (generatedBlocks.length > 0) {\n return { blocks: generatedBlocks } as BlockKitTemplate;\n }\n }\n\n // 4. If template exists but no blocks, try to bind and check again\n if (template) {\n const bound = bindDataToTemplate(template as unknown as BlockKitTemplate, data);\n if (bound?.blocks?.length) {\n return bound;\n }\n }\n\n return null;\n }, [template, data]);\n\n // Handle action clicks\n const handleAction = useCallback(\n (actionId: string, value?: string, actionData?: Record<string, unknown>) => {\n if (onAction) {\n onAction(actionId, { value, ...actionData });\n }\n },\n [onAction]\n );\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 animate-in fade-in duration-200\">\n <div className=\"animate-pulse space-y-2 w-full\">\n <div className=\"h-4 bg-bg-sunken rounded w-3/4\" />\n <div className=\"h-4 bg-bg-sunken rounded w-1/2\" />\n <div className=\"h-4 bg-bg-sunken rounded w-5/6\" />\n </div>\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 text-red-600 dark:text-red-400\">\n <div className=\"text-center\">\n <p className=\"font-medium\">Error rendering blocks</p>\n <p className=\"text-sm mt-1\">{error}</p>\n </div>\n </div>\n );\n }\n\n // Empty state\n if (!boundTemplate?.blocks?.length) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 text-secondary animate-in fade-in duration-200\">\n <div className=\"text-center\">\n <p>No content to display</p>\n <p className=\"text-sm mt-1\">Configure blocks or provide data</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full p-4 bg-surface\">\n {/* Render blocks */}\n {boundTemplate.blocks.map((block, i) => (\n <BlockRenderer key={i} block={block} index={i} onAction={handleAction} />\n ))}\n\n {/* Render attachments */}\n {boundTemplate.attachments?.map((attachment, i) => (\n <div\n key={`attachment-${i}`}\n className=\"mt-4 border-l-4 pl-3\"\n style={{ borderLeftColor: attachment.color || 'var(--color-border)' }}\n >\n {attachment.blocks?.map((block, j) => (\n <BlockRenderer key={j} block={block} index={j} onAction={handleAction} />\n ))}\n {attachment.fallback && !attachment.blocks && <p className=\"text-sm text-secondary\">{attachment.fallback}</p>}\n </div>\n ))}\n </div>\n );\n});\n\nexport default BlockKitRenderer;\n"],"mappings":";;;AAkBA,SAAS,EAAW,GAA0D;AAC5E,KAAI,CAAC,EAAM,QAAO;AAElB,KAAI,OAAO,KAAS,SAClB,QAAO;AAGT,KAAI,EAAK,SAAS,UAAU;EAE1B,IAAM,IAAU,EAAK,QAAQ,IAGvB,IAA8B,EAAE,EAClC,IAAY,GACZ,IAAM;AAGV,SAAO,EAAU,SAAS,IAAG;GAE3B,IAAM,IAAY,EAAU,MAAM,eAAe;AACjD,OAAI,GAAW;AAEb,IADA,EAAS,KAAK,kBAAC,UAAD,EAAA,UAAqB,EAAU,IAAY,EAA9B,IAA8B,CAAC,EAC1D,IAAY,EAAU,MAAM,EAAU,GAAG,OAAO;AAChD;;GAIF,IAAM,IAAc,EAAU,MAAM,aAAa;AACjD,OAAI,GAAa;AAEf,IADA,EAAS,KAAK,kBAAC,MAAD,EAAA,UAAiB,EAAY,IAAQ,EAA5B,IAA4B,CAAC,EACpD,IAAY,EAAU,MAAM,EAAY,GAAG,OAAO;AAClD;;GAIF,IAAM,IAAc,EAAU,MAAM,aAAa;AACjD,OAAI,GAAa;AAEf,IADA,EAAS,KAAK,kBAAC,OAAD,EAAA,UAAkB,EAAY,IAAS,EAA7B,IAA6B,CAAC,EACtD,IAAY,EAAU,MAAM,EAAY,GAAG,OAAO;AAClD;;GAIF,IAAM,IAAY,EAAU,MAAM,aAAa;AAC/C,OAAI,GAAW;AAMb,IALA,EAAS,KACP,kBAAC,QAAD;KAAkB,WAAU;eACzB,EAAU;KACN,EAFI,IAEJ,CACR,EACD,IAAY,EAAU,MAAM,EAAU,GAAG,OAAO;AAChD;;GAIF,IAAM,IAAgB,EAAU,MAAM,uBAAuB;AAC7D,OAAI,GAAe;AAYjB,IAXA,EAAS,KACP,kBAAC,KAAD;KAEE,MAAM,EAAc;KACpB,WAAU;KACV,QAAO;KACP,KAAI;eAEH,EAAc;KACb,EAPG,IAOH,CACL,EACD,IAAY,EAAU,MAAM,EAAc,GAAG,OAAO;AACpD;;GAIF,IAAM,IAAY,EAAU,MAAM,cAAc;AAChD,OAAI,GAAW;AAYb,IAXA,EAAS,KACP,kBAAC,KAAD;KAEE,MAAM,EAAU;KAChB,WAAU;KACV,QAAO;KACP,KAAI;eAEH,EAAU;KACT,EAPG,IAOH,CACL,EACD,IAAY,EAAU,MAAM,EAAU,GAAG,OAAO;AAChD;;GAIF,IAAM,IAAa,EAAU,MAAM,aAAa;AAChD,OAAI,GAAY;AAEd,IADA,EAAS,KAAK,kBAAC,EAAM,UAAP,EAAA,UAA6B,EAAW,IAAoB,EAAvC,IAAuC,CAAC,EAC3E,IAAY,EAAU,MAAM,EAAW,GAAG,OAAO;AACjD;;AAKF,GADA,EAAS,KAAK,kBAAC,EAAM,UAAP,EAAA,UAA6B,EAAU,IAAoB,EAAtC,IAAsC,CAAC,EAC1E,IAAY,EAAU,MAAM,EAAE;;AAGhC,SAAO,kBAAC,QAAD,EAAA,UAAO,GAAgB,CAAA;;AAIhC,QAAO,EAAK;;AAMd,SAAS,EAAoB,GAAwB;AACnD,SAAQ,GAAR;EACE,KAAK,UACH,QAAO;EACT,KAAK,SACH,QAAO;EACT,QACE,QAAO;;;AAQb,SAAS,EAAsB,GAAa,GAAkC;AAC5E,KAAI,KAAa,KACf,QAAO;AAGT,KAAI,OAAO,KAAa,SAEtB,QAAO,EAAS,QAAQ,mBAAmB,GAAG,MAAS;EACrD,IAAM,IAAQ,EAAe,GAAM,EAAK,MAAM,CAAC;AAC/C,SAAO,MAAU,KAAA,IAA4B,KAAhB,OAAO,EAAM;GAC1C;AAGJ,KAAI,MAAM,QAAQ,EAAS,CACzB,QAAO,EAAS,KAAK,MAAS,EAAmB,GAAM,EAAK,CAAC;AAG/D,KAAI,OAAO,KAAa,UAAU;EAChC,IAAM,IAAkC,EAAE;AAC1C,OAAK,IAAM,CAAC,GAAK,MAAU,OAAO,QAAQ,EAAS,CACjD,GAAO,KAAO,EAAmB,GAAO,EAAK;AAE/C,SAAO;;AAGT,QAAO;;AAGT,SAAS,EAAe,GAA8B,GAAuB;CAC3E,IAAM,IAAQ,EAAK,MAAM,IAAI,EACzB,IAAmB;AAEvB,MAAK,IAAM,KAAQ,EACjB,KAAI,KAAW,OAAO,KAAY,YAAY,KAAS,EACrD,KAAW,EAAoC;KAE/C;AAIJ,QAAO;;AAaT,IAAM,IAA8C,EAAK,SAAuB,EAAE,UAAO,OAAO,GAAQ,eAAY;AAClH,SAAQ,EAAM,MAAd;EAEE,KAAK,SACH,QACE,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,MAAD;IAAI,WAAU;cAAkC,EAAW,EAAM,KAAK;IAAM,CAAA;GACxE,CAAA;EAIV,KAAK,UACH,QACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,EAAM,QAAQ,kBAAC,KAAD;KAAG,WAAU;eAAgB,EAAW,EAAM,KAAK;KAAK,CAAA,EACtE,EAAM,UAAU,EAAM,OAAO,SAAS,KACrC,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAM,OAAO,KAAK,GAAO,MACxB,kBAAC,OAAD;MAAa,WAAU;gBACpB,EAAW,EAAM;MACd,EAFI,EAEJ,CACN;KACE,CAAA,CAEJ;OACL,EAAM,aACL,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KAAmB,WAAW,EAAM;KAAwC;KAAY,CAAA;IACpF,CAAA,CAEJ;;EAIV,KAAK,UACH,QAAO,kBAAC,MAAD,EAAI,WAAU,uBAAwB,CAAA;EAG/C,KAAK,QACH,QACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,EAAM,SAAS,kBAAC,KAAD;IAAG,WAAU;cAA+B,EAAW,EAAM,MAAM;IAAK,CAAA,EACxF,kBAAC,OAAD;IAAK,KAAK,EAAM;IAAW,KAAK,EAAM,YAAY;IAAI,WAAU;IAA8B,CAAA,CAC1F;;EAIV,KAAK,UACH,QACE,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAM,UAAU,KAAK,GAAS,MAC7B,kBAAC,QAAD,EAAA,UACG,EAAQ,SAAS,UAChB,kBAAC,OAAD;IAAK,KAAK,EAAQ;IAAW,KAAK,EAAQ,YAAY;IAAI,WAAU;IAAiC,CAAA,GAErG,EAAW,EAAQ,KAAqB,EAErC,EANI,EAMJ,CACP;GACE,CAAA;EAIV,KAAK,UACH,QACE,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAM,UAAU,KAAK,GAAS,MAC7B,kBAAC,GAAD;IAAiC;IAAmB;IAAY,EAA3C,EAA2C,CAChE;GACE,CAAA;EAIV,KAAK,QACH,QACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,EAAM,QACL,kBAAC,SAAD;IAAO,WAAU;cACd,EAAW,EAAM,MAAsB;IAClC,CAAA,GACN,MACJ,kBAAC,SAAD;IACE,MAAK;IACL,WAAU;IACV,oBAAoB;KAClB,IAAM,IAAU,EAAM;AACtB,YAAO,GAAS,cAAe,EAAW,EAAQ,YAA4B,GAAc;QAC1F;IACJ,CAAA,CACE;;EAIV,KAAK,YACH,QACE,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAM,UAAU,KAAK,GAAS,MAC7B,kBAAC,QAAD,EAAA,UAAe,EAAW,EAAQ,KAAqB,EAAQ,EAApD,EAAoD,CAC/D;GACE,CAAA;EAGV,QACE,QAAO;;EAEX,EAWI,IAAsD,EAAK,SAA2B,EAAE,cAAW,eAAY;AACnH,SAAQ,EAAU,MAAlB;EACE,KAAK,QACH,QACE,kBAAC,OAAD;GAAK,KAAK,EAAU;GAAW,KAAK,EAAU,YAAY;GAAI,WAAU;GAAmC,CAAA;EAG/G,KAAK,SACH,QACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,IAAW,EAAU,aAAa,IAAI,EAAU,MAAM;GACrE,WAAW,2CAA2C,EAAoB,EAAU,MAAM;aAEzF,EAAW,EAAU,KAAK;GACpB,CAAA;EAGb,KAAK,WACH,QACE,kBAAC,UAAD;GAAQ,MAAK;GAAS,WAAU;aAC9B,kBAAC,OAAD;IAAK,WAAU;IAAU,MAAK;IAAe,SAAQ;cACnD,kBAAC,QAAD,EAAM,GAAE,gGAAiG,CAAA;IACrG,CAAA;GACC,CAAA;EAGb,QACE,QAAO;;EAEX,EAWI,IAAgD,EAAK,SAAwB,EAAE,YAAS,eAAY;CACxG,IAAM,IAAc,QAAkB;AAIpC,EAHI,EAAQ,aACV,IAAW,EAAQ,WAAW,EAAQ,MAAM,EAE1C,EAAQ,OACV,OAAO,KAAK,EAAQ,KAAK,SAAS;IAEnC,CAAC,GAAS,EAAS,CAAC;AAEvB,SAAQ,EAAQ,MAAhB;EACE,KAAK,SACH,QACE,kBAAC,UAAD;GACE,MAAK;GACL,SAAS;GACT,WAAW,yCAAyC,EAAoB,EAAQ,MAAM;aAErF,EAAW,EAAQ,KAAK;GAClB,CAAA;EAGb,KAAK,gBACH,QACE,kBAAC,UAAD;GACE,WAAU;GACV,WAAW,MAAM,IAAW,EAAQ,aAAa,IAAI,EAAE,OAAO,MAAM;aAFtE,CAIG,EAAQ,cACP,kBAAC,UAAD;IAAQ,OAAM;cAAI,EAAW,EAAQ,YAA4B;IAAuB,CAAA,GACtF,MACF,EAAQ,SAA0D,KAAK,GAAQ,MAC/E,kBAAC,UAAD;IAAgB,OAAO,EAAO;cAC3B,EAAW,EAAO,KAAK;IACjB,EAFI,EAEJ,CACT,CACK;;EAGb,KAAK,aACH,QACE,kBAAC,SAAD;GACE,MAAK;GACL,WAAU;GACV,WAAW,MAAM,IAAW,EAAQ,aAAa,IAAI,EAAE,OAAO,MAAM;GACpE,CAAA;EAGN,KAAK,aACH,QACE,kBAAC,OAAD;GAAK,WAAU;aACX,EAAQ,SAA0D,KAAK,GAAQ,MAC/E,kBAAC,SAAD;IAAe,WAAU;cAAzB,CACE,kBAAC,SAAD;KACE,MAAK;KACL,WAAU;KACV,WAAW,MAAM,IAAW,EAAQ,aAAa,IAAI,EAAO,OAAO,EAAE,SAAS,EAAE,OAAO,SAAS,CAAC;KACjG,CAAA,EACD,EAAW,EAAO,KAAK,CAClB;MAPI,EAOJ,CACR;GACE,CAAA;EAGV,KAAK,gBACH,QACE,kBAAC,OAAD;GAAK,WAAU;aACX,EAAQ,SAA0D,KAAK,GAAQ,MAC/E,kBAAC,SAAD;IAAe,WAAU;cAAzB,CACE,kBAAC,SAAD;KACE,MAAK;KACL,MAAM,EAAQ;KACd,WAAU;KACV,gBAAgB,IAAW,EAAQ,aAAa,IAAI,EAAO,MAAM;KACjE,CAAA,EACD,EAAW,EAAO,KAAK,CAClB;MARI,EAQJ,CACR;GACE,CAAA;EAGV,QACE,QAAO;;EAEX;AAMF,SAAS,EAAuB,GAAkD;CAChF,IAAM,IAA4B,EAAE;AAEpC,MAAK,IAAM,CAAC,GAAK,MAAU,OAAO,QAAQ,EAAK,CACzC,UAAU,KAGd,KAAI,MAAM,QAAQ,EAAM,CAYtB,CAVA,EAAO,KAAK;EACV,MAAM;EACN,MAAM;GACJ,MAAM;GACN,MAAM,EAAU,EAAI;GACpB,OAAO;GACR;EACF,CAAC,EAGF,EAAM,SAAS,GAAM,MAAU;AAC7B,MAAI,OAAO,KAAS,YAAY,GAAe;GAE7C,IAAM,IAAyB,EAAE;AACjC,QAAK,IAAM,CAAC,GAAS,MAAc,OAAO,QAAQ,EAAK,CACrD,CAAI,KAAc,QAChB,EAAO,KAAK;IACV,MAAM;IACN,MAAM,IAAI,EAAU,EAAQ,CAAC,MAAM,EAAY,EAAU;IAC1D,CAAC;AAGN,GAAI,EAAO,SAAS,KAClB,EAAO,KAAK;IACV,MAAM;IACN,QAAQ,EAAO,MAAM,GAAG,GAAG;IAC5B,CAAC;QAIJ,GAAO,KAAK;GACV,MAAM;GACN,MAAM;IACJ,MAAM;IACN,MAAM,GAAG,IAAQ,EAAE,IAAI,EAAY,EAAK;IACzC;GACF,CAAC;AAIJ,EAAI,IAAQ,EAAM,SAAS,KACzB,EAAO,KAAK,EAAE,MAAM,WAAW,CAAC;GAElC;UAGK,OAAO,KAAU,UAAU;AAClC,IAAO,KAAK;GACV,MAAM;GACN,MAAM;IACJ,MAAM;IACN,MAAM,EAAU,EAAI;IACpB,OAAO;IACR;GACF,CAAC;EAEF,IAAM,IAAyB,EAAE;AACjC,OAAK,IAAM,CAAC,GAAQ,MAAa,OAAO,QAAQ,EAAiC,CAC/E,CAAI,KAAa,QACf,EAAO,KAAK;GACV,MAAM;GACN,MAAM,IAAI,EAAU,EAAO,CAAC,MAAM,EAAY,EAAS;GACxD,CAAC;AAGN,EAAI,EAAO,SAAS,KAClB,EAAO,KAAK;GACV,MAAM;GACN,QAAQ,EAAO,MAAM,GAAG,GAAG;GAC5B,CAAC;OAKJ,GAAO,KAAK;EACV,MAAM;EACN,MAAM;GACJ,MAAM;GACN,MAAM,IAAI,EAAU,EAAI,CAAC,KAAK,EAAY,EAAM;GACjD;EACF,CAAC;AAIN,QAAO;;AAGT,SAAS,EAAU,GAAqB;AAEtC,QAAO,EACJ,QAAQ,MAAM,IAAI,CAClB,QAAQ,YAAY,MAAM,CAC1B,QAAQ,OAAO,MAAQ,EAAI,aAAa,CAAC,CACzC,MAAM;;AAGX,SAAS,EAAY,GAAwB;AAI3C,QAHI,KAAU,OAAoC,MAC9C,OAAO,KAAU,YAAkB,IAAQ,QAAQ,OACnD,OAAO,KAAU,WAAiB,KAAK,UAAU,EAAM,GACpD,OAAO,EAAM;;AAOtB,IAAa,IAA4C,EAAK,SAA0B,EACtF,WACA,SACA,aACA,cACA,UACA,aAAa,GACb,eACC;CAID,IAAM,IAAgB,QAAc;AAElC,MAAI,KAAY,YAAa,EAC3B,QAAO,EAAmB,GAAyC,EAAK;AAI1E,MAAI,KAAQ,YAAY,EACtB,QAAO;AAIT,MAAI,KAAQ,OAAO,KAAK,EAAK,CAAC,SAAS,GAAG;GACxC,IAAM,IAAkB,EAAuB,EAAK;AACpD,OAAI,EAAgB,SAAS,EAC3B,QAAO,EAAE,QAAQ,GAAiB;;AAKtC,MAAI,GAAU;GACZ,IAAM,IAAQ,EAAmB,GAAyC,EAAK;AAC/E,OAAI,GAAO,QAAQ,OACjB,QAAO;;AAIX,SAAO;IACN,CAAC,GAAU,EAAK,CAAC,EAGd,IAAe,GAClB,GAAkB,GAAgB,MAAyC;AAC1E,EAAI,KACF,EAAS,GAAU;GAAE;GAAO,GAAG;GAAY,CAAC;IAGhD,CAAC,EAAS,CACX;AAuCD,QApCI,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD,EAAK,WAAU,kCAAmC,CAAA;IAClD,kBAAC,OAAD,EAAK,WAAU,kCAAmC,CAAA;IAClD,kBAAC,OAAD,EAAK,WAAU,kCAAmC,CAAA;IAC9C;;EACF,CAAA,GAKN,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,KAAD;IAAG,WAAU;cAAc;IAA0B,CAAA,EACrD,kBAAC,KAAD;IAAG,WAAU;cAAgB;IAAU,CAAA,CACnC;;EACF,CAAA,GAKL,GAAe,QAAQ,SAY1B,kBAAC,OAAD;EAAK,WAAU;YAAf,CAEG,EAAc,OAAO,KAAK,GAAO,MAChC,kBAAC,GAAD;GAA8B;GAAO,OAAO;GAAG,UAAU;GAAgB,EAArD,EAAqD,CACzE,EAGD,EAAc,aAAa,KAAK,GAAY,MAC3C,kBAAC,OAAD;GAEE,WAAU;GACV,OAAO,EAAE,iBAAiB,EAAW,SAAS,uBAAuB;aAHvE,CAKG,EAAW,QAAQ,KAAK,GAAO,MAC9B,kBAAC,GAAD;IAA8B;IAAO,OAAO;IAAG,UAAU;IAAgB,EAArD,EAAqD,CACzE,EACD,EAAW,YAAY,CAAC,EAAW,UAAU,kBAAC,KAAD;IAAG,WAAU;cAA0B,EAAW;IAAa,CAAA,CACzG;KARC,cAAc,IAQf,CACN,CACE;MA7BJ,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,KAAD,EAAA,UAAG,yBAAyB,CAAA,EAC5B,kBAAC,KAAD;IAAG,WAAU;cAAe;IAAoC,CAAA,CAC5D;;EACF,CAAA;EA0BV"}
|