@seed-ship/mcp-ui-solid 6.8.2 → 6.10.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 (41) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/dist/components/ChartJSRenderer.cjs +27 -13
  3. package/dist/components/ChartJSRenderer.cjs.map +1 -1
  4. package/dist/components/ChartJSRenderer.d.ts.map +1 -1
  5. package/dist/components/ChartJSRenderer.js +28 -14
  6. package/dist/components/ChartJSRenderer.js.map +1 -1
  7. package/dist/components/DegradedFallback.cjs +73 -0
  8. package/dist/components/DegradedFallback.cjs.map +1 -0
  9. package/dist/components/DegradedFallback.d.ts +37 -0
  10. package/dist/components/DegradedFallback.d.ts.map +1 -0
  11. package/dist/components/DegradedFallback.js +73 -0
  12. package/dist/components/DegradedFallback.js.map +1 -0
  13. package/dist/components/GraphRenderer.cjs +30 -15
  14. package/dist/components/GraphRenderer.cjs.map +1 -1
  15. package/dist/components/GraphRenderer.d.ts.map +1 -1
  16. package/dist/components/GraphRenderer.js +31 -16
  17. package/dist/components/GraphRenderer.js.map +1 -1
  18. package/dist/components/MapRenderer.cjs +132 -110
  19. package/dist/components/MapRenderer.cjs.map +1 -1
  20. package/dist/components/MapRenderer.d.ts +37 -1
  21. package/dist/components/MapRenderer.d.ts.map +1 -1
  22. package/dist/components/MapRenderer.js +134 -112
  23. package/dist/components/MapRenderer.js.map +1 -1
  24. package/dist/index.cjs +4 -4
  25. package/dist/index.js +1 -1
  26. package/dist/utils/degraded-projections.cjs +87 -0
  27. package/dist/utils/degraded-projections.cjs.map +1 -0
  28. package/dist/utils/degraded-projections.d.ts +64 -0
  29. package/dist/utils/degraded-projections.d.ts.map +1 -0
  30. package/dist/utils/degraded-projections.js +87 -0
  31. package/dist/utils/degraded-projections.js.map +1 -0
  32. package/package.json +1 -1
  33. package/src/components/ChartJSRenderer.tsx +94 -85
  34. package/src/components/DegradedFallback.test.tsx +61 -0
  35. package/src/components/DegradedFallback.tsx +93 -0
  36. package/src/components/GraphRenderer.tsx +26 -4
  37. package/src/components/MapRenderer.security.test.ts +83 -0
  38. package/src/components/MapRenderer.tsx +502 -392
  39. package/src/utils/degraded-projections.test.ts +113 -0
  40. package/src/utils/degraded-projections.ts +149 -0
  41. package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,77 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [6.10.0] - 2026-05-31
9
+
10
+ ### Security — map popups/tooltips are text-safe by default (XSS hardening)
11
+
12
+ Leaflet renders bound tooltip/popup strings as **HTML**, so `marker.tooltip`,
13
+ `marker.popup` and a GeoJSON `popup.template` were an **XSS vector** when the
14
+ payload is untrusted — the default for an LLM/connector-driven public package
15
+ (audit `docs/briefs/AUDIT-2026-05-30-visual-renderers-g6-ontology.md`, P1.2).
16
+
17
+ `<MapRenderer>` now treats that content as **plain text by default**:
18
+
19
+ - `marker.tooltip` / `marker.popup` are HTML-escaped before binding.
20
+ - a GeoJSON `popup.template` (raw HTML) is **ignored** on the default path;
21
+ the safe auto-generated popup (`titleField` / `fields`, values already
22
+ escaped) is used instead.
23
+ - substituted `{{prop}}` values in a template are escaped even on the
24
+ trusted path (they are data, not markup).
25
+
26
+ A **host** that renders `<MapRenderer>` with trusted data can restore rich
27
+ HTML popups via the new `allowHtmlPopups` prop. This is deliberately a
28
+ host-level prop, **not** a payload field — a malicious payload could just set
29
+ its own flag. The `<UIResourceRenderer>` path never sets it, so every
30
+ payload-driven map is text-safe.
31
+
32
+ **Behavior change:** if you previously relied on HTML inside
33
+ `marker.tooltip` / `marker.popup` / `popup.template`, pass `allowHtmlPopups`
34
+ to `<MapRenderer>` (or migrate that markup into the structured
35
+ `titleField` / `fields` popup config). Maps that pass plain-text popups —
36
+ the common case — are visually unchanged.
37
+
38
+ ### Tests
39
+
40
+ - `MapRenderer.security.test.ts` — 14 cases via new exported pure helpers
41
+ `popupSafeText` and `buildPopupContent`: escapes by default, passes raw
42
+ HTML only when opted in, template gated on the untrusted path, auto-popup
43
+ always escapes values.
44
+
45
+ ## [6.9.0] - 2026-05-31
46
+
47
+ ### Added — renderer fallback ladder (no silent blanks)
48
+
49
+ Every heavy renderer now follows the same three-rung contract (audit
50
+ `docs/briefs/AUDIT-2026-05-30-visual-renderers-g6-ontology.md`, P2.5):
51
+
52
+ 1. **native** render when the peer lib is available and succeeds;
53
+ 2. **degraded but useful** view when the native render throws — a visible
54
+ notice plus a plain data table, so the user still sees the underlying
55
+ data instead of a blank space;
56
+ 3. a `component:error` **telemetry** event (`detail.degraded = true`) so the
57
+ failure is observable by hosts that mount `<MCPUITelemetryProvider>`.
58
+
59
+ Previously a render-path failure left an empty canvas / blank container
60
+ (graph, chart) or a bare red error string (map). Now:
61
+
62
+ - **`graph`** (`<GraphRenderer>`) — on G6 render error, degrades to an
63
+ edge table (Source / Target / Label) or, when there are no edges, a node
64
+ list. The export menu (PNG / Mermaid / JSON) stays usable.
65
+ - **`map`** (`<MapRenderer>`) — the Leaflet drawing block is now wrapped;
66
+ on failure it degrades to a coordinate table (markers + GeoJSON features
67
+ → Type / Lat / Lng / Info).
68
+ - **`chart`** (`<ChartJSRenderer>`) — on Chart.js render error, degrades to
69
+ a series table (labels × datasets) instead of a blank canvas.
70
+
71
+ New exports: `DegradedFallback` component (+ `DegradedFallbackProps`) for
72
+ hosts that want to build consistent fallbacks of their own. New pure
73
+ projection helpers (`graphToDegradedTable`, `mapToDegradedTable`,
74
+ `chartToDegradedTable`) under `src/utils/degraded-projections`.
75
+
76
+ `source_card` is intentionally out of scope here — it is not yet a real
77
+ `UIResourceRenderer` type (tracked separately as audit P2.6).
78
+
8
79
  ## [6.8.2] - 2026-05-30
9
80
 
10
81
  ### Fixed — `type:'map'` rejected when it renders purely from GeoJSON
@@ -25,7 +25,10 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
25
25
  const web = require("solid-js/web");
26
26
  const solidJs = require("solid-js");
27
27
  const ExpandableWrapper = require("./ExpandableWrapper.cjs");
