@seed-ship/mcp-ui-solid 6.21.0 → 6.23.0

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.
Files changed (93) hide show
  1. package/CHANGELOG.md +173 -0
  2. package/README.md +102 -0
  3. package/dist/components/ChartJSRenderer.cjs +244 -68
  4. package/dist/components/ChartJSRenderer.cjs.map +1 -1
  5. package/dist/components/ChartJSRenderer.d.ts +9 -0
  6. package/dist/components/ChartJSRenderer.d.ts.map +1 -1
  7. package/dist/components/ChartJSRenderer.js +246 -70
  8. package/dist/components/ChartJSRenderer.js.map +1 -1
  9. package/dist/components/ExpandableWrapper.cjs +9 -1
  10. package/dist/components/ExpandableWrapper.cjs.map +1 -1
  11. package/dist/components/ExpandableWrapper.d.ts.map +1 -1
  12. package/dist/components/ExpandableWrapper.js +9 -1
  13. package/dist/components/ExpandableWrapper.js.map +1 -1
  14. package/dist/components/LightboxOverlay.cjs +139 -16
  15. package/dist/components/LightboxOverlay.cjs.map +1 -1
  16. package/dist/components/LightboxOverlay.d.ts +6 -0
  17. package/dist/components/LightboxOverlay.d.ts.map +1 -1
  18. package/dist/components/LightboxOverlay.js +141 -18
  19. package/dist/components/LightboxOverlay.js.map +1 -1
  20. package/dist/components/UIResourceRenderer.cjs +15 -5
  21. package/dist/components/UIResourceRenderer.cjs.map +1 -1
  22. package/dist/components/UIResourceRenderer.d.ts +2 -1
  23. package/dist/components/UIResourceRenderer.d.ts.map +1 -1
  24. package/dist/components/UIResourceRenderer.js +15 -5
  25. package/dist/components/UIResourceRenderer.js.map +1 -1
  26. package/dist/context/MCPUIConfigContext.cjs +2 -1
  27. package/dist/context/MCPUIConfigContext.cjs.map +1 -1
  28. package/dist/context/MCPUIConfigContext.d.ts +90 -12
  29. package/dist/context/MCPUIConfigContext.d.ts.map +1 -1
  30. package/dist/context/MCPUIConfigContext.js +2 -1
  31. package/dist/context/MCPUIConfigContext.js.map +1 -1
  32. package/dist/context/MCPUIMathContext.cjs +19 -0
  33. package/dist/context/MCPUIMathContext.cjs.map +1 -0
  34. package/dist/context/MCPUIMathContext.d.ts +18 -0
  35. package/dist/context/MCPUIMathContext.d.ts.map +1 -0
  36. package/dist/context/MCPUIMathContext.js +19 -0
  37. package/dist/context/MCPUIMathContext.js.map +1 -0
  38. package/dist/context/MCPUIStringsContext.cjs +6 -1
  39. package/dist/context/MCPUIStringsContext.cjs.map +1 -1
  40. package/dist/context/MCPUIStringsContext.d.ts +22 -0
  41. package/dist/context/MCPUIStringsContext.d.ts.map +1 -1
  42. package/dist/context/MCPUIStringsContext.js +6 -1
  43. package/dist/context/MCPUIStringsContext.js.map +1 -1
  44. package/dist/hooks/index.d.ts +2 -0
  45. package/dist/hooks/index.d.ts.map +1 -1
  46. package/dist/hooks.cjs +2 -0
  47. package/dist/hooks.cjs.map +1 -1
  48. package/dist/hooks.js +2 -0
  49. package/dist/hooks.js.map +1 -1
  50. package/dist/index.cjs +8 -0
  51. package/dist/index.cjs.map +1 -1
  52. package/dist/index.d.cts +5 -1
  53. package/dist/index.d.ts +5 -1
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +9 -1
  56. package/dist/index.js.map +1 -1
  57. package/dist/plugins/katex.cjs +23 -0
  58. package/dist/plugins/katex.cjs.map +1 -0
  59. package/dist/plugins/katex.d.cts +7 -0
  60. package/dist/plugins/katex.d.ts +7 -0
  61. package/dist/plugins/katex.d.ts.map +1 -0
  62. package/dist/plugins/katex.js +23 -0
  63. package/dist/plugins/katex.js.map +1 -0
  64. package/dist/utils/iframe-coep.cjs.map +1 -1
  65. package/dist/utils/iframe-coep.d.ts +8 -0
  66. package/dist/utils/iframe-coep.d.ts.map +1 -1
  67. package/dist/utils/iframe-coep.js.map +1 -1
  68. package/dist/utils/math-markdown.cjs +240 -0
  69. package/dist/utils/math-markdown.cjs.map +1 -0
  70. package/dist/utils/math-markdown.d.ts +16 -0
  71. package/dist/utils/math-markdown.d.ts.map +1 -0
  72. package/dist/utils/math-markdown.js +240 -0
  73. package/dist/utils/math-markdown.js.map +1 -0
  74. package/dist/utils/pan-zoom.cjs +312 -0
  75. package/dist/utils/pan-zoom.cjs.map +1 -0
  76. package/dist/utils/pan-zoom.d.ts +235 -0
  77. package/dist/utils/pan-zoom.d.ts.map +1 -0
  78. package/dist/utils/pan-zoom.js +312 -0
  79. package/dist/utils/pan-zoom.js.map +1 -0
  80. package/package.json +26 -3
  81. package/src/components/ChartJSRenderer.tsx +396 -1
  82. package/src/components/ExpandableWrapper.tsx +26 -0
  83. package/src/components/LightboxOverlay.tsx +280 -4
  84. package/src/components/UIResourceRenderer.tsx +24 -3
  85. package/src/context/MCPUIConfigContext.tsx +92 -12
  86. package/src/context/MCPUIMathContext.tsx +32 -0
  87. package/src/context/MCPUIStringsContext.tsx +30 -0
  88. package/src/hooks/index.ts +13 -0
  89. package/src/index.ts +20 -0
  90. package/src/plugins/katex.ts +25 -0
  91. package/src/utils/iframe-coep.ts +8 -0
  92. package/src/utils/math-markdown.ts +222 -0
  93. package/src/utils/pan-zoom.ts +779 -0