28
- var _tmpl$ = /* @__PURE__ */ web.template(`<h3 class="text-sm font-semibold text-gray-900 dark:text-white">`), _tmpl$2 = /* @__PURE__ */ web.template(`<button class="opacity-0 group-hover: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"title="Download PNG"aria-label="Download chart as PNG"><svg class="w-3 h-3 text-gray-500 dark:text-gray-400"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 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">`), _tmpl$3 = /* @__PURE__ */ web.template(`<div class="flex items-center justify-between mb-3 flex-shrink-0"><!$><!/><!$><!/>`), _tmpl$4 = /* @__PURE__ */ web.template(`<div class="absolute inset-0 flex items-center justify-center bg-white/80 dark:bg-gray-800/80"><div class="flex flex-col items-center gap-2"><div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div><span class="text-sm text-gray-500 dark:text-gray-400">Loading chart...`), _tmpl$5 = /* @__PURE__ */ web.template(`<div class="absolute inset-0 flex items-center justify-center p-4 bg-white dark:bg-gray-800"><div class=text-center><div class="inline-flex items-center justify-center w-12 h-12 rounded-full bg-red-100 dark:bg-red-900/20 mb-3"><svg class="w-6 h-6 text-red-600 dark:text-red-400"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg></div><p class="text-red-600 dark:text-red-400 text-sm font-medium">Chart Error</p><p class="text-gray-600 dark:text-gray-400 text-xs mt-1 max-w-xs">`), _tmpl$6 = /* @__PURE__ */ web.template(`<div><!$><!/><!$><!/><!$><!/><div><canvas>`);
28
+ const DegradedFallback = require("./DegradedFallback.cjs");
29
+ const degradedProjections = require("../utils/degraded-projections.cjs");
30
+ const MCPUITelemetryContext = require("../context/MCPUITelemetryContext.cjs");
31
+ var _tmpl$ = /* @__PURE__ */ web.template(`<h3 class="text-sm font-semibold text-gray-900 dark:text-white">`), _tmpl$2 = /* @__PURE__ */ web.template(`<button class="opacity-0 group-hover: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"title="Download PNG"aria-label="Download chart as PNG"><svg class="w-3 h-3 text-gray-500 dark:text-gray-400"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 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">`), _tmpl$3 = /* @__PURE__ */ web.template(`<div class="flex items-center justify-between mb-3 flex-shrink-0"><!$><!/><!$><!/>`), _tmpl$4 = /* @__PURE__ */ web.template(`<div class="absolute inset-0 flex items-center justify-center bg-white/80 dark:bg-gray-800/80"><div class="flex flex-col items-center gap-2"><div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div><span class="text-sm text-gray-500 dark:text-gray-400">Loading chart...`), _tmpl$5 = /* @__PURE__ */ web.template(`<div><!$><!/><!$><!/><!$><!/><div><canvas>`);
29
32
  let ChartJS = null;
30
33
  let chartJSLoadPromise = null;
31
34
  const loadChartJS = async () => {
@@ -56,6 +59,7 @@ const ChartJSRenderer = (props) => {
56
59
  let chartInstance;
57
60
  const params = () => props.component.params;
58
61
  const isExpanded = ExpandableWrapper.useExpanded();
62
+ const telemetry = MCPUITelemetryContext.useTelemetry();
59
63
  const exportEnabled = () => params().exportable !== false;
60
64
  const copyDataJSON = () => JSON.stringify({
61
65
  type: params().type,
@@ -70,7 +74,7 @@ const ChartJSRenderer = (props) => {
70
74
  a.click();
71
75
  };
72
76
  solidJs.createEffect(async () => {
73
- var _a, _b, _c, _d, _e;
77
+ var _a, _b, _c, _d, _e, _f;
74
78
  if (!canvasRef) return;
75
79
  const chartParams = params();
76
80
  setIsLoading(true);
@@ -125,7 +129,14 @@ const ChartJSRenderer = (props) => {
125
129
  const error2 = err instanceof Error ? err : new Error("Chart rendering failed");
126
130
  setError(error2.message);
127
131
  setIsLoading(false);
128
- (_e = props.onError) == null ? void 0 : _e.call(props, error2);
132
+ telemetry == null ? void 0 : telemetry.dispatch({
133
+ type: "render:error",
134
+ errorMessage: error2.message,
135
+ id: ((_e = props.component) == null ? void 0 : _e.id) ?? "",
136
+ componentType: "chart",
137
+ ts: Date.now()
138
+ });
139
+ (_f = props.onError) == null ? void 0 : _f.call(props, error2);
129
140
  }
130
141
  });
131
142
  solidJs.onCleanup(() => {
@@ -146,7 +157,7 @@ const ChartJSRenderer = (props) => {
146
157
  return props.toolbarVariant;
147
158
  },
148
159
  get children() {
149
- var _el$ = web.getNextElement(_tmpl$6), _el$15 = _el$.firstChild, [_el$16, _co$3] = web.getNextMarker(_el$15.nextSibling), _el$17 = _el$16.nextSibling, [_el$18, _co$4] = web.getNextMarker(_el$17.nextSibling), _el$19 = _el$18.nextSibling, [_el$20, _co$5] = web.getNextMarker(_el$19.nextSibling), _el$13 = _el$20.nextSibling, _el$14 = _el$13.firstChild;
160
+ var _el$ = web.getNextElement(_tmpl$5), _el$10 = _el$.firstChild, [_el$11, _co$3] = web.getNextMarker(_el$10.nextSibling), _el$12 = _el$11.nextSibling, [_el$13, _co$4] = web.getNextMarker(_el$12.nextSibling), _el$14 = _el$13.nextSibling, [_el$15, _co$5] = web.getNextMarker(_el$14.nextSibling), _el$0 = _el$15.nextSibling, _el$1 = _el$0.firstChild;
150
161
  web.insert(_el$, web.createComponent(solidJs.Show, {
151
162
  get when() {
152
163
  return params().title || exportEnabled();
@@ -176,7 +187,7 @@ const ChartJSRenderer = (props) => {
176
187
  }), _el$8, _co$2);
177
188
  return _el$2;
178
189
  }
179
- }), _el$16, _co$3);
190
+ }), _el$11, _co$3);
180
191
  web.insert(_el$, web.createComponent(solidJs.Show, {
181
192
  get when() {
182
193
  return isLoading();
@@ -184,19 +195,22 @@ const ChartJSRenderer = (props) => {
184
195
  get children() {
185
196
  return web.getNextElement(_tmpl$4);
186
197
  }
187
- }), _el$18, _co$4);
198
+ }), _el$13, _co$4);
188
199
  web.insert(_el$, web.createComponent(solidJs.Show, {
189
200
  get when() {
190
201
  return error();
191
202
  },
192
203
  get children() {
193
- var _el$0 = web.getNextElement(_tmpl$5), _el$1 = _el$0.firstChild, _el$10 = _el$1.firstChild, _el$11 = _el$10.nextSibling, _el$12 = _el$11.nextSibling;
194
- web.insert(_el$12, error);
195
- return _el$0;
204
+ return web.createComponent(DegradedFallback.DegradedFallback, web.mergeProps({
205
+ get message() {
206
+ return `Chart rendering failed: ${error()}`;
207
+ },
208
+ caption: "Showing the chart data as a table — the interactive chart is unavailable."
209
+ }, () => degradedProjections.chartToDegradedTable(params() ?? {})));
196
210
  }
197
- }), _el$20, _co$5);
211
+ }), _el$15, _co$5);
198
212
  var _ref$ = canvasRef;