@@ -1 +1 @@
1
- {"version":3,"file":"ChartJSRenderer.js","sources":["../../src/components/ChartJSRenderer.tsx"],"sourcesContent":["/**\n * ChartJSRenderer - Native Chart.js rendering\n * Sprint 4: State & Charts\n *\n * Requires chart.js peer dependency:\n * ```\n * pnpm add chart.js\n * ```\n */\n\nimport {\n Component,\n For,\n createEffect,\n createMemo,\n createUniqueId,\n onCleanup,\n createSignal,\n Show,\n} from 'solid-js';\nimport type { Accessor, JSX } from 'solid-js';\nimport type { UIComponent, ChartComponentParams } from '../types';\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper';\nimport { DegradedFallback } from './DegradedFallback';\nimport { chartToDataTable } from './chart-data-table';\nimport { useTelemetry } from '../context/MCPUITelemetryContext';\nimport { formatMCPUIString, useMCPUIStrings } from '../context/MCPUIStringsContext';\n\n// Lazy load Chart.js to avoid bundling if not used\nlet ChartJS: any = null;\nlet chartJSLoadPromise: Promise<any> | null = null;\n\nconst loadChartJS = async () => {\n if (ChartJS) return ChartJS;\n\n if (!chartJSLoadPromise) {\n chartJSLoadPromise = import('chart.js/auto')\n .then((module) => {\n ChartJS = module.default || module.Chart;\n return ChartJS;\n })\n .catch((err) => {\n chartJSLoadPromise = null;\n throw err;\n });\n }\n\n return chartJSLoadPromise;\n};\n\n/**\n * Check if Chart.js is available\n */\nexport async function isChartJSAvailable(): Promise<boolean> {\n try {\n await loadChartJS();\n return true;\n } catch {\n return false;\n }\n}\n\nexport interface ChartJSRendererProps {\n /**\n * UIComponent with chart params\n */\n component: UIComponent;\n\n /**\n * Error callback\n */\n onError?: (error: Error) => void;\n\n /**\n * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).\n * @see ExpandableWrapperProps.toolbarVariant\n */\n toolbarVariant?: 'hover' | 'always-visible';\n}\n\nconst ContextAwareChartLayout: Component<{\n render: (isExpanded: Accessor<boolean>) => JSX.Element;\n}> = (props) => props.render(useExpanded());\n\n/**\n * Native Chart.js renderer component\n *\n * @example\n * ```tsx\n * const chartComponent: UIComponent = {\n * id: 'revenue-chart',\n * type: 'chart',\n * position: { colStart: 1, colSpan: 6 },\n * params: {\n * type: 'bar',\n * title: 'Monthly Revenue',\n * data: {\n * labels: ['Jan', 'Feb', 'Mar'],\n * datasets: [{ label: 'Revenue', data: [100, 200, 150] }]\n * },\n * renderer: 'native',\n * },\n * }\n * <ChartJSRenderer component={chartComponent} />\n * ```\n */\nexport const ChartJSRenderer: Component<ChartJSRendererProps> = (props) => {\n const [isLoading, setIsLoading] = createSignal(true);\n const [error, setError] = createSignal<string>();\n const [activeView, setActiveView] = createSignal<'chart' | 'data'>('chart');\n let canvasRef: HTMLCanvasElement | undefined;\n let chartInstance: any;\n let renderVersion = 0;\n\n const params = () => props.component.params as ChartComponentParams;\n const telemetry = useTelemetry();\n const strings = useMCPUIStrings();\n // The accessible data table is a pure projection: its column headers come\n // from the chrome strings, injected rather than hardcoded in the helper.\n const tableData = createMemo(() =>\n chartToDataTable(params(), {\n series: strings.chartTableSeries,\n point: strings.chartTablePoint,\n label: strings.degradedColLabel,\n seriesName: strings.degradedSeries,\n })\n );\n const descriptionId = createUniqueId();\n const title = () => params().title || strings.chartView;\n\n // v6.1.0 — export visibility :\n // - undefined / true → button shown (new default, was opt-in)\n // - false → button hidden (explicit opt-out, unchanged)\n const exportEnabled = () => params().exportable !== false;\n\n // v6.1.0 — copy data for the ExpandableWrapper modal-header copy button.\n // Lazy-stringified each time the button is clicked.\n const copyDataJSON = () => JSON.stringify({ type: params().type, data: params().data }, null, 2);\n\n // Chart PNG export\n const handleExportPNG = () => {\n if (!canvasRef) return;\n const url = canvasRef.toDataURL('image/png');\n const a = document.createElement('a');\n a.href = url;\n a.download = `${(params().title || 'chart').replace(/\\s+/g, '-').toLowerCase()}.png`;\n a.click();\n };\n\n // Create/update chart when params change\n createEffect(async () => {\n if (!canvasRef) return;\n\n // Access params to track dependencies\n const chartParams = params();\n const version = ++renderVersion;\n\n setIsLoading(true);\n setError(undefined);\n\n try {\n const Chart = await loadChartJS();\n\n // A newer prop update or an unmount superseded this async render.\n if (version !== renderVersion || !canvasRef) return;\n\n // Destroy previous instance\n if (chartInstance) {\n chartInstance.destroy();\n chartInstance = null;\n }\n\n // Build options, merging time-axis config if present (v3.1.0)\n const baseOptions: any = {\n responsive: true,\n maintainAspectRatio: false,\n ...chartParams.options,\n plugins: {\n ...chartParams.options?.plugins,\n legend: {\n display: true,\n position: 'bottom',\n ...chartParams.options?.plugins?.legend,\n },\n },\n };\n\n // Time-series axis (v3.1.0)\n if (chartParams.timeAxis) {\n const ta = chartParams.timeAxis;\n baseOptions.scales = {\n ...baseOptions.scales,\n x: {\n ...baseOptions.scales?.x,\n type: 'time',\n time: {\n parser: ta.parser,\n unit: ta.unit,\n tooltipFormat: ta.tooltipFormat,\n },\n ...(ta.min ? { min: ta.min } : {}),\n ...(ta.max ? { max: ta.max } : {}),\n },\n };\n }\n\n // Create new chart\n chartInstance = new Chart(canvasRef, {\n type: chartParams.type,\n data: chartParams.data,\n options: baseOptions,\n });\n\n setIsLoading(false);\n } catch (err) {\n if (version !== renderVersion) return;\n const error = err instanceof Error ? err : new Error(strings.chartRenderError);\n setError(error.message);\n setIsLoading(false);\n // Fallback ladder (P2.5): record the failure so it's observable, then\n // degrade to the series table below instead of a blank canvas.\n telemetry?.dispatch({\n type: 'render:error',\n errorMessage: error.message,\n id: props.component?.id ?? '',\n componentType: 'chart',\n ts: Date.now(),\n });\n props.onError?.(error);\n }\n });\n\n // Chart.js may measure a zero-sized canvas while the data view is visible.\n // Resize once Solid has revealed it again.\n createEffect(() => {\n if (activeView() !== 'chart' || !chartInstance) return;\n queueMicrotask(() => chartInstance?.resize?.());\n });\n\n // Cleanup on unmount\n onCleanup(() => {\n renderVersion += 1;\n if (chartInstance) {\n chartInstance.destroy();\n chartInstance = null;\n }\n });\n\n return (\n <ExpandableWrapper\n title={title()}\n copyData={copyDataJSON()}\n copyLabel={strings.chartCopy}\n toolbarVariant={props.toolbarVariant}\n >\n <ContextAwareChartLayout\n render={(isExpanded) => (\n <div\n class={`relative w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden p-4 group ${\n isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''\n } ${params().className || ''}`}\n >\n <div class=\"flex items-center justify-between gap-3 mb-3 flex-shrink-0\">\n <div class=\"min-w-0\">\n <Show when={params().title}>\n <h3 class=\"text-sm font-semibold text-gray-900 dark:text-white\">{params().title}</h3>\n </Show>\n </div>\n <div class=\"flex items-center gap-2\">\n <div\n class=\"inline-flex rounded-md border border-gray-200 p-0.5 dark:border-gray-600\"\n role=\"group\"\n aria-label={strings.chartViewSelector}\n >\n <button\n type=\"button\"\n aria-pressed={activeView() === 'chart'}\n class={`rounded px-2 py-1 text-xs font-medium transition-colors ${\n activeView() === 'chart'\n ? 'bg-gray-900 text-white dark:bg-gray-100 dark:text-gray-900'\n : 'text-gray-600 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700'\n }`}\n onClick={() => setActiveView('chart')}\n >\n {strings.chartView}\n </button>\n <button\n type=\"button\"\n aria-pressed={activeView() === 'data'}\n class={`rounded px-2 py-1 text-xs font-medium transition-colors ${\n activeView() === 'data'\n ? 'bg-gray-900 text-white dark:bg-gray-100 dark:text-gray-900'\n : 'text-gray-600 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700'\n }`}\n onClick={() => setActiveView('data')}\n >\n {strings.chartDataView}\n </button>\n </div>\n <Show when={exportEnabled()}>\n <button\n type=\"button\"\n onClick={handleExportPNG}\n disabled={activeView() !== 'chart' || isLoading() || Boolean(error())}\n class=\"opacity-0 group-hover:opacity-60 hover:!opacity-100 disabled:!opacity-30 disabled:cursor-not-allowed px-2 py-1 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm\"\n title={strings.chartDownloadPng}\n aria-label={strings.chartDownloadPngAria}\n >\n <svg\n class=\"w-3 h-3 text-gray-500 dark:text-gray-400\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"\n />\n </svg>\n </button>\n </Show>\n </div>\n </div>\n\n <p id={descriptionId} class=\"sr-only\">\n {title()}. {strings.chartDataSummary}\n </p>\n\n {/* Fallback ladder (P2.5): degrade to a series table on render error\n instead of a bare \"Chart Error\" message. */}\n <Show when={error() && activeView() === 'chart'}>\n <DegradedFallback\n message={formatMCPUIString(strings.chartRenderFailed, { error: String(error()) })}\n caption={strings.chartDegradedCaption}\n {...tableData()}\n />\n </Show>\n\n <Show when={activeView() === 'data'}>\n <div\n class={`w-full overflow-auto rounded border border-gray-200 dark:border-gray-700 ${\n isExpanded() ? 'flex-1 min-h-0' : ''\n }`}\n style={\n isExpanded()\n ? undefined\n : { height: params().height || '250px', 'max-height': '70vh' }\n }\n >\n <table class=\"w-full border-collapse text-left text-sm\">\n <caption class=\"sr-only\">\n {title()} — {strings.chartDataTable}\n </caption>\n <thead class=\"bg-gray-50 dark:bg-gray-900/40\">\n <tr>\n <For each={tableData().columns}>\n {(column) => (\n <th scope=\"col\" class=\"px-3 py-2 font-medium text-gray-900 dark:text-gray-100\">\n {column}\n </th>\n )}\n </For>\n </tr>\n </thead>\n <tbody>\n <For each={tableData().rows}>\n {(row) => (\n <tr class=\"border-t border-gray-100 dark:border-gray-700\">\n <For each={tableData().columns}>\n {(_column, index) => (\n <td class=\"px-3 py-2 text-gray-700 dark:text-gray-300\">\n {String(row[index()] ?? '')}\n </td>\n )}\n </For>\n </tr>\n )}\n </For>\n <Show when={tableData().rows.length === 0}>\n <tr>\n <td\n colSpan={Math.max(1, tableData().columns.length)}\n class=\"px-3 py-6 text-center text-gray-500 dark:text-gray-400\"\n >\n {strings.chartNoData}\n </td>\n </tr>\n </Show>\n </tbody>\n </table>\n </div>\n </Show>\n\n <div\n class={`relative w-full ${isExpanded() ? 'flex-1 min-h-0' : ''}`}\n style={\n error() || activeView() === 'data'\n ? { display: 'none' }\n : isExpanded()\n ? { height: '100%', display: 'block' }\n : { height: params().height || '250px', display: 'block' }\n }\n >\n <canvas\n ref={canvasRef}\n role=\"img\"\n aria-label={title()}\n aria-describedby={descriptionId}\n aria-hidden={activeView() !== 'chart' || Boolean(error())}\n />\n <Show when={isLoading() && activeView() === 'chart'}>\n <div class=\"absolute inset-0 flex items-center justify-center bg-white/80 dark:bg-gray-800/80\">\n <div class=\"flex flex-col items-center gap-2\">\n <div class=\"animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600\" />\n <span class=\"text-sm text-gray-500 dark:text-gray-400\">{strings.chartLoading}</span>\n </div>\n </div>\n </Show>\n </div>\n </div>\n )}\n />\n </ExpandableWrapper>\n );\n};\n"],"names":["ChartJS","chartJSLoadPromise","loadChartJS","then","module","default","Chart","catch","err","isChartJSAvailable","ContextAwareChartLayout","props","render","useExpanded","ChartJSRenderer","isLoading","setIsLoading","createSignal","error","setError","activeView","setActiveView","canvasRef","chartInstance","renderVersion","params","component","telemetry","useTelemetry","strings","useMCPUIStrings","tableData","createMemo","chartToDataTable","series","chartTableSeries","point","chartTablePoint","label","degradedColLabel","seriesName","degradedSeries","descriptionId","createUniqueId","title","chartView","exportEnabled","exportable","copyDataJSON","JSON","stringify","type","data","handleExportPNG","url","toDataURL","a","document","createElement","href","download","replace","toLowerCase","click","createEffect","chartParams","version","undefined","destroy","baseOptions","responsive","maintainAspectRatio","options","plugins","legend","display","position","timeAxis","ta","scales","x","time","parser","unit","tooltipFormat","min","max","Error","chartRenderError","message","dispatch","errorMessage","id","componentType","ts","Date","now","onError","queueMicrotask","resize","onCleanup","_$createComponent","ExpandableWrapper","copyData","copyLabel","chartCopy","toolbarVariant","children","isExpanded","_el$","_$getNextElement","_tmpl$6","_el$2","firstChild","_el$3","_el$5","nextSibling","_el$6","_el$7","_el$8","_el$0","_el$1","_co$","_$getNextMarker","_el$10","_el$12","_el$13","_co$2","_el$11","_el$14","_el$15","_co$3","_el$41","_el$42","_co$9","_el$43","_el$44","_co$0","_el$33","_el$34","_el$39","_el$40","_co$8","_$insert","Show","when","_el$4","_tmpl$","$$click","chartDataView","_el$9","_tmpl$2","_$effect","_p$","_v$","Boolean","_v$2","chartDownloadPng","_v$3","chartDownloadPngAria","e","_$setProperty","t","_$setAttribute","_$runHydrationEvents","chartDataSummary","_$memo","DegradedFallback","_$mergeProps","formatMCPUIString","chartRenderFailed","String","caption","chartDegradedCaption","_el$16","_tmpl$4","_el$17","_el$18","_el$20","_el$21","_co$4","_el$19","_el$22","_el$23","_co$5","_el$24","_el$25","_el$26","_el$29","_el$30","_co$6","_el$31","_el$32","_co$7","chartDataTable","For","each","columns","column","_el$45","_tmpl$7","rows","row","_el$46","_tmpl$8","_column","index","_el$47","_tmpl$9","length","_el$27","_tmpl$3","_el$28","chartNoData","Math","_v$4","_v$5","height","_$className","_$style","_ref$","_$use","_el$35","_tmpl$5","_el$36","_el$37","_el$38","chartLoading","_v$6","className","_v$7","chartViewSelector","_v$8","_v$9","_v$0","_v$1","_v$10","_v$11","_v$12","_v$13","o","i","n","s","h","r","d","_$delegateEvents"],"mappings":"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"}
1
+ {"version":3,"file":"ChartJSRenderer.js","sources":["../../src/components/ChartJSRenderer.tsx"],"sourcesContent":["/**\n * ChartJSRenderer - Native Chart.js rendering\n * Sprint 4: State & Charts\n *\n * Requires chart.js peer dependency:\n * ```\n * pnpm add chart.js\n * ```\n *\n * Wheel-zoom / drag-pan / pinch (v6.22.0) need a SECOND optional peer, since a\n * canvas cannot be zoomed with a CSS transform without blurring:\n * ```\n * pnpm add chartjs-plugin-zoom\n * ```\n * Without it the chart renders exactly as it did before 6.22.0. Where zoom is\n * offered is the host's call — see `MCPUIConfig.chartZoom`, default\n * `'expanded'` (fullscreen modal only).\n */\n\nimport {\n Component,\n For,\n createEffect,\n createMemo,\n createUniqueId,\n onCleanup,\n createSignal,\n untrack,\n Show,\n} from 'solid-js';\nimport type { Accessor, JSX } from 'solid-js';\nimport type { UIComponent, ChartComponentParams } from '../types';\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper';\nimport { DegradedFallback } from './DegradedFallback';\nimport { chartToDataTable } from './chart-data-table';\nimport { useTelemetry } from '../context/MCPUITelemetryContext';\nimport { useMCPUIConfig } from '../context/MCPUIConfigContext';\nimport { formatMCPUIString, useMCPUIStrings } from '../context/MCPUIStringsContext';\n\n// Lazy load Chart.js to avoid bundling if not used\nlet ChartJS: any = null;\nlet chartJSLoadPromise: Promise<any> | null = null;\n\nconst loadChartJS = async () => {\n if (ChartJS) return ChartJS;\n\n if (!chartJSLoadPromise) {\n chartJSLoadPromise = import('chart.js/auto')\n .then((module) => {\n ChartJS = module.default || module.Chart;\n return ChartJS;\n })\n .catch((err) => {\n chartJSLoadPromise = null;\n throw err;\n });\n }\n\n return chartJSLoadPromise;\n};\n\n/**\n * Check if Chart.js is available\n */\nexport async function isChartJSAvailable(): Promise<boolean> {\n try {\n await loadChartJS();\n return true;\n } catch {\n return false;\n }\n}\n\n// ─── Optional zoom peer (v6.22.0) ─────────────────────────────────────────\n//\n// A chart is a CANVAS, so the CSS-transform approach used by `createPanZoom`\n// for DOM surfaces would just magnify a bitmap into a blurry one. Real chart\n// zoom means re-projecting the scales, which only Chart.js itself can do —\n// hence `chartjs-plugin-zoom`, wired here as an OPTIONAL peer dependency\n// alongside `chart.js`, `leaflet` and `@antv/g6`. A host that does not install\n// it gets exactly the chart this renderer built before 6.22.0.\n\nlet zoomPluginPromise: Promise<unknown | null> | null = null;\nlet zoomPluginRegistered = false;\n\n/**\n * Import `chartjs-plugin-zoom` once, lazily.\n *\n * Resolves to `null` instead of rejecting when the peer is absent: an optional\n * dependency that is simply not installed is not an error condition, and the\n * caller must be able to fall through to the unzoomable chart without an error\n * state or console noise. The `null` is cached with the promise — a peer that\n * is missing now is still missing on the next chart, and retrying the import\n * per render would re-log the module-resolution failure every time.\n *\n * Only ever called from an effect, never at module scope or during render, so\n * SSR never reaches `hammerjs` (a plugin dependency that reads `document` as\n * it loads).\n *\n * `@vite-ignore` for the same reason `duckdb.ts` and `MapRenderer.tsx` carry\n * it on THEIR optional peers: this library is built with the peer externalised,\n * so the bare specifier survives into the published bundle and a consumer's\n * bundler resolves it statically — an app that never installed the plugin then\n * fails its own dependency scan at build time, long before the `.catch()`\n * below could turn the absence into the supported `null`. The four peers that\n * anyone using the feature at all has installed (`chart.js`, `leaflet`,\n * `@antv/g6`, `highlight.js`) stay bare on purpose: they benefit from the\n * pre-bundling this comment opts out of.\n */\nconst loadZoomPlugin = (): Promise<unknown | null> => {\n if (!zoomPluginPromise) {\n zoomPluginPromise = import(/* @vite-ignore */ 'chartjs-plugin-zoom')\n .then((module: any) => module.default ?? module)\n .catch(() => null);\n }\n\n return zoomPluginPromise;\n};\n\n/**\n * Load the plugin and register it on the Chart.js singleton, once per process.\n *\n * Registration is global and not undoable, which is precisely why it is\n * deferred to the first surface that actually asks for zoom\n * (`MCPUIConfig.chartZoom`): with the default `'expanded'`, a chart sitting\n * inline in a chat feed never even downloads the plugin.\n *\n * Chart.js only gives the plugin's hooks to charts constructed AFTER the\n * `register()` call, so the caller rebuilds its chart once this resolves.\n *\n * Resolves to `null` on EVERY failure and never rejects. Its one call site is\n * a fire-and-forget `.then()` inside an effect, with nowhere to put a rejection\n * handler that would mean anything to the host: `loadZoomPlugin()` already\n * swallows the missing optional peer, but `loadChartJS()` below rejects on a\n * host that installed neither, and that rejection would surface as an\n * unhandled one. The chart's own render effect reports the missing `chart.js`\n * through `onError` and the degraded table; zoom just quietly is not offered.\n */\nconst ensureZoomPlugin = async (): Promise<unknown | null> => {\n try {\n const plugin = await loadZoomPlugin();\n if (!plugin) return null;\n\n if (!zoomPluginRegistered) {\n const Chart: any = await loadChartJS();\n // A stubbed or partial Chart build may not expose `register`; treat that\n // like an absent peer. Returning the plugin here would be worse than\n // returning nothing: the renderer would hang zoom options off a chart\n // that has no zoom hooks, and paint a toolbar whose buttons do nothing.\n if (typeof Chart?.register !== 'function') return null;\n\n Chart.register(plugin);\n // Set only now that a registration has actually happened, so a later\n // call still gets its chance on a Chart build that does expose it.\n zoomPluginRegistered = true;\n }\n\n return plugin;\n } catch {\n return null;\n }\n};\n\n/**\n * Chart types whose axes zoom meaningfully.\n *\n * `pie`, `doughnut`, `polarArea` and `radar` carry no cartesian scales, so the\n * plugin would capture the wheel and move nothing — strictly worse than not\n * offering zoom at all.\n */\nconst ZOOMABLE_CHART_TYPES = new Set(['bar', 'line', 'scatter', 'bubble']);\n\n/**\n * Ratio one press of the chart's zoom buttons applies.\n *\n * The same 1.5 the lightbox uses for `+` / `-`: coarse enough that a couple of\n * presses get somewhere, fine enough to stop on a region of interest.\n */\nconst CHART_ZOOM_BUTTON_FACTOR = 1.5;\n\n/**\n * Whether a chart instance currently sits off its initial viewport.\n *\n * `isZoomedOrPanned()` is the plugin's own API (2.x). The `getZoomLevel()`\n * branch covers an older plugin build that only exposes the zoom factor, and\n * the `false` covers a chart created before the plugin was registered.\n */\nconst isChartZoomed = (chart: any): boolean => {\n if (typeof chart?.isZoomedOrPanned === 'function') return Boolean(chart.isZoomedOrPanned());\n const level = chart?.getZoomLevel?.();\n return typeof level === 'number' ? level !== 1 : false;\n};\n\n/** A JSON-ish object — the only shape a zoom option tree is ever built from. */\nconst isPlainZoomObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\n/**\n * Deep-merge this renderer's zoom block with the consumer's own.\n *\n * `ChartComponentParams.options` is the public Chart.js escape hatch, and\n * `plugins.zoom` is where a host tunes what this renderer cannot guess:\n * `limits`, wheel `speed`, a pan `modifierKey`, its own callbacks. Spreading\n * the renderer's block over it would silently drop the lot, so the two trees\n * are merged branch by branch (`zoom.wheel`, `zoom.pinch`, `zoom.drag`, `pan`,\n * `limits`, …).\n *\n * Two rules, both deliberate:\n * - on a plain value the CALLER wins — an explicit `mode: 'x'` is a more\n * specific instruction than this renderer's default `'xy'`;\n * - on a function both run, renderer first. The only functions the renderer\n * puts in that tree are `onZoom` / `onPan`, and they are what keeps the\n * reset control in sync with the viewport — letting a caller's callback\n * REPLACE them would leave the toolbar lying about the chart, so they are\n * composed instead.\n */\nconst mergeZoomOptions = (\n base: Record<string, unknown>,\n override: unknown\n): Record<string, unknown> => {\n if (!isPlainZoomObject(override)) return base;\n\n const merged: Record<string, unknown> = { ...base };\n\n for (const [key, value] of Object.entries(override)) {\n // An explicitly-undefined key is an OMISSION, not an instruction to erase.\n // A host forwarding an optional prop — `zoom: { onZoom: props.onZoom }`\n // with nothing passed — would otherwise delete the renderer's own hook,\n // which is exactly what keeps the reset control honest. This is also the\n // semantics `mergeProps` gives every other merge in this package.\n if (value === undefined) continue;\n\n const ours = merged[key];\n\n if (isPlainZoomObject(ours) && isPlainZoomObject(value)) {\n merged[key] = mergeZoomOptions(ours, value);\n } else if (typeof ours === 'function' && typeof value === 'function') {\n const rendererHook = ours as (...args: unknown[]) => unknown;\n const callerHook = value as (...args: unknown[]) => unknown;\n merged[key] = (...args: unknown[]) => {\n rendererHook(...args);\n return callerHook(...args);\n };\n } else {\n merged[key] = value;\n }\n }\n\n return merged;\n};\n\nexport interface ChartJSRendererProps {\n /**\n * UIComponent with chart params\n */\n component: UIComponent;\n\n /**\n * Error callback\n */\n onError?: (error: Error) => void;\n\n /**\n * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).\n * @see ExpandableWrapperProps.toolbarVariant\n */\n toolbarVariant?: 'hover' | 'always-visible';\n}\n\nconst ContextAwareChartLayout: Component<{\n render: (isExpanded: Accessor<boolean>) => JSX.Element;\n}> = (props) => props.render(useExpanded());\n\n/**\n * Native Chart.js renderer component\n *\n * @example\n * ```tsx\n * const chartComponent: UIComponent = {\n * id: 'revenue-chart',\n * type: 'chart',\n * position: { colStart: 1, colSpan: 6 },\n * params: {\n * type: 'bar',\n * title: 'Monthly Revenue',\n * data: {\n * labels: ['Jan', 'Feb', 'Mar'],\n * datasets: [{ label: 'Revenue', data: [100, 200, 150] }]\n * },\n * renderer: 'native',\n * },\n * }\n * <ChartJSRenderer component={chartComponent} />\n * ```\n */\nexport const ChartJSRenderer: Component<ChartJSRendererProps> = (props) => {\n const [isLoading, setIsLoading] = createSignal(true);\n const [error, setError] = createSignal<string>();\n const [activeView, setActiveView] = createSignal<'chart' | 'data'>('chart');\n // Reported by `<ExpandableWrapper onExpandedChange>` rather than read from\n // `useExpanded()`: this component IS the wrapper's owner, so its own\n // `useExpanded()` call sits above the wrapper's provider and would only ever\n // see an outer wrapper (see ExpandableWrapperProps.onExpandedChange).\n const [isExpanded, setIsExpanded] = createSignal(false);\n const [zoomPlugin, setZoomPlugin] = createSignal<unknown | null>(null);\n const [isZoomed, setIsZoomed] = createSignal(false);\n let canvasRef: HTMLCanvasElement | undefined;\n let chartInstance: any;\n let renderVersion = 0;\n\n const params = () => props.component.params as ChartComponentParams;\n const telemetry = useTelemetry();\n const config = useMCPUIConfig();\n const strings = useMCPUIStrings();\n // The accessible data table is a pure projection: its column headers come\n // from the chrome strings, injected rather than hardcoded in the helper.\n const tableData = createMemo(() =>\n chartToDataTable(params(), {\n series: strings.chartTableSeries,\n point: strings.chartTablePoint,\n label: strings.degradedColLabel,\n seriesName: strings.degradedSeries,\n })\n );\n const descriptionId = createUniqueId();\n const title = () => params().title || strings.chartView;\n\n // v6.1.0 — export visibility :\n // - undefined / true → button shown (new default, was opt-in)\n // - false → button hidden (explicit opt-out, unchanged)\n const exportEnabled = () => params().exportable !== false;\n\n // v6.1.0 — copy data for the ExpandableWrapper modal-header copy button.\n // Lazy-stringified each time the button is clicked.\n const copyDataJSON = () => JSON.stringify({ type: params().type, data: params().data }, null, 2);\n\n // ── Zoom / pan (v6.22.0) ───────────────────────────────────────────────\n\n /**\n * Whether this chart should be capturing the wheel right now: the host\n * policy (`MCPUIConfig.chartZoom`) crossed with the surface the chart\n * currently lives on, and with the chart type.\n *\n * `'expanded'` (the default) only says yes inside `ExpandableWrapper`'s\n * fullscreen modal — the one surface that owns its viewport. Inline, the\n * wheel belongs to the page the chart is scrolling in.\n */\n const zoomWanted = createMemo(() => {\n if (!ZOOMABLE_CHART_TYPES.has(params().type)) return false;\n // `false` (or `null`) on the host's own `plugins.zoom` is Chart.js's\n // documented per-chart plugin opt-out. It speaks about THIS chart, so it\n // beats the host-wide `chartZoom` policy — and gating here rather than at\n // the options merge keeps one source of truth, so the plugin is never even\n // downloaded and the toolbar never offers controls that would do nothing.\n const hostZoom = params().options?.plugins?.zoom;\n if (hostZoom === false || hostZoom === null) return false;\n const mode = config.chartZoom;\n if (mode === 'never') return false;\n if (mode === 'always') return true;\n return isExpanded();\n });\n\n // Download + register the plugin the first time a surface asks for zoom.\n // `zoomPlugin` is read untracked: setting it must not re-run this effect.\n createEffect(() => {\n if (!zoomWanted() || untrack(zoomPlugin)) return;\n void ensureZoomPlugin().then((plugin) => {\n if (plugin) setZoomPlugin(() => plugin);\n });\n });\n\n /**\n * The plugin's Chart.js options. Built once per renderer instance so that\n * `zoomOptions()` keeps a STABLE identity: a fresh object each read would\n * look like a config change and rebuild the chart on every update.\n */\n const zoomPluginOptions = {\n zoom: {\n wheel: { enabled: true },\n pinch: { enabled: true },\n // Drag is reserved for panning below; a drag-to-select zoom would make\n // the two gestures fight over the same mouse button.\n drag: { enabled: false },\n mode: 'xy',\n onZoom: ({ chart }: { chart: unknown }) => setIsZoomed(isChartZoomed(chart)),\n },\n pan: {\n enabled: true,\n mode: 'xy',\n onPan: ({ chart }: { chart: unknown }) => setIsZoomed(isChartZoomed(chart)),\n },\n };\n\n /**\n * The `plugins.zoom` block to merge into the chart config, or `null`.\n *\n * `null` while the peer is absent, which keeps the built config byte-for-byte\n * what it was before 6.22.0 — and, because the memo then never changes value,\n * also keeps expanding the modal from rebuilding the chart at all on a host\n * that did not install the plugin.\n */\n const zoomOptions = createMemo(() => (zoomWanted() && zoomPlugin() ? zoomPluginOptions : null));\n\n /**\n * Whether the zoom toolbar belongs on screen at all: the chart is zoomable\n * here (host policy × surface × chart type, and the peer actually loaded),\n * and the chart is what is currently being shown.\n */\n const zoomControlsShown = () =>\n Boolean(zoomOptions()) && activeView() === 'chart' && !error();\n\n /**\n * Zoom the chart by a fixed ratio, about its centre.\n *\n * Every gesture the plugin offers — wheel, drag-pan, pinch — is a POINTER\n * gesture, and the canvas is not focusable, so without these buttons the\n * whole feature is unreachable by keyboard, switch or voice (WCAG 2.1.1) —\n * including the reset control, which only exists once a gesture has already\n * moved the chart. The lightbox settled the same question the same way, with\n * persistent `+` / `-` controls next to its pointer gestures.\n *\n * `chart.zoom()` is `chartjs-plugin-zoom`'s own imperative API; the `?.`\n * covers a chart built before the plugin was registered. The plugin calls\n * `onZoom` itself, but the state is written here too so the reset control\n * appears even on a build whose hook does not fire for a programmatic zoom.\n */\n const handleZoomBy = (factor: number) => {\n chartInstance?.zoom?.(factor);\n setIsZoomed(isChartZoomed(chartInstance));\n };\n\n /** Back to the chart's initial viewport. */\n const handleResetZoom = () => {\n chartInstance?.resetZoom?.();\n setIsZoomed(false);\n };\n\n // Chart PNG export\n const handleExportPNG = () => {\n if (!canvasRef) return;\n const url = canvasRef.toDataURL('image/png');\n const a = document.createElement('a');\n a.href = url;\n a.download = `${(params().title || 'chart').replace(/\\s+/g, '-').toLowerCase()}.png`;\n a.click();\n };\n\n // Create/update chart when params change\n createEffect(async () => {\n if (!canvasRef) return;\n\n // Access params to track dependencies. `zoomOptions()` is read here, before\n // the first `await`, for the same reason: an async effect only tracks what\n // it reads synchronously, and toggling the expanded state has to rebuild\n // the chart rather than leave it on a stale config (Chart.js reads the\n // plugin options at construction and the plugin only hooks charts created\n // after it was registered).\n const chartParams = params();\n const zoom = zoomOptions();\n const version = ++renderVersion;\n\n setIsLoading(true);\n setError(undefined);\n\n try {\n const Chart = await loadChartJS();\n\n // A newer prop update or an unmount superseded this async render.\n if (version !== renderVersion || !canvasRef) return;\n\n // Destroy previous instance\n if (chartInstance) {\n chartInstance.destroy();\n chartInstance = null;\n }\n\n // Build options, merging time-axis config if present (v3.1.0)\n const hostZoomOptions = chartParams.options?.plugins?.zoom;\n const baseOptions: any = {\n responsive: true,\n maintainAspectRatio: false,\n ...chartParams.options,\n plugins: {\n ...chartParams.options?.plugins,\n legend: {\n display: true,\n position: 'bottom',\n ...chartParams.options?.plugins?.legend,\n },\n // Present only once the optional peer has loaded AND the current\n // surface wants zoom. Merged rather than assigned, so a consumer's\n // own `plugins.zoom` survives; when zoom is off, the spread above\n // has already left their block exactly as they wrote it.\n // An explicit `plugins.zoom: false` never reaches here: `zoomWanted`\n // already returned false for it, so `zoom` is null and the spread\n // above carries the host's own value through untouched.\n ...(zoom ? { zoom: mergeZoomOptions(zoom, hostZoomOptions) } : {}),\n },\n };\n\n // Time-series axis (v3.1.0)\n if (chartParams.timeAxis) {\n const ta = chartParams.timeAxis;\n baseOptions.scales = {\n ...baseOptions.scales,\n x: {\n ...baseOptions.scales?.x,\n type: 'time',\n time: {\n parser: ta.parser,\n unit: ta.unit,\n tooltipFormat: ta.tooltipFormat,\n },\n ...(ta.min ? { min: ta.min } : {}),\n ...(ta.max ? { max: ta.max } : {}),\n },\n };\n }\n\n // Create new chart\n chartInstance = new Chart(canvasRef, {\n type: chartParams.type,\n data: chartParams.data,\n options: baseOptions,\n });\n\n // A freshly built chart always sits on its initial viewport, so the\n // reset control goes away until the user zooms again.\n setIsZoomed(false);\n setIsLoading(false);\n } catch (err) {\n if (version !== renderVersion) return;\n const error = err instanceof Error ? err : new Error(strings.chartRenderError);\n setError(error.message);\n setIsLoading(false);\n // Fallback ladder (P2.5): record the failure so it's observable, then\n // degrade to the series table below instead of a blank canvas.\n telemetry?.dispatch({\n type: 'render:error',\n errorMessage: error.message,\n id: props.component?.id ?? '',\n componentType: 'chart',\n ts: Date.now(),\n });\n props.onError?.(error);\n }\n });\n\n // Chart.js may measure a zero-sized canvas while the data view is visible.\n // Resize once Solid has revealed it again.\n createEffect(() => {\n if (activeView() !== 'chart' || !chartInstance) return;\n queueMicrotask(() => chartInstance?.resize?.());\n });\n\n // Cleanup on unmount\n onCleanup(() => {\n renderVersion += 1;\n if (chartInstance) {\n chartInstance.destroy();\n chartInstance = null;\n }\n });\n\n return (\n <ExpandableWrapper\n title={title()}\n copyData={copyDataJSON()}\n copyLabel={strings.chartCopy}\n toolbarVariant={props.toolbarVariant}\n onExpandedChange={setIsExpanded}\n >\n <ContextAwareChartLayout\n render={(isExpanded) => (\n <div\n class={`relative w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden p-4 group ${\n isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''\n } ${params().className || ''}`}\n >\n <div class=\"flex items-center justify-between gap-3 mb-3 flex-shrink-0\">\n <div class=\"min-w-0\">\n <Show when={params().title}>\n <h3 class=\"text-sm font-semibold text-gray-900 dark:text-white\">{params().title}</h3>\n </Show>\n </div>\n <div class=\"flex items-center gap-2\">\n <div\n class=\"inline-flex rounded-md border border-gray-200 p-0.5 dark:border-gray-600\"\n role=\"group\"\n aria-label={strings.chartViewSelector}\n >\n <button\n type=\"button\"\n aria-pressed={activeView() === 'chart'}\n class={`rounded px-2 py-1 text-xs font-medium transition-colors ${\n activeView() === 'chart'\n ? 'bg-gray-900 text-white dark:bg-gray-100 dark:text-gray-900'\n : 'text-gray-600 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700'\n }`}\n onClick={() => setActiveView('chart')}\n >\n {strings.chartView}\n </button>\n <button\n type=\"button\"\n aria-pressed={activeView() === 'data'}\n class={`rounded px-2 py-1 text-xs font-medium transition-colors ${\n activeView() === 'data'\n ? 'bg-gray-900 text-white dark:bg-gray-100 dark:text-gray-900'\n : 'text-gray-600 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700'\n }`}\n onClick={() => setActiveView('data')}\n >\n {strings.chartDataView}\n </button>\n </div>\n {/* Zoom controls — permanently visible (not hover-revealed like\n the export button) for as long as this chart is zoomable at\n all. Zoom in/out come FIRST and unconditionally: they are the\n only keyboard route into a feature whose gestures are all\n pointer gestures, on a canvas that takes no focus. Reset joins\n them only once the chart is actually off its initial viewport,\n so the toolbar stays quiet on a chart nobody moved. */}\n <Show when={zoomControlsShown()}>\n <div class=\"inline-flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => handleZoomBy(1 / CHART_ZOOM_BUTTON_FACTOR)}\n class=\"opacity-60 hover:opacity-100 px-2 py-1 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm\"\n title={strings.zoomOut}\n aria-label={strings.zoomOut}\n >\n <svg\n class=\"w-3 h-3 text-gray-500 dark:text-gray-400\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607zM13.5 10.5h-6\"\n />\n </svg>\n </button>\n\n <button\n type=\"button\"\n onClick={() => handleZoomBy(CHART_ZOOM_BUTTON_FACTOR)}\n class=\"opacity-60 hover:opacity-100 px-2 py-1 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm\"\n title={strings.zoomIn}\n aria-label={strings.zoomIn}\n >\n <svg\n class=\"w-3 h-3 text-gray-500 dark:text-gray-400\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607zM10.5 7.5v6m3-3h-6\"\n />\n </svg>\n </button>\n\n <Show when={isZoomed()}>\n <button\n type=\"button\"\n onClick={handleResetZoom}\n class=\"opacity-60 hover:opacity-100 px-2 py-1 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm\"\n title={strings.zoomReset}\n aria-label={strings.zoomReset}\n >\n <svg\n class=\"w-3 h-3 text-gray-500 dark:text-gray-400\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"\n />\n </svg>\n </button>\n </Show>\n </div>\n </Show>\n <Show when={exportEnabled()}>\n <button\n type=\"button\"\n onClick={handleExportPNG}\n disabled={activeView() !== 'chart' || isLoading() || Boolean(error())}\n class=\"opacity-0 group-hover:opacity-60 hover:!opacity-100 disabled:!opacity-30 disabled:cursor-not-allowed px-2 py-1 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm\"\n title={strings.chartDownloadPng}\n aria-label={strings.chartDownloadPngAria}\n >\n <svg\n class=\"w-3 h-3 text-gray-500 dark:text-gray-400\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"\n />\n </svg>\n </button>\n </Show>\n </div>\n </div>\n\n <p id={descriptionId} class=\"sr-only\">\n {title()}. {strings.chartDataSummary}\n </p>\n\n {/* Fallback ladder (P2.5): degrade to a series table on render error\n instead of a bare \"Chart Error\" message. */}\n <Show when={error() && activeView() === 'chart'}>\n <DegradedFallback\n message={formatMCPUIString(strings.chartRenderFailed, { error: String(error()) })}\n caption={strings.chartDegradedCaption}\n {...tableData()}\n />\n </Show>\n\n <Show when={activeView() === 'data'}>\n <div\n class={`w-full overflow-auto rounded border border-gray-200 dark:border-gray-700 ${\n isExpanded() ? 'flex-1 min-h-0' : ''\n }`}\n style={\n isExpanded()\n ? undefined\n : { height: params().height || '250px', 'max-height': '70vh' }\n }\n >\n <table class=\"w-full border-collapse text-left text-sm\">\n <caption class=\"sr-only\">\n {title()} — {strings.chartDataTable}\n </caption>\n <thead class=\"bg-gray-50 dark:bg-gray-900/40\">\n <tr>\n <For each={tableData().columns}>\n {(column) => (\n <th scope=\"col\" class=\"px-3 py-2 font-medium text-gray-900 dark:text-gray-100\">\n {column}\n </th>\n )}\n </For>\n </tr>\n </thead>\n <tbody>\n <For each={tableData().rows}>\n {(row) => (\n <tr class=\"border-t border-gray-100 dark:border-gray-700\">\n <For each={tableData().columns}>\n {(_column, index) => (\n <td class=\"px-3 py-2 text-gray-700 dark:text-gray-300\">\n {String(row[index()] ?? '')}\n </td>\n )}\n </For>\n </tr>\n )}\n </For>\n <Show when={tableData().rows.length === 0}>\n <tr>\n <td\n colSpan={Math.max(1, tableData().columns.length)}\n class=\"px-3 py-6 text-center text-gray-500 dark:text-gray-400\"\n >\n {strings.chartNoData}\n </td>\n </tr>\n </Show>\n </tbody>\n </table>\n </div>\n </Show>\n\n <div\n class={`relative w-full ${isExpanded() ? 'flex-1 min-h-0' : ''}`}\n style={\n error() || activeView() === 'data'\n ? { display: 'none' }\n : isExpanded()\n ? { height: '100%', display: 'block' }\n : { height: params().height || '250px', display: 'block' }\n }\n >\n <canvas\n ref={canvasRef}\n role=\"img\"\n aria-label={title()}\n aria-describedby={descriptionId}\n aria-hidden={activeView() !== 'chart' || Boolean(error())}\n />\n <Show when={isLoading() && activeView() === 'chart'}>\n <div class=\"absolute inset-0 flex items-center justify-center bg-white/80 dark:bg-gray-800/80\">\n <div class=\"flex flex-col items-center gap-2\">\n <div class=\"animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600\" />\n <span class=\"text-sm text-gray-500 dark:text-gray-400\">{strings.chartLoading}</span>\n </div>\n </div>\n </Show>\n </div>\n </div>\n )}\n />\n </ExpandableWrapper>\n );\n};\n"],"names":["ChartJS","chartJSLoadPromise","loadChartJS","then","module","default","Chart","catch","err","isChartJSAvailable","zoomPluginPromise","zoomPluginRegistered","loadZoomPlugin","ensureZoomPlugin","plugin","register","ZOOMABLE_CHART_TYPES","Set","CHART_ZOOM_BUTTON_FACTOR","isChartZoomed","chart","isZoomedOrPanned","Boolean","level","getZoomLevel","isPlainZoomObject","value","Array","isArray","mergeZoomOptions","base","override","merged","key","Object","entries","undefined","ours","rendererHook","callerHook","args","ContextAwareChartLayout","props","render","useExpanded","ChartJSRenderer","isLoading","setIsLoading","createSignal","error","setError","activeView","setActiveView","isExpanded","setIsExpanded","zoomPlugin","setZoomPlugin","isZoomed","setIsZoomed","canvasRef","chartInstance","renderVersion","params","component","telemetry","useTelemetry","config","useMCPUIConfig","strings","useMCPUIStrings","tableData","createMemo","chartToDataTable","series","chartTableSeries","point","chartTablePoint","label","degradedColLabel","seriesName","degradedSeries","descriptionId","createUniqueId","title","chartView","exportEnabled","exportable","copyDataJSON","JSON","stringify","type","data","zoomWanted","has","hostZoom","options","plugins","zoom","mode","chartZoom","createEffect","untrack","zoomPluginOptions","wheel","enabled","pinch","drag","onZoom","pan","onPan","zoomOptions","zoomControlsShown","handleZoomBy","factor","handleResetZoom","resetZoom","handleExportPNG","url","toDataURL","a","document","createElement","href","download","replace","toLowerCase","click","chartParams","version","destroy","hostZoomOptions","baseOptions","responsive","maintainAspectRatio","legend","display","position","timeAxis","ta","scales","x","time","parser","unit","tooltipFormat","min","max","Error","chartRenderError","message","dispatch","errorMessage","id","componentType","ts","Date","now","onError","queueMicrotask","resize","onCleanup","_$createComponent","ExpandableWrapper","copyData","copyLabel","chartCopy","toolbarVariant","onExpandedChange","children","_el$","_$getNextElement","_tmpl$8","_el$2","firstChild","_el$3","_el$5","nextSibling","_el$6","_el$7","_el$8","_el$14","_el$15","_co$2","_$getNextMarker","_el$16","_el$17","_co$3","_el$18","_el$20","_el$21","_co$4","_el$19","_el$22","_el$23","_co$5","_el$49","_el$50","_co$1","_el$51","_el$52","_co$10","_el$41","_el$42","_el$47","_el$48","_co$0","_$insert","Show","when","_el$4","_tmpl$","$$click","chartDataView","_el$9","_tmpl$3","_el$0","_el$1","_el$11","_el$12","_co$","_el$10","_tmpl$2","_$effect","_p$","_v$","zoomReset","_v$2","e","_$setAttribute","t","_$runHydrationEvents","_v$3","zoomOut","_v$4","_v$5","zoomIn","_v$6","o","_el$13","_tmpl$4","_v$7","_v$8","chartDownloadPng","_v$9","chartDownloadPngAria","_$setProperty","chartDataSummary","_$memo","DegradedFallback","_$mergeProps","formatMCPUIString","chartRenderFailed","String","caption","chartDegradedCaption","_el$24","_tmpl$6","_el$25","_el$26","_el$28","_el$29","_co$6","_el$27","_el$30","_el$31","_co$7","_el$32","_el$33","_el$34","_el$37","_el$38","_co$8","_el$39","_el$40","_co$9","chartDataTable","For","each","columns","column","_el$53","_tmpl$9","rows","row","_el$54","_tmpl$0","_column","index","_el$55","_tmpl$1","length","_el$35","_tmpl$5","_el$36","chartNoData","Math","_v$0","_v$1","height","_$className","_$style","_ref$","_$use","_el$43","_tmpl$7","_el$44","_el$45","_el$46","chartLoading","_v$10","className","_v$11","chartViewSelector","_v$12","_v$13","_v$14","_v$15","_v$16","_v$17","_v$18","_v$19","i","n","s","h","r","d","_$delegateEvents"],"mappings":"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"}
@@ -58,7 +58,14 @@ const ExpandableWrapper = (props) => {
58
58
  });
59
59
  }
60
60
  });
61
+ let pressedOnBackdrop = null;
62
+ const handleBackdropPointerDown = (e) => {
63
+ pressedOnBackdrop = e.target === e.currentTarget;
64
+ };
61
65
  const handleBackdropClick = (e) => {
66
+ const startedOnBackdrop = pressedOnBackdrop;
67
+ pressedOnBackdrop = null;
68
+ if (startedOnBackdrop === false) return;
62
69
  if (e.target === e.currentTarget) handleClose();
63
70
  };
64
71
  const handleCopy = async () => {
@@ -95,6 +102,7 @@ const ExpandableWrapper = (props) => {
95
102
  var _el$5 = web.getNextElement(_tmpl$3), _el$6 = _el$5.firstChild, _el$7 = _el$6.firstChild, _el$8 = _el$7.firstChild, _el$9 = _el$8.nextSibling, _el$11 = _el$9.firstChild, [_el$12, _co$] = web.getNextMarker(_el$11.nextSibling), _el$10 = _el$12.nextSibling, _el$13 = _el$7.nextSibling;
96
103
  var _ref$3 = dialogRef;
97
104
  typeof _ref$3 === "function" ? web.use(_ref$3, _el$5) : dialogRef = _el$5;
105
+ _el$5.$$pointerdown = handleBackdropPointerDown;
98
106
  _el$5.$$click = handleBackdropClick;
99
107
  _el$6.$$click = (e) => e.stopPropagation();
100
108
  web.insert(_el$8, () => props.title || strings.expandedView);
@@ -163,7 +171,7 @@ const ExpandableWrapper = (props) => {
163
171
  return _el$;
164
172
  })();
165
173
  };