199
- typeof _ref$ === "function" ? web.use(_ref$, _el$14) : canvasRef = _el$14;
213
+ typeof _ref$ === "function" ? web.use(_ref$, _el$1) : canvasRef = _el$1;
200
214
  web.effect((_p$) => {
201
215
  var _v$ = `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 ${isExpanded() ? "flex-1 min-h-0 flex flex-col" : ""}`, _v$2 = `w-full ${isExpanded() ? "flex-1 min-h-0" : ""}`, _v$3 = error() ? {
202
216
  display: "none"
@@ -208,8 +222,8 @@ const ChartJSRenderer = (props) => {
208
222
  display: "block"
209
223
  };
210
224
  _v$ !== _p$.e && web.className(_el$, _p$.e = _v$);
211
- _v$2 !== _p$.t && web.className(_el$13, _p$.t = _v$2);
212
- _p$.a = web.style(_el$13, _v$3, _p$.a);
225
+ _v$2 !== _p$.t && web.className(_el$0, _p$.t = _v$2);
226
+ _p$.a = web.style(_el$0, _v$3, _p$.a);
213
227
  return _p$;
214
228
  }, {
215
229
  e: void 0,
@@ -1 +1 @@
1
- {"version":3,"file":"ChartJSRenderer.cjs","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 { Component, createEffect, onCleanup, createSignal, Show } from 'solid-js'\nimport type { UIComponent, ChartComponentParams } from '../types'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\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\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 let canvasRef: HTMLCanvasElement | undefined\n let chartInstance: any\n\n const params = () => props.component.params as ChartComponentParams\n const isExpanded = useExpanded()\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\n setIsLoading(true)\n setError(undefined)\n\n try {\n const Chart = await loadChartJS()\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 const error = err instanceof Error ? err : new Error('Chart rendering failed')\n setError(error.message)\n setIsLoading(false)\n props.onError?.(error)\n }\n })\n\n // Cleanup on unmount\n onCleanup(() => {\n if (chartInstance) {\n chartInstance.destroy()\n chartInstance = null\n }\n })\n\n return (\n <ExpandableWrapper\n title={params().title || 'Chart'}\n copyData={copyDataJSON()}\n copyLabel=\"Copy chart data (JSON)\"\n toolbarVariant={props.toolbarVariant}\n >\n <div 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 }`}>\n <Show when={params().title || exportEnabled()}>\n <div class=\"flex items-center justify-between mb-3 flex-shrink-0\">\n <Show when={params().title}>\n <h3 class=\"text-sm font-semibold text-gray-900 dark:text-white\">\n {params().title}\n </h3>\n </Show>\n <Show when={exportEnabled()}>\n <button\n onClick={handleExportPNG}\n class=\"opacity-0 group-hover: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=\"Download PNG\"\n aria-label=\"Download chart as PNG\"\n >\n <svg class=\"w-3 h-3 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=\"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 </svg>\n </button>\n </Show>\n </div>\n </Show>\n\n <Show when={isLoading()}>\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\">Loading chart...</span>\n </div>\n </div>\n </Show>\n\n <Show when={error()}>\n <div class=\"absolute inset-0 flex items-center justify-center p-4 bg-white dark:bg-gray-800\">\n <div class=\"text-center\">\n <div class=\"inline-flex items-center justify-center w-12 h-12 rounded-full bg-red-100 dark:bg-red-900/20 mb-3\">\n <svg\n class=\"w-6 h-6 text-red-600 dark:text-red-400\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <p class=\"text-red-600 dark:text-red-400 text-sm font-medium\">Chart Error</p>\n <p class=\"text-gray-600 dark:text-gray-400 text-xs mt-1 max-w-xs\">{error()}</p>\n </div>\n </div>\n </Show>\n\n <div\n class={`w-full ${isExpanded() ? 'flex-1 min-h-0' : ''}`}\n style={\n error()\n ? { display: 'none' }\n : isExpanded()\n ? { height: '100%', display: 'block' }\n : { height: params().height || '250px', display: 'block' }\n }\n >\n <canvas ref={canvasRef} />\n </div>\n </div>\n </ExpandableWrapper>\n )\n}\n"],"names":["ChartJS","chartJSLoadPromise","loadChartJS","then","module","default","Chart","catch","err","isChartJSAvailable","ChartJSRenderer","props","isLoading","setIsLoading","createSignal","error","setError","canvasRef","chartInstance","params","component","isExpanded","useExpanded","exportEnabled","exportable","copyDataJSON","JSON","stringify","type","data","handleExportPNG","url","toDataURL","a","document","createElement","href","download","title","replace","toLowerCase","click","createEffect","chartParams","undefined","destroy","baseOptions","responsive","maintainAspectRatio","options","plugins","legend","display","position","timeAxis","ta","scales","x","time","parser","unit","tooltipFormat","min","max","Error","message","onError","onCleanup","_$createComponent","ExpandableWrapper","copyData","copyLabel","toolbarVariant","children","_el$","_$getNextElement","_tmpl$6","_el$15","firstChild","_el$16","_co$3","_$getNextMarker","nextSibling","_el$17","_el$18","_co$4","_el$19","_el$20","_co$5","_el$13","_el$14","_$insert","Show","when","_el$2","_tmpl$3","_el$5","_el$6","_co$","_el$7","_el$8","_co$2","_el$3","_tmpl$","_el$4","_tmpl$2","$$click","_$runHydrationEvents","_tmpl$4","_el$0","_tmpl$5","_el$1","_el$10","_el$11","_el$12","_ref$","_$use","_$effect","_p$","_v$","_v$2","_v$3","height","e","_$className","t","_$style","_$delegateEvents"],"mappings":"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"}
1
+ {"version":3,"file":"ChartJSRenderer.cjs","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 { Component, createEffect, onCleanup, createSignal, Show } from 'solid-js';\nimport type { UIComponent, ChartComponentParams } from '../types';\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper';\nimport { DegradedFallback } from './DegradedFallback';\nimport { chartToDegradedTable } from '../utils/degraded-projections';\nimport { useTelemetry } from '../context/MCPUITelemetryContext';\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\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 let canvasRef: HTMLCanvasElement | undefined;\n let chartInstance: any;\n\n const params = () => props.component.params as ChartComponentParams;\n const isExpanded = useExpanded();\n const telemetry = useTelemetry();\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\n setIsLoading(true);\n setError(undefined);\n\n try {\n const Chart = await loadChartJS();\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 const error = err instanceof Error ? err : new Error('Chart rendering failed');\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 // Cleanup on unmount\n onCleanup(() => {\n if (chartInstance) {\n chartInstance.destroy();\n chartInstance = null;\n }\n });\n\n return (\n <ExpandableWrapper\n title={params().title || 'Chart'}\n copyData={copyDataJSON()}\n copyLabel=\"Copy chart data (JSON)\"\n toolbarVariant={props.toolbarVariant}\n >\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 }`}\n >\n <Show when={params().title || exportEnabled()}>\n <div class=\"flex items-center justify-between mb-3 flex-shrink-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 <Show when={exportEnabled()}>\n <button\n onClick={handleExportPNG}\n class=\"opacity-0 group-hover: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=\"Download PNG\"\n aria-label=\"Download chart as PNG\"\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 </Show>\n\n <Show when={isLoading()}>\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\">Loading chart...</span>\n </div>\n </div>\n </Show>\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()}>\n <DegradedFallback\n message={`Chart rendering failed: ${error()}`}\n caption=\"Showing the chart data as a table — the interactive chart is unavailable.\"\n {...chartToDegradedTable(params() ?? {})}\n />\n </Show>\n\n <div\n class={`w-full ${isExpanded() ? 'flex-1 min-h-0' : ''}`}\n style={\n error()\n ? { display: 'none' }\n : isExpanded()\n ? { height: '100%', display: 'block' }\n : { height: params().height || '250px', display: 'block' }\n }\n >\n <canvas ref={canvasRef} />\n </div>\n </div>\n </ExpandableWrapper>\n );\n};\n"],"names":["ChartJS","chartJSLoadPromise","loadChartJS","then","module","default","Chart","catch","err","isChartJSAvailable","ChartJSRenderer","props","isLoading","setIsLoading","createSignal","error","setError","canvasRef","chartInstance","params","component","isExpanded","useExpanded","telemetry","useTelemetry","exportEnabled","exportable","copyDataJSON","JSON","stringify","type","data","handleExportPNG","url","toDataURL","a","document","createElement","href","download","title","replace","toLowerCase","click","createEffect","chartParams","undefined","destroy","baseOptions","responsive","maintainAspectRatio","options","plugins","legend","display","position","timeAxis","ta","scales","x","time","parser","unit","tooltipFormat","min","max","Error","message","dispatch","errorMessage","id","componentType","ts","Date","now","onError","onCleanup","_$createComponent","ExpandableWrapper","copyData","copyLabel","toolbarVariant","children","_el$","_$getNextElement","_tmpl$5","_el$10","firstChild","_el$11","_co$3","_$getNextMarker","nextSibling","_el$12","_el$13","_co$4","_el$14","_el$15","_co$5","_el$0","_el$1","_$insert","Show","when","_el$2","_tmpl$3","_el$5","_el$6","_co$","_el$7","_el$8","_co$2","_el$3","_tmpl$","_el$4","_tmpl$2","$$click","_$runHydrationEvents","_tmpl$4","DegradedFallback","_$mergeProps","caption","chartToDegradedTable","_ref$","_$use","_$effect","_p$","_v$","_v$2","_v$3","height","e","_$className","t","_$style","_$delegateEvents"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ChartJSRenderer.d.ts","sourceRoot":"","sources":["../../src/components/ChartJSRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAA+C,MAAM,UAAU,CAAA;AACjF,OAAO,KAAK,EAAE,WAAW,EAAwB,MAAM,UAAU,CAAA;AAyBjE;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC,CAO3D;AAED,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,SAAS,EAAE,WAAW,CAAA;IAEtB;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAEhC;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAA;CAC5C;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,CAAC,oBAAoB,CAyL3D,CAAA"}
1
+ {"version":3,"file":"ChartJSRenderer.d.ts","sourceRoot":"","sources":["../../src/components/ChartJSRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAA+C,MAAM,UAAU,CAAC;AAClF,OAAO,KAAK,EAAE,WAAW,EAAwB,MAAM,UAAU,CAAC;AA4BlE;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC,CAO3D;AAED,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,SAAS,EAAE,WAAW,CAAC;IAEvB;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAC;CAC7C;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,CAAC,oBAAoB,CA+L3D,CAAC"}
@@ -1,7 +1,10 @@
1
- import { delegateEvents, createComponent, getNextElement, template, getNextMarker, insert, runHydrationEvents, effect, className, style, use } from "solid-js/web";
1
+ import { delegateEvents, createComponent, getNextElement, template, getNextMarker, insert, runHydrationEvents, mergeProps, effect, className, style, use } from "solid-js/web";
2
2
  import { createSignal, createEffect, onCleanup, Show } from "solid-js";
3
3
  import { useExpanded, ExpandableWrapper } from "./ExpandableWrapper.js";
4
- var _tmpl$ = /* @__PURE__ */ template(`<h3 class="text-sm font-semibold text-gray-900 dark:text-white">`), _tmpl$2 = /* @__PURE__ */ template(`<button class="opacity-0 group-hover: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"title="Download PNG"aria-label="Download chart as PNG"><svg class="w-3 h-3 text-gray-500 dark:text-gray-400"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 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">`), _tmpl$3 = /* @__PURE__ */ template(`<div class="flex items-center justify-between mb-3 flex-shrink-0"><!$><!/><!$><!/>`), _tmpl$4 = /* @__PURE__ */ template(`<div class="absolute inset-0 flex items-center justify-center bg-white/80 dark:bg-gray-800/80"><div class="flex flex-col items-center gap-2"><div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div><span class="text-sm text-gray-500 dark:text-gray-400">Loading chart...`), _tmpl$5 = /* @__PURE__ */ template(`<div class="absolute inset-0 flex items-center justify-center p-4 bg-white dark:bg-gray-800"><div class=text-center><div class="inline-flex items-center justify-center w-12 h-12 rounded-full bg-red-100 dark:bg-red-900/20 mb-3"><svg class="w-6 h-6 text-red-600 dark:text-red-400"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg></div><p class="text-red-600 dark:text-red-400 text-sm font-medium">Chart Error</p><p class="text-gray-600 dark:text-gray-400 text-xs mt-1 max-w-xs">`), _tmpl$6 = /* @__PURE__ */ template(`<div><!$><!/><!$><!/><!$><!/><div><canvas>`);
4
+ import { DegradedFallback } from "./DegradedFallback.js";
5
+ import { chartToDegradedTable } from "../utils/degraded-projections.js";
6
+ import { useTelemetry } from "../context/MCPUITelemetryContext.js";
7
+ var _tmpl$ = /* @__PURE__ */ template(`<h3 class="text-sm font-semibold text-gray-900 dark:text-white">`), _tmpl$2 = /* @__PURE__ */ template(`<button class="opacity-0 group-hover: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"title="Download PNG"aria-label="Download chart as PNG"><svg class="w-3 h-3 text-gray-500 dark:text-gray-400"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 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">`), _tmpl$3 = /* @__PURE__ */ template(`<div class="flex items-center justify-between mb-3 flex-shrink-0"><!$><!/><!$><!/>`), _tmpl$4 = /* @__PURE__ */ template(`<div class="absolute inset-0 flex items-center justify-center bg-white/80 dark:bg-gray-800/80"><div class="flex flex-col items-center gap-2"><div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div><span class="text-sm text-gray-500 dark:text-gray-400">Loading chart...`), _tmpl$5 = /* @__PURE__ */ template(`<div><!$><!/><!$><!/><!$><!/><div><canvas>`);
5
8
  let ChartJS = null;
6
9
  let chartJSLoadPromise = null;
7
10
  const loadChartJS = async () => {
@@ -32,6 +35,7 @@ const ChartJSRenderer = (props) => {
32
35
  let chartInstance;
33
36
  const params = () => props.component.params;
34
37
  const isExpanded = useExpanded();
38
+ const telemetry = useTelemetry();
35
39
  const exportEnabled = () => params().exportable !== false;
36
40
  const copyDataJSON = () => JSON.stringify({
37
41
  type: params().type,
@@ -46,7 +50,7 @@ const ChartJSRenderer = (props) => {
46
50
  a.click();
47
51
  };
48
52
  createEffect(async () => {
49
- var _a, _b, _c, _d, _e;
53
+ var _a, _b, _c, _d, _e, _f;
50
54
  if (!canvasRef) return;
51
55
  const chartParams = params();
52
56
  setIsLoading(true);
@@ -101,7 +105,14 @@ const ChartJSRenderer = (props) => {
101
105
  const error2 = err instanceof Error ? err : new Error("Chart rendering failed");
102
106
  setError(error2.message);
103
107
  setIsLoading(false);
104
- (_e = props.onError) == null ? void 0 : _e.call(props, error2);
108
+ telemetry == null ? void 0 : telemetry.dispatch({
109
+ type: "render:error",
110
+ errorMessage: error2.message,
111
+ id: ((_e = props.component) == null ? void 0 : _e.id) ?? "",
112
+ componentType: "chart",
113
+ ts: Date.now()
114
+ });
115
+ (_f = props.onError) == null ? void 0 : _f.call(props, error2);
105
116
  }
106
117
  });
107
118
  onCleanup(() => {
@@ -122,7 +133,7 @@ const ChartJSRenderer = (props) => {
122
133
  return props.toolbarVariant;
123
134
  },
124
135
  get children() {
125
- var _el$ = getNextElement(_tmpl$6), _el$15 = _el$.firstChild, [_el$16, _co$3] = getNextMarker(_el$15.nextSibling), _el$17 = _el$16.nextSibling, [_el$18, _co$4] = getNextMarker(_el$17.nextSibling), _el$19 = _el$18.nextSibling, [_el$20, _co$5] = getNextMarker(_el$19.nextSibling), _el$13 = _el$20.nextSibling, _el$14 = _el$13.firstChild;
136
+ var _el$ = getNextElement(_tmpl$5), _el$10 = _el$.firstChild, [_el$11, _co$3] = getNextMarker(_el$10.nextSibling), _el$12 = _el$11.nextSibling, [_el$13, _co$4] = getNextMarker(_el$12.nextSibling), _el$14 = _el$13.nextSibling, [_el$15, _co$5] = getNextMarker(_el$14.nextSibling), _el$0 = _el$15.nextSibling, _el$1 = _el$0.firstChild;
126
137
  insert(_el$, createComponent(Show, {
127
138
  get when() {
128
139
  return params().title || exportEnabled();
@@ -152,7 +163,7 @@ const ChartJSRenderer = (props) => {
152
163
  }), _el$8, _co$2);
153
164
  return _el$2;
154
165
  }
155
- }), _el$16, _co$3);
166
+ }), _el$11, _co$3);
156
167
  insert(_el$, createComponent(Show, {
157
168
  get when() {
158
169
  return isLoading();
@@ -160,19 +171,22 @@ const ChartJSRenderer = (props) => {
160
171
  get children() {
161
172
  return getNextElement(_tmpl$4);
162
173
  }
163
- }), _el$18, _co$4);
174
+ }), _el$13, _co$4);
164
175
  insert(_el$, createComponent(Show, {
165
176
  get when() {
166
177
  return error();
167
178
  },
168
179
  get children() {
169
- var _el$0 = getNextElement(_tmpl$5), _el$1 = _el$0.firstChild, _el$10 = _el$1.firstChild, _el$11 = _el$10.nextSibling, _el$12 = _el$11.nextSibling;
170
- insert(_el$12, error);
171
- return _el$0;
180
+ return createComponent(DegradedFallback, mergeProps({
181
+ get message() {
182
+ return `Chart rendering failed: ${error()}`;
183
+ },
184
+ caption: "Showing the chart data as a table — the interactive chart is unavailable."
185
+ }, () => chartToDegradedTable(params() ?? {})));
172
186
  }
173
- }), _el$20, _co$5);
187
+ }), _el$15, _co$5);
174
188
  var _ref$ = canvasRef;
175
- typeof _ref$ === "function" ? use(_ref$, _el$14) : canvasRef = _el$14;
189
+ typeof _ref$ === "function" ? use(_ref$, _el$1) : canvasRef = _el$1;
176
190
  effect((_p$) => {
177
191
  var _v$ = `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 ${isExpanded() ? "flex-1 min-h-0 flex flex-col" : ""}`, _v$2 = `w-full ${isExpanded() ? "flex-1 min-h-0" : ""}`, _v$3 = error() ? {
178
192
  display: "none"
@@ -184,8 +198,8 @@ const ChartJSRenderer = (props) => {
184
198
  display: "block"
185
199
  };
186
200
  _v$ !== _p$.e && className(_el$, _p$.e = _v$);
187
- _v$2 !== _p$.t && className(_el$13, _p$.t = _v$2);
188
- _p$.a = style(_el$13, _v$3, _p$.a);
201
+ _v$2 !== _p$.t && className(_el$0, _p$.t = _v$2);
202
+ _p$.a = style(_el$0, _v$3, _p$.a);
189
203
  return _p$;
190
204
  }, {
191
205
  e: void 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 { Component, createEffect, onCleanup, createSignal, Show } from 'solid-js'\nimport type { UIComponent, ChartComponentParams } from '../types'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\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\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 let canvasRef: HTMLCanvasElement | undefined\n let chartInstance: any\n\n const params = () => props.component.params as ChartComponentParams\n const isExpanded = useExpanded()\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\n setIsLoading(true)\n setError(undefined)\n\n try {\n const Chart = await loadChartJS()\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 const error = err instanceof Error ? err : new Error('Chart rendering failed')\n setError(error.message)\n setIsLoading(false)\n props.onError?.(error)\n }\n })\n\n // Cleanup on unmount\n onCleanup(() => {\n if (chartInstance) {\n chartInstance.destroy()\n chartInstance = null\n }\n })\n\n return (\n <ExpandableWrapper\n title={params().title || 'Chart'}\n copyData={copyDataJSON()}\n copyLabel=\"Copy chart data (JSON)\"\n toolbarVariant={props.toolbarVariant}\n >\n <div 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 }`}>\n <Show when={params().title || exportEnabled()}>\n <div class=\"flex items-center justify-between mb-3 flex-shrink-0\">\n <Show when={params().title}>\n <h3 class=\"text-sm font-semibold text-gray-900 dark:text-white\">\n {params().title}\n </h3>\n </Show>\n <Show when={exportEnabled()}>\n <button\n onClick={handleExportPNG}\n class=\"opacity-0 group-hover: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=\"Download PNG\"\n aria-label=\"Download chart as PNG\"\n >\n <svg class=\"w-3 h-3 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=\"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 </svg>\n </button>\n </Show>\n </div>\n </Show>\n\n <Show when={isLoading()}>\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\">Loading chart...</span>\n </div>\n </div>\n </Show>\n\n <Show when={error()}>\n <div class=\"absolute inset-0 flex items-center justify-center p-4 bg-white dark:bg-gray-800\">\n <div class=\"text-center\">\n <div class=\"inline-flex items-center justify-center w-12 h-12 rounded-full bg-red-100 dark:bg-red-900/20 mb-3\">\n <svg\n class=\"w-6 h-6 text-red-600 dark:text-red-400\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <p class=\"text-red-600 dark:text-red-400 text-sm font-medium\">Chart Error</p>\n <p class=\"text-gray-600 dark:text-gray-400 text-xs mt-1 max-w-xs\">{error()}</p>\n </div>\n </div>\n </Show>\n\n <div\n class={`w-full ${isExpanded() ? 'flex-1 min-h-0' : ''}`}\n style={\n error()\n ? { display: 'none' }\n : isExpanded()\n ? { height: '100%', display: 'block' }\n : { height: params().height || '250px', display: 'block' }\n }\n >\n <canvas ref={canvasRef} />\n </div>\n </div>\n </ExpandableWrapper>\n )\n}\n"],"names":["ChartJS","chartJSLoadPromise","loadChartJS","then","module","default","Chart","catch","err","isChartJSAvailable","ChartJSRenderer","props","isLoading","setIsLoading","createSignal","error","setError","canvasRef","chartInstance","params","component","isExpanded","useExpanded","exportEnabled","exportable","copyDataJSON","JSON","stringify","type","data","handleExportPNG","url","toDataURL","a","document","createElement","href","download","title","replace","toLowerCase","click","createEffect","chartParams","undefined","destroy","baseOptions","responsive","maintainAspectRatio","options","plugins","legend","display","position","timeAxis","ta","scales","x","time","parser","unit","tooltipFormat","min","max","Error","message","onError","onCleanup","_$createComponent","ExpandableWrapper","copyData","copyLabel","toolbarVariant","children","_el$","_$getNextElement","_tmpl$6","_el$15","firstChild","_el$16","_co$3","_$getNextMarker","nextSibling","_el$17","_el$18","_co$4","_el$19","_el$20","_co$5","_el$13","_el$14","_$insert","Show","when","_el$2","_tmpl$3","_el$5","_el$6","_co$","_el$7","_el$8","_co$2","_el$3","_tmpl$","_el$4","_tmpl$2","$$click","_$runHydrationEvents","_tmpl$4","_el$0","_tmpl$5","_el$1","_el$10","_el$11","_el$12","_ref$","_$use","_$effect","_p$","_v$","_v$2","_v$3","height","e","_$className","t","_$style","_$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\nimport { Component, createEffect, onCleanup, createSignal, Show } from 'solid-js';\nimport type { UIComponent, ChartComponentParams } from '../types';\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper';\nimport { DegradedFallback } from './DegradedFallback';\nimport { chartToDegradedTable } from '../utils/degraded-projections';\nimport { useTelemetry } from '../context/MCPUITelemetryContext';\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\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 let canvasRef: HTMLCanvasElement | undefined;\n let chartInstance: any;\n\n const params = () => props.component.params as ChartComponentParams;\n const isExpanded = useExpanded();\n const telemetry = useTelemetry();\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\n setIsLoading(true);\n setError(undefined);\n\n try {\n const Chart = await loadChartJS();\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 const error = err instanceof Error ? err : new Error('Chart rendering failed');\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 // Cleanup on unmount\n onCleanup(() => {\n if (chartInstance) {\n chartInstance.destroy();\n chartInstance = null;\n }\n });\n\n return (\n <ExpandableWrapper\n title={params().title || 'Chart'}\n copyData={copyDataJSON()}\n copyLabel=\"Copy chart data (JSON)\"\n toolbarVariant={props.toolbarVariant}\n >\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 }`}\n >\n <Show when={params().title || exportEnabled()}>\n <div class=\"flex items-center justify-between mb-3 flex-shrink-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 <Show when={exportEnabled()}>\n <button\n onClick={handleExportPNG}\n class=\"opacity-0 group-hover: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=\"Download PNG\"\n aria-label=\"Download chart as PNG\"\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 </Show>\n\n <Show when={isLoading()}>\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\">Loading chart...</span>\n </div>\n </div>\n </Show>\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()}>\n <DegradedFallback\n message={`Chart rendering failed: ${error()}`}\n caption=\"Showing the chart data as a table — the interactive chart is unavailable.\"\n {...chartToDegradedTable(params() ?? {})}\n />\n </Show>\n\n <div\n class={`w-full ${isExpanded() ? 'flex-1 min-h-0' : ''}`}\n style={\n error()\n ? { display: 'none' }\n : isExpanded()\n ? { height: '100%', display: 'block' }\n : { height: params().height || '250px', display: 'block' }\n }\n >\n <canvas ref={canvasRef} />\n </div>\n </div>\n </ExpandableWrapper>\n );\n};\n"],"names":["ChartJS","chartJSLoadPromise","loadChartJS","then","module","default","Chart","catch","err","isChartJSAvailable","ChartJSRenderer","props","isLoading","setIsLoading","createSignal","error","setError","canvasRef","chartInstance","params","component","isExpanded","useExpanded","telemetry","useTelemetry","exportEnabled","exportable","copyDataJSON","JSON","stringify","type","data","handleExportPNG","url","toDataURL","a","document","createElement","href","download","title","replace","toLowerCase","click","createEffect","chartParams","undefined","destroy","baseOptions","responsive","maintainAspectRatio","options","plugins","legend","display","position","timeAxis","ta","scales","x","time","parser","unit","tooltipFormat","min","max","Error","message","dispatch","errorMessage","id","componentType","ts","Date","now","onError","onCleanup","_$createComponent","ExpandableWrapper","copyData","copyLabel","toolbarVariant","children","_el$","_$getNextElement","_tmpl$5","_el$10","firstChild","_el$11","_co$3","_$getNextMarker","nextSibling","_el$12","_el$13","_co$4","_el$14","_el$15","_co$5","_el$0","_el$1","_$insert","Show","when","_el$2","_tmpl$3","_el$5","_el$6","_co$","_el$7","_el$8","_co$2","_el$3","_tmpl$","_el$4","_tmpl$2","$$click","_$runHydrationEvents","_tmpl$4","DegradedFallback","_$mergeProps","caption","chartToDegradedTable","_ref$","_$use","_$effect","_p$","_v$","_v$2","_v$3","height","e","_$className","t","_$style","_$delegateEvents"],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const web = require("solid-js/web");
4
+ const solidJs = require("solid-js");
5
+ var _tmpl$ = /* @__PURE__ */ web.template(`<div class="mt-2 max-h-64 overflow-auto rounded border border-amber-200 dark:border-amber-800"><table class="w-full border-collapse text-left text-xs"><thead class="sticky top-0 bg-amber-100 dark:bg-amber-900/40"><tr></tr></thead><tbody>`), _tmpl$2 = /* @__PURE__ */ web.template(`<p class="mt-1 text-[10px] text-amber-600 dark:text-amber-400">+<!$><!/> more rows not shown.`), _tmpl$3 = /* @__PURE__ */ web.template(`<div class="w-full rounded-lg border border-amber-200 bg-amber-50 p-3 dark:border-amber-800 dark:bg-amber-900/20"role=alert><p class="text-sm font-medium text-amber-900 dark:text-amber-100"></p><p class="mt-0.5 text-xs text-amber-700 dark:text-amber-300"></p><!$><!/>`), _tmpl$4 = /* @__PURE__ */ web.template(`<th class="px-2 py-1 font-medium text-amber-900 dark:text-amber-100">`), _tmpl$5 = /* @__PURE__ */ web.template(`<tr class="border-t border-amber-100 dark:border-amber-800/60">`), _tmpl$6 = /* @__PURE__ */ web.template(`<td class="px-2 py-1 text-amber-800 dark:text-amber-200">`);
6
+ const DegradedFallback = (props) => {
7
+ const maxRows = () => props.maxRows ?? 50;
8
+ const allRows = () => props.rows ?? [];
9
+ const shownRows = () => allRows().slice(0, maxRows());
10
+ const hiddenCount = () => Math.max(0, allRows().length - shownRows().length);
11
+ const hasTable = () => {
12
+ var _a;
13
+ return (((_a = props.columns) == null ? void 0 : _a.length) ?? 0) > 0 && allRows().length > 0;
14
+ };
15
+ return (() => {
16
+ var _el$ = web.getNextElement(_tmpl$3), _el$2 = _el$.firstChild, _el$3 = _el$2.nextSibling, _el$12 = _el$3.nextSibling, [_el$13, _co$2] = web.getNextMarker(_el$12.nextSibling);
17
+ web.insert(_el$2, () => props.message);
18
+ web.insert(_el$3, () => props.caption ?? "Showing the underlying data — the interactive view is unavailable.");
19
+ web.insert(_el$, web.createComponent(solidJs.Show, {
20
+ get when() {
21
+ return hasTable();
22
+ },
23
+ get children() {
24
+ return [(() => {
25
+ var _el$4 = web.getNextElement(_tmpl$), _el$5 = _el$4.firstChild, _el$6 = _el$5.firstChild, _el$7 = _el$6.firstChild, _el$8 = _el$6.nextSibling;
26
+ web.insert(_el$7, web.createComponent(solidJs.For, {
27
+ get each() {
28
+ return props.columns;
29
+ },
30
+ children: (col) => (() => {
31
+ var _el$14 = web.getNextElement(_tmpl$4);
32
+ web.insert(_el$14, col);
33
+ return _el$14;
34
+ })()
35
+ }));
36
+ web.insert(_el$8, web.createComponent(solidJs.For, {
37
+ get each() {
38
+ return shownRows();
39
+ },
40
+ children: (row) => (() => {
41
+ var _el$15 = web.getNextElement(_tmpl$5);
42
+ web.insert(_el$15, web.createComponent(solidJs.For, {
43
+ get each() {
44
+ return props.columns;
45
+ },
46
+ children: (_col, i) => (() => {
47
+ var _el$16 = web.getNextElement(_tmpl$6);
48
+ web.insert(_el$16, () => String(row[i()] ?? ""));
49
+ return _el$16;
50
+ })()
51
+ }));
52
+ return _el$15;
53
+ })()
54
+ }));
55
+ return _el$4;
56
+ })(), web.createComponent(solidJs.Show, {
57
+ get when() {
58
+ return hiddenCount() > 0;
59
+ },
60
+ get children() {
61
+ var _el$9 = web.getNextElement(_tmpl$2), _el$0 = _el$9.firstChild, _el$10 = _el$0.nextSibling, [_el$11, _co$] = web.getNextMarker(_el$10.nextSibling);
62
+ _el$11.nextSibling;
63
+ web.insert(_el$9, hiddenCount, _el$11, _co$);
64
+ return _el$9;
65
+ }
66
+ })];
67
+ }
68
+ }), _el$13, _co$2);
69
+ return _el$;
70
+ })();
71
+ };
72
+ exports.DegradedFallback = DegradedFallback;
73
+ //# sourceMappingURL=DegradedFallback.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DegradedFallback.cjs","sources":["../../src/components/DegradedFallback.tsx"],"sourcesContent":["/**\n * DegradedFallback — the middle rung of the renderer fallback ladder\n * (audit 2026-05-30, P2.5).\n *\n * Each heavy renderer (graph / map / chart) follows the same contract:\n * 1. native render when its peer lib is available and succeeds;\n * 2. **degraded but useful** view when the native render throws — this\n * component: a visible notice + a plain data table so the user still\n * sees the underlying data instead of a blank space;\n * 3. (the caller also emits a `component:error` telemetry event).\n *\n * Pure / presentational — no peer deps, no side effects — so a render-path\n * failure in a heavy lib can never cascade into the fallback itself, and it\n * is trivially unit-testable. Rows/cells are rendered as text; callers are\n * responsible for stringifying complex cell values.\n */\n\nimport { Component, For, Show } from 'solid-js';\n\nexport interface DegradedFallbackProps {\n /** Short, human-readable reason the native render was skipped/failed. */\n message: string;\n /**\n * Column headers for the degraded data table. When omitted (or empty),\n * only the notice banner is shown.\n */\n columns?: string[];\n /** Row data — each row is an array of cells aligned to `columns`. */\n rows?: Array<Array<string | number>>;\n /**\n * Caption under the table. Defaults to a generic\n * \"interactive view unavailable\" line.\n */\n caption?: string;\n /** Max rows to render before truncating (default 50). */\n maxRows?: number;\n}\n\nexport const DegradedFallback: Component<DegradedFallbackProps> = (props) => {\n const maxRows = () => props.maxRows ?? 50;\n const allRows = () => props.rows ?? [];\n const shownRows = () => allRows().slice(0, maxRows());\n const hiddenCount = () => Math.max(0, allRows().length - shownRows().length);\n const hasTable = () => (props.columns?.length ?? 0) > 0 && allRows().length > 0;\n\n return (\n <div\n class=\"w-full rounded-lg border border-amber-200 bg-amber-50 p-3 dark:border-amber-800 dark:bg-amber-900/20\"\n role=\"alert\"\n >\n <p class=\"text-sm font-medium text-amber-900 dark:text-amber-100\">{props.message}</p>\n <p class=\"mt-0.5 text-xs text-amber-700 dark:text-amber-300\">\n {props.caption ?? 'Showing the underlying data — the interactive view is unavailable.'}\n </p>\n\n <Show when={hasTable()}>\n <div class=\"mt-2 max-h-64 overflow-auto rounded border border-amber-200 dark:border-amber-800\">\n <table class=\"w-full border-collapse text-left text-xs\">\n <thead class=\"sticky top-0 bg-amber-100 dark:bg-amber-900/40\">\n <tr>\n <For each={props.columns}>\n {(col) => (\n <th class=\"px-2 py-1 font-medium text-amber-900 dark:text-amber-100\">{col}</th>\n )}\n </For>\n </tr>\n </thead>\n <tbody>\n <For each={shownRows()}>\n {(row) => (\n <tr class=\"border-t border-amber-100 dark:border-amber-800/60\">\n <For each={props.columns}>\n {(_col, i) => (\n <td class=\"px-2 py-1 text-amber-800 dark:text-amber-200\">\n {String(row[i()] ?? '')}\n </td>\n )}\n </For>\n </tr>\n )}\n </For>\n </tbody>\n </table>\n </div>\n <Show when={hiddenCount() > 0}>\n <p class=\"mt-1 text-[10px] text-amber-600 dark:text-amber-400\">\n +{hiddenCount()} more rows not shown.\n </p>\n </Show>\n </Show>\n </div>\n );\n};\n"],"names":["DegradedFallback","props","maxRows","allRows","rows","shownRows","slice","hiddenCount","Math","max","length","hasTable","columns","_el$","_$getNextElement","_tmpl$3","_el$2","firstChild","_el$3","nextSibling","_el$12","_el$13","_co$2","_$getNextMarker","_$insert","message","caption","_$createComponent","Show","when","children","_el$4","_tmpl$","_el$5","_el$6","_el$7","_el$8","For","each","col","_el$14","_tmpl$4","row","_el$15","_tmpl$5","_col","i","_el$16","_tmpl$6","String","_el$9","_tmpl$2","_el$0","_el$10","_el$11","_co$"],"mappings":";;;;;AAsCO,MAAMA,mBAAsDC,CAAAA,UAAU;AAC3E,QAAMC,UAAUA,MAAMD,MAAMC,WAAW;AACvC,QAAMC,UAAUA,MAAMF,MAAMG,QAAQ,CAAA;AACpC,QAAMC,YAAYA,MAAMF,QAAAA,EAAUG,MAAM,GAAGJ,SAAS;AACpD,QAAMK,cAAcA,MAAMC,KAAKC,IAAI,GAAGN,UAAUO,SAASL,UAAAA,EAAYK,MAAM;AAC3E,QAAMC,WAAWA,MAAAA;;AAAOV,yBAAMW,YAANX,mBAAeS,WAAU,KAAK,KAAKP,UAAUO,SAAS;AAAA;AAE9E,UAAA,MAAA;AAAA,QAAAG,OAAAC,IAAAA,eAAAC,OAAA,GAAAC,QAAAH,KAAAI,YAAAC,QAAAF,MAAAG,aAAAC,SAAAF,MAAAC,aAAA,CAAAE,QAAAC,KAAA,IAAAC,IAAAA,cAAAH,OAAAD,WAAA;AAAAK,QAAAA,OAAAR,OAAA,MAKuEf,MAAMwB,OAAO;AAAAD,QAAAA,OAAAN,OAAA,MAE7EjB,MAAMyB,WAAW,oEAAoE;AAAAF,eAAAX,MAAAc,IAAAA,gBAGvFC,cAAI;AAAA,MAAA,IAACC,OAAI;AAAA,eAAElB,SAAAA;AAAAA,MAAU;AAAA,MAAA,IAAAmB,WAAA;AAAA,eAAA,EAAA,MAAA;AAAA,cAAAC,QAAAjB,IAAAA,eAAAkB,MAAA,GAAAC,QAAAF,MAAAd,YAAAiB,QAAAD,MAAAhB,YAAAkB,QAAAD,MAAAjB,YAAAmB,QAAAF,MAAAf;AAAAK,qBAAAW,OAAAR,IAAAA,gBAKXU,aAAG;AAAA,YAAA,IAACC,OAAI;AAAA,qBAAErC,MAAMW;AAAAA,YAAO;AAAA,YAAAkB,UACpBS,UAAG,MAAA;AAAA,kBAAAC,SAAA1B,IAAAA,eAAA2B,OAAA;AAAAjB,kBAAAA,OAAAgB,QACmED,GAAG;AAAA,qBAAAC;AAAAA,YAAA,GAAA;AAAA,UAAA,CAC1E,CAAA;AAAAhB,qBAAAY,OAAAT,IAAAA,gBAKJU,aAAG;AAAA,YAAA,IAACC,OAAI;AAAA,qBAAEjC,UAAAA;AAAAA,YAAW;AAAA,YAAAyB,UAClBY,UAAG,MAAA;AAAA,kBAAAC,SAAA7B,IAAAA,eAAA8B,OAAA;AAAApB,yBAAAmB,QAAAhB,IAAAA,gBAEAU,aAAG;AAAA,gBAAA,IAACC,OAAI;AAAA,yBAAErC,MAAMW;AAAAA,gBAAO;AAAA,gBAAAkB,UACrBA,CAACe,MAAMC,OAAC,MAAA;AAAA,sBAAAC,SAAAjC,IAAAA,eAAAkC,OAAA;AAAAxB,sBAAAA,OAAAuB,QAAA,MAEJE,OAAOP,IAAII,GAAG,KAAK,EAAE,CAAC;AAAA,yBAAAC;AAAAA,gBAAA,GAAA;AAAA,cAAA,CAE1B,CAAA;AAAA,qBAAAJ;AAAAA,YAAA,GAAA;AAAA,UAAA,CAGN,CAAA;AAAA,iBAAAZ;AAAAA,QAAA,GAAA,GAAAJ,IAAAA,gBAKRC,cAAI;AAAA,UAAA,IAACC,OAAI;AAAA,mBAAEtB,gBAAgB;AAAA,UAAC;AAAA,UAAA,IAAAuB,WAAA;AAAA,gBAAAoB,QAAApC,IAAAA,eAAAqC,OAAA,GAAAC,QAAAF,MAAAjC,YAAAoC,SAAAD,MAAAjC,aAAA,CAAAmC,QAAAC,IAAA,IAAAhC,IAAAA,cAAA8B,OAAAlC,WAAA;AAAAmC,mBAAAnC;AAAAK,gBAAAA,OAAA0B,OAEvB3C,aAAW+C,QAAAC,IAAA;AAAA,mBAAAL;AAAAA,UAAA;AAAA,QAAA,CAAA,CAAA;AAAA,MAAA;AAAA,IAAA,CAAA,GAAA7B,QAAAC,KAAA;AAAA,WAAAT;AAAAA,EAAA,GAAA;AAMzB;;"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * DegradedFallback — the middle rung of the renderer fallback ladder
3
+ * (audit 2026-05-30, P2.5).
4
+ *
5
+ * Each heavy renderer (graph / map / chart) follows the same contract:
6
+ * 1. native render when its peer lib is available and succeeds;
7
+ * 2. **degraded but useful** view when the native render throws — this
8
+ * component: a visible notice + a plain data table so the user still
9
+ * sees the underlying data instead of a blank space;
10
+ * 3. (the caller also emits a `component:error` telemetry event).
11
+ *
12
+ * Pure / presentational — no peer deps, no side effects — so a render-path
13
+ * failure in a heavy lib can never cascade into the fallback itself, and it
14
+ * is trivially unit-testable. Rows/cells are rendered as text; callers are
15
+ * responsible for stringifying complex cell values.
16
+ */
17
+ import { Component } from 'solid-js';
18
+ export interface DegradedFallbackProps {
19
+ /** Short, human-readable reason the native render was skipped/failed. */
20
+ message: string;
21
+ /**
22
+ * Column headers for the degraded data table. When omitted (or empty),
23
+ * only the notice banner is shown.
24
+ */
25
+ columns?: string[];
26
+ /** Row data — each row is an array of cells aligned to `columns`. */
27
+ rows?: Array<Array<string | number>>;
28
+ /**
29
+ * Caption under the table. Defaults to a generic
30
+ * "interactive view unavailable" line.
31
+ */
32
+ caption?: string;
33
+ /** Max rows to render before truncating (default 50). */
34
+ maxRows?: number;
35
+ }
36
+ export declare const DegradedFallback: Component<DegradedFallbackProps>;
37
+ //# sourceMappingURL=DegradedFallback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DegradedFallback.d.ts","sourceRoot":"","sources":["../../src/components/DegradedFallback.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,SAAS,EAAa,MAAM,UAAU,CAAC;AAEhD,MAAM,WAAW,qBAAqB;IACpC,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,qEAAqE;IACrE,IAAI,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;IACrC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,gBAAgB,EAAE,SAAS,CAAC,qBAAqB,CAsD7D,CAAC"}