166
- web.delegateEvents(["click"]);
174
+ web.delegateEvents(["click", "pointerdown"]);
167
175
  exports.ExpandableWrapper = ExpandableWrapper;
168
176
  exports.useExpanded = useExpanded;
169
177
  //# sourceMappingURL=ExpandableWrapper.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExpandableWrapper.cjs","sources":["../../src/components/ExpandableWrapper.tsx"],"sourcesContent":["/**\n * ExpandableWrapper - Generic expand/fullscreen wrapper for components\n * v2.2.0: Reusable wrapper that adds expand button + fullscreen modal\n *\n * Uses DOM reparenting to avoid rendering children twice — critical for\n * imperative components like ChartJS that bind instances to DOM nodes.\n */\n\nimport { Component, Show, createSignal, createEffect, onCleanup, JSX, createContext, useContext, Accessor } from 'solid-js'\nimport { Portal } from 'solid-js/web'\nimport { useMCPUIStrings } from '../context/MCPUIStringsContext'\n\n/** Context for child components to know if they're in expanded/fullscreen view */\nconst ExpandedContext = createContext<Accessor<boolean>>(() => false)\n\n/** Hook for child components to read expanded state */\nexport const useExpanded = () => useContext(ExpandedContext)\n\nexport interface ExpandableWrapperProps {\n /** Content to render inline (and in expanded view) */\n children: JSX.Element\n /** Title shown in the expanded modal header */\n title?: string\n /** Data string for copy-to-clipboard in expanded view */\n copyData?: string\n /** Label for copy button tooltip */\n copyLabel?: string\n /**\n * Visibility behavior of the inline expand button (v6.3.0 — axe 4 deposium handoff).\n * - `'hover'` (default) : opacity 0, fades to 0.7 on parent group hover.\n * Backwards-compatible — pre-v6.3.0 behavior.\n * - `'always-visible'` : opacity 0.6 permanent, 1 on hover. Use this when\n * the inline button needs to be discoverable without hovering — esp.\n * on touch surfaces and consumer themes where the hover-only pattern\n * hides the affordance.\n */\n toolbarVariant?: 'hover' | 'always-visible'\n /**\n * Called with the expanded state on mount and whenever it changes (v6.19.0).\n * Needed by renderers that render their own `<ExpandableWrapper>`: their\n * `useExpanded()` call sits ABOVE the wrapper's context provider and would\n * only ever see an outer wrapper.\n */\n onExpandedChange?: (expanded: boolean) => void\n}\n\n/**\n * Wraps any component with an expand button (top-right corner).\n * Opens a fullscreen Portal modal. The children's DOM is physically\n * reparented into the modal (not duplicated), so imperative bindings\n * like Chart.js canvas refs stay intact.\n *\n * @example\n * <ExpandableWrapper title=\"Sales Data\" copyData={tsvData}>\n * <TableRenderer ... />\n * </ExpandableWrapper>\n */\nexport const ExpandableWrapper: Component<ExpandableWrapperProps> = (props) => {\n const [isExpanded, setIsExpanded] = createSignal(false)\n const [copied, setCopied] = createSignal(false)\n const strings = useMCPUIStrings()\n let dialogRef: HTMLDivElement | undefined\n let contentRef: HTMLDivElement | undefined\n let inlineSlotRef: HTMLDivElement | undefined\n let modalSlotRef: HTMLDivElement | undefined\n\n const handleOpen = () => setIsExpanded(true)\n const handleClose = () => setIsExpanded(false)\n\n // Report the state to the owner (see `onExpandedChange` docs).\n createEffect(() => {\n props.onExpandedChange?.(isExpanded())\n })\n\n // Reparent content DOM between inline and modal slots\n createEffect(() => {\n if (!contentRef) return\n\n if (isExpanded()) {\n // Move content into modal\n modalSlotRef?.appendChild(contentRef)\n } else {\n // Move content back to inline\n inlineSlotRef?.appendChild(contentRef)\n }\n })\n\n // Keyboard: Escape to close\n createEffect(() => {\n if (!isExpanded()) return\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault()\n handleClose()\n }\n }\n\n document.addEventListener('keydown', onKeyDown)\n onCleanup(() => document.removeEventListener('keydown', onKeyDown))\n })\n\n // Prevent body scroll when expanded\n createEffect(() => {\n if (isExpanded()) {\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n // Focus the dialog\n setTimeout(() => dialogRef?.focus(), 10)\n onCleanup(() => {\n document.body.style.overflow = prev\n })\n }\n })\n\n const handleBackdropClick = (e: MouseEvent) => {\n if (e.target === e.currentTarget) handleClose()\n }\n\n const handleCopy = async () => {\n if (!props.copyData) return\n try {\n await navigator.clipboard.writeText(props.copyData)\n setCopied(true)\n setTimeout(() => setCopied(false), 2000)\n } catch (err) {\n console.error('Failed to copy:', err)\n }\n }\n\n return (\n <div class=\"relative group\">\n {/* Inline slot — content lives here when not expanded */}\n <div ref={inlineSlotRef}>\n <div ref={contentRef}>\n <ExpandedContext.Provider value={isExpanded}>\n {props.children}\n </ExpandedContext.Provider>\n </div>\n </div>\n\n {/* Expand button — visibility per `toolbarVariant` (default 'hover') */}\n <button\n type=\"button\"\n onClick={handleOpen}\n class={`absolute top-2 right-2 z-10 ${\n props.toolbarVariant === 'always-visible'\n ? 'opacity-60 hover:opacity-100'\n : 'opacity-0 group-hover:opacity-70 hover:!opacity-100'\n } p-1.5 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm`}\n title={strings.expand}\n aria-label={strings.expand}\n data-mcp-ui-action=\"expand\"\n >\n <svg class=\"w-3.5 h-3.5 text-gray-500 dark:text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5v-4m0 4h-4m4 0l-5-5\" />\n </svg>\n </button>\n\n {/* Fullscreen modal via Portal */}\n <Show when={isExpanded()}>\n <Portal>\n <div\n class=\"fixed inset-0 z-50 flex flex-col bg-black/50 backdrop-blur-sm\"\n style={{ animation: 'expandable-fade-in 0.15s ease-out' }}\n onClick={handleBackdropClick}\n role=\"dialog\"\n data-mcp-ui-portal=\"dialog\"\n aria-modal=\"true\"\n aria-label={props.title || strings.expandedView}\n tabIndex={-1}\n ref={dialogRef}\n >\n {/* Modal panel */}\n <div\n class=\"relative flex flex-col m-4 flex-1 bg-white dark:bg-gray-800 rounded-lg shadow-xl overflow-hidden\"\n style={{ animation: 'expandable-scale-in 0.15s ease-out' }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div class=\"flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex-shrink-0\">\n <h2 class=\"text-lg font-semibold text-gray-900 dark:text-white truncate\">\n {props.title || strings.expandedView}\n </h2>\n <div class=\"flex items-center gap-2\">\n {/* Copy button */}\n <Show when={props.copyData}>\n <button\n type=\"button\"\n onClick={handleCopy}\n class=\"p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n title={props.copyLabel || strings.copyToClipboard}\n aria-label={props.copyLabel || strings.copyToClipboard}\n data-mcp-ui-action=\"copy\"\n >\n <Show\n when={!copied()}\n fallback={\n <svg class=\"w-5 h-5 text-green-500\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\" />\n </svg>\n }\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" 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 </svg>\n </Show>\n </button>\n </Show>\n {/* Close button */}\n <button\n type=\"button\"\n onClick={handleClose}\n class=\"p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n aria-label={strings.closeExpandedView}\n data-mcp-ui-action=\"close\"\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n </div>\n\n {/* Modal slot — content is reparented here when expanded.\n v6.1.0 : `flex flex-col` lets aware children opt into\n `flex-1 min-h-0` to fill the modal vertically (chart,\n table, map, graph). Unaware children keep working\n thanks to `overflow-auto` (their natural height\n scrolls if it overflows the slot). */}\n <div class=\"flex-1 min-h-0 overflow-auto p-4 flex flex-col\" ref={modalSlotRef} />\n </div>\n </div>\n\n <style>{`\n @keyframes expandable-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes expandable-scale-in {\n from { opacity: 0; transform: scale(0.97); }\n to { opacity: 1; transform: scale(1); }\n }\n `}</style>\n </Portal>\n </Show>\n </div>\n )\n}\n"],"names":["ExpandedContext","createContext","useExpanded","useContext","ExpandableWrapper","props","isExpanded","setIsExpanded","createSignal","copied","setCopied","strings","useMCPUIStrings","dialogRef","contentRef","inlineSlotRef","modalSlotRef","handleOpen","handleClose","createEffect","onExpandedChange","appendChild","onKeyDown","e","key","preventDefault","document","addEventListener","onCleanup","removeEventListener","prev","body","style","overflow","setTimeout","focus","handleBackdropClick","target","currentTarget","handleCopy","copyData","navigator","clipboard","writeText","err","console","error","_el$","_$getNextElement","_tmpl$5","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$15","_el$16","_co$2","_$getNextMarker","_ref$","_$use","_ref$2","_$insert","_$createComponent","Provider","value","children","$$click","Show","when","Portal","_el$5","_tmpl$3","_el$6","_el$7","_el$8","_el$9","_el$11","_el$12","_co$","_el$10","_el$13","_ref$3","stopPropagation","title","expandedView","_el$0","_tmpl$2","fallback","_tmpl$6","_tmpl$","_$effect","_p$","_v$","copyLabel","copyToClipboard","_v$2","_$setAttribute","t","undefined","_$runHydrationEvents","_ref$4","_v$3","_v$4","closeExpandedView","_tmpl$4","_v$5","toolbarVariant","_v$6","expand","_v$7","_$className","a","_$delegateEvents"],"mappings":"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"}
1
+ {"version":3,"file":"ExpandableWrapper.cjs","sources":["../../src/components/ExpandableWrapper.tsx"],"sourcesContent":["/**\n * ExpandableWrapper - Generic expand/fullscreen wrapper for components\n * v2.2.0: Reusable wrapper that adds expand button + fullscreen modal\n *\n * Uses DOM reparenting to avoid rendering children twice — critical for\n * imperative components like ChartJS that bind instances to DOM nodes.\n */\n\nimport { Component, Show, createSignal, createEffect, onCleanup, JSX, createContext, useContext, Accessor } from 'solid-js'\nimport { Portal } from 'solid-js/web'\nimport { useMCPUIStrings } from '../context/MCPUIStringsContext'\n\n/** Context for child components to know if they're in expanded/fullscreen view */\nconst ExpandedContext = createContext<Accessor<boolean>>(() => false)\n\n/** Hook for child components to read expanded state */\nexport const useExpanded = () => useContext(ExpandedContext)\n\nexport interface ExpandableWrapperProps {\n /** Content to render inline (and in expanded view) */\n children: JSX.Element\n /** Title shown in the expanded modal header */\n title?: string\n /** Data string for copy-to-clipboard in expanded view */\n copyData?: string\n /** Label for copy button tooltip */\n copyLabel?: string\n /**\n * Visibility behavior of the inline expand button (v6.3.0 — axe 4 deposium handoff).\n * - `'hover'` (default) : opacity 0, fades to 0.7 on parent group hover.\n * Backwards-compatible — pre-v6.3.0 behavior.\n * - `'always-visible'` : opacity 0.6 permanent, 1 on hover. Use this when\n * the inline button needs to be discoverable without hovering — esp.\n * on touch surfaces and consumer themes where the hover-only pattern\n * hides the affordance.\n */\n toolbarVariant?: 'hover' | 'always-visible'\n /**\n * Called with the expanded state on mount and whenever it changes (v6.19.0).\n * Needed by renderers that render their own `<ExpandableWrapper>`: their\n * `useExpanded()` call sits ABOVE the wrapper's context provider and would\n * only ever see an outer wrapper.\n */\n onExpandedChange?: (expanded: boolean) => void\n}\n\n/**\n * Wraps any component with an expand button (top-right corner).\n * Opens a fullscreen Portal modal. The children's DOM is physically\n * reparented into the modal (not duplicated), so imperative bindings\n * like Chart.js canvas refs stay intact.\n *\n * @example\n * <ExpandableWrapper title=\"Sales Data\" copyData={tsvData}>\n * <TableRenderer ... />\n * </ExpandableWrapper>\n */\nexport const ExpandableWrapper: Component<ExpandableWrapperProps> = (props) => {\n const [isExpanded, setIsExpanded] = createSignal(false)\n const [copied, setCopied] = createSignal(false)\n const strings = useMCPUIStrings()\n let dialogRef: HTMLDivElement | undefined\n let contentRef: HTMLDivElement | undefined\n let inlineSlotRef: HTMLDivElement | undefined\n let modalSlotRef: HTMLDivElement | undefined\n\n const handleOpen = () => setIsExpanded(true)\n const handleClose = () => setIsExpanded(false)\n\n // Report the state to the owner (see `onExpandedChange` docs).\n createEffect(() => {\n props.onExpandedChange?.(isExpanded())\n })\n\n // Reparent content DOM between inline and modal slots\n createEffect(() => {\n if (!contentRef) return\n\n if (isExpanded()) {\n // Move content into modal\n modalSlotRef?.appendChild(contentRef)\n } else {\n // Move content back to inline\n inlineSlotRef?.appendChild(contentRef)\n }\n })\n\n // Keyboard: Escape to close\n createEffect(() => {\n if (!isExpanded()) return\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault()\n handleClose()\n }\n }\n\n document.addEventListener('keydown', onKeyDown)\n onCleanup(() => document.removeEventListener('keydown', onKeyDown))\n })\n\n // Prevent body scroll when expanded\n createEffect(() => {\n if (isExpanded()) {\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n // Focus the dialog\n setTimeout(() => dialogRef?.focus(), 10)\n onCleanup(() => {\n document.body.style.overflow = prev\n })\n }\n })\n\n /**\n * Did the gesture that produced the current click START on the backdrop?\n *\n * `null` means no pointer press was seen — a synthetic or programmatic\n * click, which stays allowed so keyboard and test-driven closes keep working.\n */\n let pressedOnBackdrop: boolean | null = null\n\n const handleBackdropPointerDown = (e: PointerEvent) => {\n pressedOnBackdrop = e.target === e.currentTarget\n }\n\n /**\n * Closing on a backdrop click is only safe when the gesture also BEGAN there.\n *\n * When a press and its release have different targets the browser dispatches\n * `click` on their nearest common ancestor — here the backdrop. So a drag\n * that starts on the content and releases over the 16px margin around the\n * panel (panning a zoomed chart, selecting text in a table) would otherwise\n * land on this handler with `e.target === e.currentTarget` and tear the modal\n * down mid-gesture.\n */\n const handleBackdropClick = (e: MouseEvent) => {\n const startedOnBackdrop = pressedOnBackdrop\n pressedOnBackdrop = null\n if (startedOnBackdrop === false) return\n if (e.target === e.currentTarget) handleClose()\n }\n\n const handleCopy = async () => {\n if (!props.copyData) return\n try {\n await navigator.clipboard.writeText(props.copyData)\n setCopied(true)\n setTimeout(() => setCopied(false), 2000)\n } catch (err) {\n console.error('Failed to copy:', err)\n }\n }\n\n return (\n <div class=\"relative group\">\n {/* Inline slot — content lives here when not expanded */}\n <div ref={inlineSlotRef}>\n <div ref={contentRef}>\n <ExpandedContext.Provider value={isExpanded}>\n {props.children}\n </ExpandedContext.Provider>\n </div>\n </div>\n\n {/* Expand button — visibility per `toolbarVariant` (default 'hover') */}\n <button\n type=\"button\"\n onClick={handleOpen}\n class={`absolute top-2 right-2 z-10 ${\n props.toolbarVariant === 'always-visible'\n ? 'opacity-60 hover:opacity-100'\n : 'opacity-0 group-hover:opacity-70 hover:!opacity-100'\n } p-1.5 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm`}\n title={strings.expand}\n aria-label={strings.expand}\n data-mcp-ui-action=\"expand\"\n >\n <svg class=\"w-3.5 h-3.5 text-gray-500 dark:text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5v-4m0 4h-4m4 0l-5-5\" />\n </svg>\n </button>\n\n {/* Fullscreen modal via Portal */}\n <Show when={isExpanded()}>\n <Portal>\n <div\n class=\"fixed inset-0 z-50 flex flex-col bg-black/50 backdrop-blur-sm\"\n style={{ animation: 'expandable-fade-in 0.15s ease-out' }}\n onClick={handleBackdropClick}\n onPointerDown={handleBackdropPointerDown}\n role=\"dialog\"\n data-mcp-ui-portal=\"dialog\"\n aria-modal=\"true\"\n aria-label={props.title || strings.expandedView}\n tabIndex={-1}\n ref={dialogRef}\n >\n {/* Modal panel */}\n <div\n class=\"relative flex flex-col m-4 flex-1 bg-white dark:bg-gray-800 rounded-lg shadow-xl overflow-hidden\"\n style={{ animation: 'expandable-scale-in 0.15s ease-out' }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div class=\"flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex-shrink-0\">\n <h2 class=\"text-lg font-semibold text-gray-900 dark:text-white truncate\">\n {props.title || strings.expandedView}\n </h2>\n <div class=\"flex items-center gap-2\">\n {/* Copy button */}\n <Show when={props.copyData}>\n <button\n type=\"button\"\n onClick={handleCopy}\n class=\"p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n title={props.copyLabel || strings.copyToClipboard}\n aria-label={props.copyLabel || strings.copyToClipboard}\n data-mcp-ui-action=\"copy\"\n >\n <Show\n when={!copied()}\n fallback={\n <svg class=\"w-5 h-5 text-green-500\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\" />\n </svg>\n }\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" 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 </svg>\n </Show>\n </button>\n </Show>\n {/* Close button */}\n <button\n type=\"button\"\n onClick={handleClose}\n class=\"p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n aria-label={strings.closeExpandedView}\n data-mcp-ui-action=\"close\"\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n </div>\n\n {/* Modal slot — content is reparented here when expanded.\n v6.1.0 : `flex flex-col` lets aware children opt into\n `flex-1 min-h-0` to fill the modal vertically (chart,\n table, map, graph). Unaware children keep working\n thanks to `overflow-auto` (their natural height\n scrolls if it overflows the slot). */}\n <div class=\"flex-1 min-h-0 overflow-auto p-4 flex flex-col\" ref={modalSlotRef} />\n </div>\n </div>\n\n <style>{`\n @keyframes expandable-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes expandable-scale-in {\n from { opacity: 0; transform: scale(0.97); }\n to { opacity: 1; transform: scale(1); }\n }\n `}</style>\n </Portal>\n </Show>\n </div>\n )\n}\n"],"names":["ExpandedContext","createContext","useExpanded","useContext","ExpandableWrapper","props","isExpanded","setIsExpanded","createSignal","copied","setCopied","strings","useMCPUIStrings","dialogRef","contentRef","inlineSlotRef","modalSlotRef","handleOpen","handleClose","createEffect","onExpandedChange","appendChild","onKeyDown","e","key","preventDefault","document","addEventListener","onCleanup","removeEventListener","prev","body","style","overflow","setTimeout","focus","pressedOnBackdrop","handleBackdropPointerDown","target","currentTarget","handleBackdropClick","startedOnBackdrop","handleCopy","copyData","navigator","clipboard","writeText","err","console","error","_el$","_$getNextElement","_tmpl$5","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$15","_el$16","_co$2","_$getNextMarker","_ref$","_$use","_ref$2","_$insert","_$createComponent","Provider","value","children","$$click","Show","when","Portal","_el$5","_tmpl$3","_el$6","_el$7","_el$8","_el$9","_el$11","_el$12","_co$","_el$10","_el$13","_ref$3","$$pointerdown","stopPropagation","title","expandedView","_el$0","_tmpl$2","fallback","_tmpl$6","_tmpl$","_$effect","_p$","_v$","copyLabel","copyToClipboard","_v$2","_$setAttribute","t","undefined","_$runHydrationEvents","_ref$4","_v$3","_v$4","closeExpandedView","_tmpl$4","_v$5","toolbarVariant","_v$6","expand","_v$7","_$className","a","_$delegateEvents"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ExpandableWrapper.d.ts","sourceRoot":"","sources":["../../src/components/ExpandableWrapper.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAA+C,GAAG,EAA6B,QAAQ,EAAE,MAAM,UAAU,CAAA;AAO3H,uDAAuD;AACvD,eAAO,MAAM,WAAW,yBAAoC,CAAA;AAE5D,MAAM,WAAW,sBAAsB;IACrC,sDAAsD;IACtD,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAA;IACrB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAA;IAC3C;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;CAC/C;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,CAAC,sBAAsB,CA+L/D,CAAA"}
1
+ {"version":3,"file":"ExpandableWrapper.d.ts","sourceRoot":"","sources":["../../src/components/ExpandableWrapper.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAA+C,GAAG,EAA6B,QAAQ,EAAE,MAAM,UAAU,CAAA;AAO3H,uDAAuD;AACvD,eAAO,MAAM,WAAW,yBAAoC,CAAA;AAE5D,MAAM,WAAW,sBAAsB;IACrC,sDAAsD;IACtD,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAA;IACrB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAA;IAC3C;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;CAC/C;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,CAAC,sBAAsB,CAyN/D,CAAA"}
@@ -56,7 +56,14 @@ const ExpandableWrapper = (props) => {
56
56
  });
57
57
  }
58
58
  });
59
+ let pressedOnBackdrop = null;
60
+ const handleBackdropPointerDown = (e) => {
61
+ pressedOnBackdrop = e.target === e.currentTarget;
62
+ };
59
63
  const handleBackdropClick = (e) => {
64
+ const startedOnBackdrop = pressedOnBackdrop;
65
+ pressedOnBackdrop = null;
66
+ if (startedOnBackdrop === false) return;
60
67
  if (e.target === e.currentTarget) handleClose();
61
68
  };
62
69
  const handleCopy = async () => {
@@ -93,6 +100,7 @@ const ExpandableWrapper = (props) => {
93
100
  var _el$5 = getNextElement(_tmpl$3), _el$6 = _el$5.firstChild, _el$7 = _el$6.firstChild, _el$8 = _el$7.firstChild, _el$9 = _el$8.nextSibling, _el$11 = _el$9.firstChild, [_el$12, _co$] = getNextMarker(_el$11.nextSibling), _el$10 = _el$12.nextSibling, _el$13 = _el$7.nextSibling;
94
101
  var _ref$3 = dialogRef;
95
102
  typeof _ref$3 === "function" ? use(_ref$3, _el$5) : dialogRef = _el$5;
103
+ _el$5.$$pointerdown = handleBackdropPointerDown;
96
104
  _el$5.$$click = handleBackdropClick;
97
105
  _el$6.$$click = (e) => e.stopPropagation();
98
106
  insert(_el$8, () => props.title || strings.expandedView);
@@ -161,7 +169,7 @@ const ExpandableWrapper = (props) => {
161
169
  return _el$;
162
170
  })();
163
171
  };
164
- delegateEvents(["click"]);
172
+ delegateEvents(["click", "pointerdown"]);
165
173
  export {
166
174
  ExpandableWrapper,
167
175
  useExpanded
@@ -1 +1 @@
1
- {"version":3,"file":"ExpandableWrapper.js","sources":["../../src/components/ExpandableWrapper.tsx"],"sourcesContent":["/**\n * ExpandableWrapper - Generic expand/fullscreen wrapper for components\n * v2.2.0: Reusable wrapper that adds expand button + fullscreen modal\n *\n * Uses DOM reparenting to avoid rendering children twice — critical for\n * imperative components like ChartJS that bind instances to DOM nodes.\n */\n\nimport { Component, Show, createSignal, createEffect, onCleanup, JSX, createContext, useContext, Accessor } from 'solid-js'\nimport { Portal } from 'solid-js/web'\nimport { useMCPUIStrings } from '../context/MCPUIStringsContext'\n\n/** Context for child components to know if they're in expanded/fullscreen view */\nconst ExpandedContext = createContext<Accessor<boolean>>(() => false)\n\n/** Hook for child components to read expanded state */\nexport const useExpanded = () => useContext(ExpandedContext)\n\nexport interface ExpandableWrapperProps {\n /** Content to render inline (and in expanded view) */\n children: JSX.Element\n /** Title shown in the expanded modal header */\n title?: string\n /** Data string for copy-to-clipboard in expanded view */\n copyData?: string\n /** Label for copy button tooltip */\n copyLabel?: string\n /**\n * Visibility behavior of the inline expand button (v6.3.0 — axe 4 deposium handoff).\n * - `'hover'` (default) : opacity 0, fades to 0.7 on parent group hover.\n * Backwards-compatible — pre-v6.3.0 behavior.\n * - `'always-visible'` : opacity 0.6 permanent, 1 on hover. Use this when\n * the inline button needs to be discoverable without hovering — esp.\n * on touch surfaces and consumer themes where the hover-only pattern\n * hides the affordance.\n */\n toolbarVariant?: 'hover' | 'always-visible'\n /**\n * Called with the expanded state on mount and whenever it changes (v6.19.0).\n * Needed by renderers that render their own `<ExpandableWrapper>`: their\n * `useExpanded()` call sits ABOVE the wrapper's context provider and would\n * only ever see an outer wrapper.\n */\n onExpandedChange?: (expanded: boolean) => void\n}\n\n/**\n * Wraps any component with an expand button (top-right corner).\n * Opens a fullscreen Portal modal. The children's DOM is physically\n * reparented into the modal (not duplicated), so imperative bindings\n * like Chart.js canvas refs stay intact.\n *\n * @example\n * <ExpandableWrapper title=\"Sales Data\" copyData={tsvData}>\n * <TableRenderer ... />\n * </ExpandableWrapper>\n */\nexport const ExpandableWrapper: Component<ExpandableWrapperProps> = (props) => {\n const [isExpanded, setIsExpanded] = createSignal(false)\n const [copied, setCopied] = createSignal(false)\n const strings = useMCPUIStrings()\n let dialogRef: HTMLDivElement | undefined\n let contentRef: HTMLDivElement | undefined\n let inlineSlotRef: HTMLDivElement | undefined\n let modalSlotRef: HTMLDivElement | undefined\n\n const handleOpen = () => setIsExpanded(true)\n const handleClose = () => setIsExpanded(false)\n\n // Report the state to the owner (see `onExpandedChange` docs).\n createEffect(() => {\n props.onExpandedChange?.(isExpanded())\n })\n\n // Reparent content DOM between inline and modal slots\n createEffect(() => {\n if (!contentRef) return\n\n if (isExpanded()) {\n // Move content into modal\n modalSlotRef?.appendChild(contentRef)\n } else {\n // Move content back to inline\n inlineSlotRef?.appendChild(contentRef)\n }\n })\n\n // Keyboard: Escape to close\n createEffect(() => {\n if (!isExpanded()) return\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault()\n handleClose()\n }\n }\n\n document.addEventListener('keydown', onKeyDown)\n onCleanup(() => document.removeEventListener('keydown', onKeyDown))\n })\n\n // Prevent body scroll when expanded\n createEffect(() => {\n if (isExpanded()) {\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n // Focus the dialog\n setTimeout(() => dialogRef?.focus(), 10)\n onCleanup(() => {\n document.body.style.overflow = prev\n })\n }\n })\n\n const handleBackdropClick = (e: MouseEvent) => {\n if (e.target === e.currentTarget) handleClose()\n }\n\n const handleCopy = async () => {\n if (!props.copyData) return\n try {\n await navigator.clipboard.writeText(props.copyData)\n setCopied(true)\n setTimeout(() => setCopied(false), 2000)\n } catch (err) {\n console.error('Failed to copy:', err)\n }\n }\n\n return (\n <div class=\"relative group\">\n {/* Inline slot — content lives here when not expanded */}\n <div ref={inlineSlotRef}>\n <div ref={contentRef}>\n <ExpandedContext.Provider value={isExpanded}>\n {props.children}\n </ExpandedContext.Provider>\n </div>\n </div>\n\n {/* Expand button — visibility per `toolbarVariant` (default 'hover') */}\n <button\n type=\"button\"\n onClick={handleOpen}\n class={`absolute top-2 right-2 z-10 ${\n props.toolbarVariant === 'always-visible'\n ? 'opacity-60 hover:opacity-100'\n : 'opacity-0 group-hover:opacity-70 hover:!opacity-100'\n } p-1.5 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm`}\n title={strings.expand}\n aria-label={strings.expand}\n data-mcp-ui-action=\"expand\"\n >\n <svg class=\"w-3.5 h-3.5 text-gray-500 dark:text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5v-4m0 4h-4m4 0l-5-5\" />\n </svg>\n </button>\n\n {/* Fullscreen modal via Portal */}\n <Show when={isExpanded()}>\n <Portal>\n <div\n class=\"fixed inset-0 z-50 flex flex-col bg-black/50 backdrop-blur-sm\"\n style={{ animation: 'expandable-fade-in 0.15s ease-out' }}\n onClick={handleBackdropClick}\n role=\"dialog\"\n data-mcp-ui-portal=\"dialog\"\n aria-modal=\"true\"\n aria-label={props.title || strings.expandedView}\n tabIndex={-1}\n ref={dialogRef}\n >\n {/* Modal panel */}\n <div\n class=\"relative flex flex-col m-4 flex-1 bg-white dark:bg-gray-800 rounded-lg shadow-xl overflow-hidden\"\n style={{ animation: 'expandable-scale-in 0.15s ease-out' }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div class=\"flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex-shrink-0\">\n <h2 class=\"text-lg font-semibold text-gray-900 dark:text-white truncate\">\n {props.title || strings.expandedView}\n </h2>\n <div class=\"flex items-center gap-2\">\n {/* Copy button */}\n <Show when={props.copyData}>\n <button\n type=\"button\"\n onClick={handleCopy}\n class=\"p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n title={props.copyLabel || strings.copyToClipboard}\n aria-label={props.copyLabel || strings.copyToClipboard}\n data-mcp-ui-action=\"copy\"\n >\n <Show\n when={!copied()}\n fallback={\n <svg class=\"w-5 h-5 text-green-500\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\" />\n </svg>\n }\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" 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 </svg>\n </Show>\n </button>\n </Show>\n {/* Close button */}\n <button\n type=\"button\"\n onClick={handleClose}\n class=\"p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n aria-label={strings.closeExpandedView}\n data-mcp-ui-action=\"close\"\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n </div>\n\n {/* Modal slot — content is reparented here when expanded.\n v6.1.0 : `flex flex-col` lets aware children opt into\n `flex-1 min-h-0` to fill the modal vertically (chart,\n table, map, graph). Unaware children keep working\n thanks to `overflow-auto` (their natural height\n scrolls if it overflows the slot). */}\n <div class=\"flex-1 min-h-0 overflow-auto p-4 flex flex-col\" ref={modalSlotRef} />\n </div>\n </div>\n\n <style>{`\n @keyframes expandable-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes expandable-scale-in {\n from { opacity: 0; transform: scale(0.97); }\n to { opacity: 1; transform: scale(1); }\n }\n `}</style>\n </Portal>\n </Show>\n </div>\n )\n}\n"],"names":["ExpandedContext","createContext","useExpanded","useContext","ExpandableWrapper","props","isExpanded","setIsExpanded","createSignal","copied","setCopied","strings","useMCPUIStrings","dialogRef","contentRef","inlineSlotRef","modalSlotRef","handleOpen","handleClose","createEffect","onExpandedChange","appendChild","onKeyDown","e","key","preventDefault","document","addEventListener","onCleanup","removeEventListener","prev","body","style","overflow","setTimeout","focus","handleBackdropClick","target","currentTarget","handleCopy","copyData","navigator","clipboard","writeText","err","console","error","_el$","_$getNextElement","_tmpl$5","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$15","_el$16","_co$2","_$getNextMarker","_ref$","_$use","_ref$2","_$insert","_$createComponent","Provider","value","children","$$click","Show","when","Portal","_el$5","_tmpl$3","_el$6","_el$7","_el$8","_el$9","_el$11","_el$12","_co$","_el$10","_el$13","_ref$3","stopPropagation","title","expandedView","_el$0","_tmpl$2","fallback","_tmpl$6","_tmpl$","_$effect","_p$","_v$","copyLabel","copyToClipboard","_v$2","_$setAttribute","t","undefined","_$runHydrationEvents","_ref$4","_v$3","_v$4","closeExpandedView","_tmpl$4","_v$5","toolbarVariant","_v$6","expand","_v$7","_$className","a","_$delegateEvents"],"mappings":"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"}
1
+ {"version":3,"file":"ExpandableWrapper.js","sources":["../../src/components/ExpandableWrapper.tsx"],"sourcesContent":["/**\n * ExpandableWrapper - Generic expand/fullscreen wrapper for components\n * v2.2.0: Reusable wrapper that adds expand button + fullscreen modal\n *\n * Uses DOM reparenting to avoid rendering children twice — critical for\n * imperative components like ChartJS that bind instances to DOM nodes.\n */\n\nimport { Component, Show, createSignal, createEffect, onCleanup, JSX, createContext, useContext, Accessor } from 'solid-js'\nimport { Portal } from 'solid-js/web'\nimport { useMCPUIStrings } from '../context/MCPUIStringsContext'\n\n/** Context for child components to know if they're in expanded/fullscreen view */\nconst ExpandedContext = createContext<Accessor<boolean>>(() => false)\n\n/** Hook for child components to read expanded state */\nexport const useExpanded = () => useContext(ExpandedContext)\n\nexport interface ExpandableWrapperProps {\n /** Content to render inline (and in expanded view) */\n children: JSX.Element\n /** Title shown in the expanded modal header */\n title?: string\n /** Data string for copy-to-clipboard in expanded view */\n copyData?: string\n /** Label for copy button tooltip */\n copyLabel?: string\n /**\n * Visibility behavior of the inline expand button (v6.3.0 — axe 4 deposium handoff).\n * - `'hover'` (default) : opacity 0, fades to 0.7 on parent group hover.\n * Backwards-compatible — pre-v6.3.0 behavior.\n * - `'always-visible'` : opacity 0.6 permanent, 1 on hover. Use this when\n * the inline button needs to be discoverable without hovering — esp.\n * on touch surfaces and consumer themes where the hover-only pattern\n * hides the affordance.\n */\n toolbarVariant?: 'hover' | 'always-visible'\n /**\n * Called with the expanded state on mount and whenever it changes (v6.19.0).\n * Needed by renderers that render their own `<ExpandableWrapper>`: their\n * `useExpanded()` call sits ABOVE the wrapper's context provider and would\n * only ever see an outer wrapper.\n */\n onExpandedChange?: (expanded: boolean) => void\n}\n\n/**\n * Wraps any component with an expand button (top-right corner).\n * Opens a fullscreen Portal modal. The children's DOM is physically\n * reparented into the modal (not duplicated), so imperative bindings\n * like Chart.js canvas refs stay intact.\n *\n * @example\n * <ExpandableWrapper title=\"Sales Data\" copyData={tsvData}>\n * <TableRenderer ... />\n * </ExpandableWrapper>\n */\nexport const ExpandableWrapper: Component<ExpandableWrapperProps> = (props) => {\n const [isExpanded, setIsExpanded] = createSignal(false)\n const [copied, setCopied] = createSignal(false)\n const strings = useMCPUIStrings()\n let dialogRef: HTMLDivElement | undefined\n let contentRef: HTMLDivElement | undefined\n let inlineSlotRef: HTMLDivElement | undefined\n let modalSlotRef: HTMLDivElement | undefined\n\n const handleOpen = () => setIsExpanded(true)\n const handleClose = () => setIsExpanded(false)\n\n // Report the state to the owner (see `onExpandedChange` docs).\n createEffect(() => {\n props.onExpandedChange?.(isExpanded())\n })\n\n // Reparent content DOM between inline and modal slots\n createEffect(() => {\n if (!contentRef) return\n\n if (isExpanded()) {\n // Move content into modal\n modalSlotRef?.appendChild(contentRef)\n } else {\n // Move content back to inline\n inlineSlotRef?.appendChild(contentRef)\n }\n })\n\n // Keyboard: Escape to close\n createEffect(() => {\n if (!isExpanded()) return\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault()\n handleClose()\n }\n }\n\n document.addEventListener('keydown', onKeyDown)\n onCleanup(() => document.removeEventListener('keydown', onKeyDown))\n })\n\n // Prevent body scroll when expanded\n createEffect(() => {\n if (isExpanded()) {\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n // Focus the dialog\n setTimeout(() => dialogRef?.focus(), 10)\n onCleanup(() => {\n document.body.style.overflow = prev\n })\n }\n })\n\n /**\n * Did the gesture that produced the current click START on the backdrop?\n *\n * `null` means no pointer press was seen — a synthetic or programmatic\n * click, which stays allowed so keyboard and test-driven closes keep working.\n */\n let pressedOnBackdrop: boolean | null = null\n\n const handleBackdropPointerDown = (e: PointerEvent) => {\n pressedOnBackdrop = e.target === e.currentTarget\n }\n\n /**\n * Closing on a backdrop click is only safe when the gesture also BEGAN there.\n *\n * When a press and its release have different targets the browser dispatches\n * `click` on their nearest common ancestor — here the backdrop. So a drag\n * that starts on the content and releases over the 16px margin around the\n * panel (panning a zoomed chart, selecting text in a table) would otherwise\n * land on this handler with `e.target === e.currentTarget` and tear the modal\n * down mid-gesture.\n */\n const handleBackdropClick = (e: MouseEvent) => {\n const startedOnBackdrop = pressedOnBackdrop\n pressedOnBackdrop = null\n if (startedOnBackdrop === false) return\n if (e.target === e.currentTarget) handleClose()\n }\n\n const handleCopy = async () => {\n if (!props.copyData) return\n try {\n await navigator.clipboard.writeText(props.copyData)\n setCopied(true)\n setTimeout(() => setCopied(false), 2000)\n } catch (err) {\n console.error('Failed to copy:', err)\n }\n }\n\n return (\n <div class=\"relative group\">\n {/* Inline slot — content lives here when not expanded */}\n <div ref={inlineSlotRef}>\n <div ref={contentRef}>\n <ExpandedContext.Provider value={isExpanded}>\n {props.children}\n </ExpandedContext.Provider>\n </div>\n </div>\n\n {/* Expand button — visibility per `toolbarVariant` (default 'hover') */}\n <button\n type=\"button\"\n onClick={handleOpen}\n class={`absolute top-2 right-2 z-10 ${\n props.toolbarVariant === 'always-visible'\n ? 'opacity-60 hover:opacity-100'\n : 'opacity-0 group-hover:opacity-70 hover:!opacity-100'\n } p-1.5 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm`}\n title={strings.expand}\n aria-label={strings.expand}\n data-mcp-ui-action=\"expand\"\n >\n <svg class=\"w-3.5 h-3.5 text-gray-500 dark:text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5v-4m0 4h-4m4 0l-5-5\" />\n </svg>\n </button>\n\n {/* Fullscreen modal via Portal */}\n <Show when={isExpanded()}>\n <Portal>\n <div\n class=\"fixed inset-0 z-50 flex flex-col bg-black/50 backdrop-blur-sm\"\n style={{ animation: 'expandable-fade-in 0.15s ease-out' }}\n onClick={handleBackdropClick}\n onPointerDown={handleBackdropPointerDown}\n role=\"dialog\"\n data-mcp-ui-portal=\"dialog\"\n aria-modal=\"true\"\n aria-label={props.title || strings.expandedView}\n tabIndex={-1}\n ref={dialogRef}\n >\n {/* Modal panel */}\n <div\n class=\"relative flex flex-col m-4 flex-1 bg-white dark:bg-gray-800 rounded-lg shadow-xl overflow-hidden\"\n style={{ animation: 'expandable-scale-in 0.15s ease-out' }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div class=\"flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex-shrink-0\">\n <h2 class=\"text-lg font-semibold text-gray-900 dark:text-white truncate\">\n {props.title || strings.expandedView}\n </h2>\n <div class=\"flex items-center gap-2\">\n {/* Copy button */}\n <Show when={props.copyData}>\n <button\n type=\"button\"\n onClick={handleCopy}\n class=\"p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n title={props.copyLabel || strings.copyToClipboard}\n aria-label={props.copyLabel || strings.copyToClipboard}\n data-mcp-ui-action=\"copy\"\n >\n <Show\n when={!copied()}\n fallback={\n <svg class=\"w-5 h-5 text-green-500\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\" />\n </svg>\n }\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" 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 </svg>\n </Show>\n </button>\n </Show>\n {/* Close button */}\n <button\n type=\"button\"\n onClick={handleClose}\n class=\"p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n aria-label={strings.closeExpandedView}\n data-mcp-ui-action=\"close\"\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n </div>\n\n {/* Modal slot — content is reparented here when expanded.\n v6.1.0 : `flex flex-col` lets aware children opt into\n `flex-1 min-h-0` to fill the modal vertically (chart,\n table, map, graph). Unaware children keep working\n thanks to `overflow-auto` (their natural height\n scrolls if it overflows the slot). */}\n <div class=\"flex-1 min-h-0 overflow-auto p-4 flex flex-col\" ref={modalSlotRef} />\n </div>\n </div>\n\n <style>{`\n @keyframes expandable-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes expandable-scale-in {\n from { opacity: 0; transform: scale(0.97); }\n to { opacity: 1; transform: scale(1); }\n }\n `}</style>\n </Portal>\n </Show>\n </div>\n )\n}\n"],"names":["ExpandedContext","createContext","useExpanded","useContext","ExpandableWrapper","props","isExpanded","setIsExpanded","createSignal","copied","setCopied","strings","useMCPUIStrings","dialogRef","contentRef","inlineSlotRef","modalSlotRef","handleOpen","handleClose","createEffect","onExpandedChange","appendChild","onKeyDown","e","key","preventDefault","document","addEventListener","onCleanup","removeEventListener","prev","body","style","overflow","setTimeout","focus","pressedOnBackdrop","handleBackdropPointerDown","target","currentTarget","handleBackdropClick","startedOnBackdrop","handleCopy","copyData","navigator","clipboard","writeText","err","console","error","_el$","_$getNextElement","_tmpl$5","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$15","_el$16","_co$2","_$getNextMarker","_ref$","_$use","_ref$2","_$insert","_$createComponent","Provider","value","children","$$click","Show","when","Portal","_el$5","_tmpl$3","_el$6","_el$7","_el$8","_el$9","_el$11","_el$12","_co$","_el$10","_el$13","_ref$3","$$pointerdown","stopPropagation","title","expandedView","_el$0","_tmpl$2","fallback","_tmpl$6","_tmpl$","_$effect","_p$","_v$","copyLabel","copyToClipboard","_v$2","_$setAttribute","t","undefined","_$runHydrationEvents","_ref$4","_v$3","_v$4","closeExpandedView","_tmpl$4","_v$5","toolbarVariant","_v$6","expand","_v$7","_$className","a","_$delegateEvents"],"mappings":"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"}