@seed-ship/mcp-ui-solid 6.3.0 → 6.4.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 (85) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/dist/components/CarouselRenderer.cjs +3 -0
  3. package/dist/components/CarouselRenderer.cjs.map +1 -1
  4. package/dist/components/CarouselRenderer.d.ts +5 -0
  5. package/dist/components/CarouselRenderer.d.ts.map +1 -1
  6. package/dist/components/CarouselRenderer.js +3 -0
  7. package/dist/components/CarouselRenderer.js.map +1 -1
  8. package/dist/components/ChartJSRenderer.cjs +3 -0
  9. package/dist/components/ChartJSRenderer.cjs.map +1 -1
  10. package/dist/components/ChartJSRenderer.d.ts +5 -0
  11. package/dist/components/ChartJSRenderer.d.ts.map +1 -1
  12. package/dist/components/ChartJSRenderer.js +3 -0
  13. package/dist/components/ChartJSRenderer.js.map +1 -1
  14. package/dist/components/CodeBlockRenderer.cjs +3 -0
  15. package/dist/components/CodeBlockRenderer.cjs.map +1 -1
  16. package/dist/components/CodeBlockRenderer.d.ts +5 -0
  17. package/dist/components/CodeBlockRenderer.d.ts.map +1 -1
  18. package/dist/components/CodeBlockRenderer.js +3 -0
  19. package/dist/components/CodeBlockRenderer.js.map +1 -1
  20. package/dist/components/GraphRenderer.cjs +33 -24
  21. package/dist/components/GraphRenderer.cjs.map +1 -1
  22. package/dist/components/GraphRenderer.d.ts +8 -2
  23. package/dist/components/GraphRenderer.d.ts.map +1 -1
  24. package/dist/components/GraphRenderer.js +33 -24
  25. package/dist/components/GraphRenderer.js.map +1 -1
  26. package/dist/components/ImageGalleryRenderer.cjs +3 -0
  27. package/dist/components/ImageGalleryRenderer.cjs.map +1 -1
  28. package/dist/components/ImageGalleryRenderer.d.ts +5 -0
  29. package/dist/components/ImageGalleryRenderer.d.ts.map +1 -1
  30. package/dist/components/ImageGalleryRenderer.js +3 -0
  31. package/dist/components/ImageGalleryRenderer.js.map +1 -1
  32. package/dist/components/MapRenderer.cjs +3 -0
  33. package/dist/components/MapRenderer.cjs.map +1 -1
  34. package/dist/components/MapRenderer.d.ts +5 -0
  35. package/dist/components/MapRenderer.d.ts.map +1 -1
  36. package/dist/components/MapRenderer.js +3 -0
  37. package/dist/components/MapRenderer.js.map +1 -1
  38. package/dist/components/PortalDropdownMenu.cjs +82 -0
  39. package/dist/components/PortalDropdownMenu.cjs.map +1 -0
  40. package/dist/components/PortalDropdownMenu.d.ts +56 -0
  41. package/dist/components/PortalDropdownMenu.d.ts.map +1 -0
  42. package/dist/components/PortalDropdownMenu.js +82 -0
  43. package/dist/components/PortalDropdownMenu.js.map +1 -0
  44. package/dist/components/UIResourceRenderer.cjs +296 -256
  45. package/dist/components/UIResourceRenderer.cjs.map +1 -1
  46. package/dist/components/UIResourceRenderer.d.ts +12 -0
  47. package/dist/components/UIResourceRenderer.d.ts.map +1 -1
  48. package/dist/components/UIResourceRenderer.js +296 -256
  49. package/dist/components/UIResourceRenderer.js.map +1 -1
  50. package/dist/components/VideoRenderer.cjs +3 -0
  51. package/dist/components/VideoRenderer.cjs.map +1 -1
  52. package/dist/components/VideoRenderer.d.ts +5 -0
  53. package/dist/components/VideoRenderer.d.ts.map +1 -1
  54. package/dist/components/VideoRenderer.js +3 -0
  55. package/dist/components/VideoRenderer.js.map +1 -1
  56. package/dist/components/index.d.ts +2 -0
  57. package/dist/components/index.d.ts.map +1 -1
  58. package/dist/components.cjs +2 -0
  59. package/dist/components.cjs.map +1 -1
  60. package/dist/components.d.cts +2 -0
  61. package/dist/components.d.ts +2 -0
  62. package/dist/components.js +2 -0
  63. package/dist/components.js.map +1 -1
  64. package/dist/index.cjs +2 -0
  65. package/dist/index.cjs.map +1 -1
  66. package/dist/index.d.cts +2 -0
  67. package/dist/index.d.ts +2 -0
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +2 -0
  70. package/dist/index.js.map +1 -1
  71. package/package.json +1 -1
  72. package/src/components/CarouselRenderer.tsx +6 -0
  73. package/src/components/ChartJSRenderer.tsx +7 -0
  74. package/src/components/CodeBlockRenderer.tsx +7 -1
  75. package/src/components/GraphRenderer.tsx +40 -21
  76. package/src/components/ImageGalleryRenderer.tsx +7 -0
  77. package/src/components/MapRenderer.tsx +7 -0
  78. package/src/components/PortalDropdownMenu.test.tsx +113 -0
  79. package/src/components/PortalDropdownMenu.tsx +130 -0
  80. package/src/components/UIResourceRenderer.fluidity.test.tsx +51 -0
  81. package/src/components/UIResourceRenderer.tsx +50 -24
  82. package/src/components/VideoRenderer.tsx +7 -0
  83. package/src/components/index.ts +4 -0
  84. package/src/index.ts +2 -0
  85. package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,93 @@ 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.4.0] - 2026-05-03
9
+
10
+ Closes axe 3 of `deposium_solid`'s
11
+ `SOLID-MCPUI-IMPROVEMENT-AXES-2026-05-03.md` handoff.
12
+
13
+ ### Changed — Export menus now mount via `<Portal>`
14
+
15
+ Both Export dropdowns shipped by the renderers — the `TableRenderer`
16
+ (`Copy TSV / Download CSV / Download JSON`) and the `GraphRenderer`
17
+ (`Download PNG / Download Mermaid / Download JSON`) — now render via
18
+ `solid-js/web` `<Portal>` on `document.body` instead of an in-tree
19
+ `position: absolute` sibling.
20
+
21
+ This fixes two long-standing pain points :
22
+
23
+ 1. **`overflow: hidden` clipping** — when the table or graph lives
24
+ inside a chat bubble, a card, or any ancestor with `overflow: hidden`,
25
+ the legacy in-tree menu got clipped at the ancestor's boundary.
26
+ Mounting on `document.body` escapes the clip stack entirely.
27
+ 2. **`z-index` wars** — chat surfaces stack composer / message rails
28
+ above the message list, and ancestor `z-index` creates a new stacking
29
+ context that captured the in-tree menu. A portal is a sibling of the
30
+ document, so a single `z-index: 9999` wins.
31
+
32
+ Pre-v6.4.0 deposium had to ship a `overflow: visible !important`
33
+ override + 4 `z-index` overrides on its `ChatUIIsland.tsx` to work
34
+ around this. Both can now be removed.
35
+
36
+ ### Added — `<PortalDropdownMenu>` (factored helper)
37
+
38
+ The portal-mounting + click-outside + Escape + scroll/resize
39
+ re-positioning logic is factored into a generic component for reuse.
40
+ Exported under both `@seed-ship/mcp-ui-solid` root and
41
+ `@seed-ship/mcp-ui-solid/components` :
42
+
43
+ ```tsx
44
+ import { PortalDropdownMenu } from '@seed-ship/mcp-ui-solid'
45
+
46
+ const [open, setOpen] = createSignal(false)
47
+ let triggerRef: HTMLButtonElement | undefined
48
+
49
+ <button ref={triggerRef} onClick={() => setOpen(true)}>Menu</button>
50
+ <PortalDropdownMenu open={open()} onClose={() => setOpen(false)} trigger={triggerRef}>
51
+ <button onClick={...}>Item 1</button>
52
+ <button onClick={...}>Item 2</button>
53
+ </PortalDropdownMenu>
54
+ ```
55
+
56
+ ### Non-breaking
57
+
58
+ The behavior change is transparent to consumers — the menu still opens
59
+ and closes from the same trigger button, with the same items. Tests :
60
+ 7 added in `PortalDropdownMenu.test.tsx` (mount target, position from
61
+ `getBoundingClientRect`, outside click + Escape close, trigger /
62
+ in-menu mousedown ignored).
63
+
64
+ ## [6.3.1] - 2026-05-03
65
+
66
+ ### Added — `<UIResourceRenderer toolbarVariant>` forwarding
67
+
68
+ Forwards the `toolbarVariant?: 'hover' | 'always-visible'` prop introduced
69
+ on `<ExpandableWrapper>` in v6.3.0 down through `<UIResourceRenderer>` to
70
+ every internal renderer that wraps `<ExpandableWrapper>` :
71
+
72
+ - `TableRenderer` (in `UIResourceRenderer.tsx`)
73
+ - `ChartJSRenderer` (native chart path)
74
+ - `GraphRenderer`
75
+ - `MapRenderer`
76
+ - `VideoRenderer`
77
+ - `CarouselRenderer`
78
+ - `ImageGalleryRenderer`
79
+ - `CodeBlockRenderer`
80
+
81
+ ```tsx
82
+ <UIResourceRenderer content={layout} toolbarVariant="always-visible" />
83
+ ```
84
+
85
+ Pre-v6.3.1, consumers had to wrap `<ExpandableWrapper>` themselves to
86
+ configure variant. Now the surface-level renderer accepts it and
87
+ propagates uniformly. Default behavior (hover-only) unchanged.
88
+
89
+ ### Non-breaking
90
+
91
+ Additive prop. All v6.3.0 APIs unchanged. 3 tests added in
92
+ `UIResourceRenderer.fluidity.test.tsx` (default + `'always-visible'` +
93
+ explicit `'hover'`).
94
+
8
95
  ## [6.3.0] - 2026-05-03
9
96
 
10
97
  Two consumer-friendly props driven by `deposium_solid`'s
@@ -32,6 +32,9 @@ const CarouselRenderer = (props) => {
32
32
  return JSON.stringify(props.items, null, 2);
33
33
  },
34
34
  copyLabel: "Copy items (JSON)",
35
+ get toolbarVariant() {
36
+ return props.toolbarVariant;
37
+ },
35
38
  get children() {
36
39
  var _el$ = web.getNextElement(_tmpl$), _el$2 = _el$.firstChild, _el$3 = _el$2.nextSibling, _el$4 = _el$3.nextSibling;
37
40
  _el$2.$$click = () => scroll("left");
@@ -1 +1 @@
1
- {"version":3,"file":"CarouselRenderer.cjs","sources":["../../src/components/CarouselRenderer.tsx"],"sourcesContent":["import { Component, For, createSignal } from 'solid-js'\nimport { isServer } from 'solid-js/web'\nimport { useRenderContext } from './RenderContext'\nimport type { UIComponent } from '../types'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\n\nexport interface CarouselRendererProps {\n items: UIComponent[]\n height?: string\n}\n\nexport const CarouselRenderer: Component<CarouselRendererProps> = (props) => {\n let scrollContainer: HTMLDivElement | undefined\n const [canScrollLeft, setCanScrollLeft] = createSignal(false)\n const [canScrollRight, setCanScrollRight] = createSignal(true)\n const isExpanded = useExpanded()\n\n // Use render context to avoid circular dependency\n const { renderComponent } = useRenderContext()\n\n const checkScroll = () => {\n if (isServer || !scrollContainer) return\n setCanScrollLeft(scrollContainer.scrollLeft > 0)\n setCanScrollRight(\n scrollContainer.scrollLeft < scrollContainer.scrollWidth - scrollContainer.clientWidth - 10\n )\n }\n\n const scroll = (direction: 'left' | 'right') => {\n if (isServer || !scrollContainer) return\n const scrollAmount = scrollContainer.clientWidth * 0.8\n scrollContainer.scrollBy({\n left: direction === 'left' ? -scrollAmount : scrollAmount,\n behavior: 'smooth'\n })\n }\n\n return (\n <ExpandableWrapper\n title={'Carousel'}\n copyData={JSON.stringify(props.items, null, 2)}\n copyLabel=\"Copy items (JSON)\"\n >\n <div class={`relative group ${isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''}`}>\n {/* Navigation Buttons */}\n <button\n onClick={() => scroll('left')}\n class={`absolute left-2 top-1/2 -translate-y-1/2 z-10 p-2 bg-white/80 dark:bg-gray-800/80 backdrop-blur-sm rounded-full shadow-md border border-gray-200 dark:border-gray-700 transition-opacity ${canScrollLeft() ? 'opacity-0 group-hover:opacity-100' : 'opacity-0 pointer-events-none'\n }`}\n >\n <svg class=\"w-5 h-5 text-gray-700 dark:text-gray-300\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\" />\n </svg>\n </button>\n\n <button\n onClick={() => scroll('right')}\n class={`absolute right-2 top-1/2 -translate-y-1/2 z-10 p-2 bg-white/80 dark:bg-gray-800/80 backdrop-blur-sm rounded-full shadow-md border border-gray-200 dark:border-gray-700 transition-opacity ${canScrollRight() ? 'opacity-0 group-hover:opacity-100' : 'opacity-0 pointer-events-none'\n }`}\n >\n <svg class=\"w-5 h-5 text-gray-700 dark:text-gray-300\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n\n {/* Scroll Container */}\n <div\n ref={scrollContainer}\n onScroll={checkScroll}\n class=\"flex gap-4 overflow-x-auto snap-x snap-mandatory pb-4 scrollbar-hide\"\n style={{ \"scroll-behavior\": \"smooth\" }}\n >\n <For each={props.items}>\n {(item) => (\n <div class=\"flex-none w-[85%] sm:w-[45%] snap-center\">\n <div class=\"h-full border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden bg-white dark:bg-gray-800\">\n {renderComponent(item)}\n </div>\n </div>\n )}\n </For>\n </div>\n </div>\n </ExpandableWrapper>\n )\n}\n"],"names":["CarouselRenderer","props","scrollContainer","canScrollLeft","setCanScrollLeft","createSignal","canScrollRight","setCanScrollRight","isExpanded","useExpanded","renderComponent","useRenderContext","checkScroll","isServer","scrollLeft","scrollWidth","clientWidth","scroll","direction","scrollAmount","scrollBy","left","behavior","_$createComponent","ExpandableWrapper","title","copyData","JSON","stringify","items","copyLabel","children","_el$","_$getNextElement","_tmpl$","_el$2","firstChild","_el$3","nextSibling","_el$4","$$click","addEventListener","_ref$","_$use","_$insert","For","each","item","_el$5","_tmpl$2","_el$6","_$effect","_p$","_v$","_v$2","_v$3","e","_$className","t","a","undefined","_$runHydrationEvents","_$delegateEvents"],"mappings":";;;;;;;AAWO,MAAMA,mBAAsDC,CAAAA,UAAU;AACzE,MAAIC;AACJ,QAAM,CAACC,eAAeC,gBAAgB,IAAIC,QAAAA,aAAa,KAAK;AAC5D,QAAM,CAACC,gBAAgBC,iBAAiB,IAAIF,QAAAA,aAAa,IAAI;AAC7D,QAAMG,aAAaC,kBAAAA,YAAAA;AAGnB,QAAM;AAAA,IAAEC;AAAAA,EAAAA,IAAoBC,+BAAAA;AAE5B,QAAMC,cAAcA,MAAM;AACtB,QAAIC,IAAAA,YAAY,CAACX,gBAAiB;AAClCE,qBAAiBF,gBAAgBY,aAAa,CAAC;AAC/CP,sBACIL,gBAAgBY,aAAaZ,gBAAgBa,cAAcb,gBAAgBc,cAAc,EAC7F;AAAA,EACJ;AAEA,QAAMC,SAASA,CAACC,cAAgC;AAC5C,QAAIL,IAAAA,YAAY,CAACX,gBAAiB;AAClC,UAAMiB,eAAejB,gBAAgBc,cAAc;AACnDd,oBAAgBkB,SAAS;AAAA,MACrBC,MAAMH,cAAc,SAAS,CAACC,eAAeA;AAAAA,MAC7CG,UAAU;AAAA,IAAA,CACb;AAAA,EACL;AAEA,SAAAC,IAAAA,gBACKC,kBAAAA,mBAAiB;AAAA,IACdC,OAAO;AAAA,IAAU,IACjBC,WAAQ;AAAA,aAAEC,KAAKC,UAAU3B,MAAM4B,OAAO,MAAM,CAAC;AAAA,IAAC;AAAA,IAC9CC,WAAS;AAAA,IAAA,IAAAC,WAAA;AAAA,UAAAC,OAAAC,IAAAA,eAAAC,MAAA,GAAAC,QAAAH,KAAAI,YAAAC,QAAAF,MAAAG,aAAAC,QAAAF,MAAAC;AAAAH,YAAAK,UAKI,MAAMvB,OAAO,MAAM;AAACoB,YAAAG,UAUpB,MAAMvB,OAAO,OAAO;AAACsB,YAAAE,iBAAA,UAYpB7B,WAAW;AAAA,UAAA8B,QADhBxC;AAAe,aAAAwC,UAAA,aAAAC,IAAAA,IAAAD,OAAAH,KAAA,IAAfrC,kBAAeqC;AAAAK,iBAAAL,OAAAhB,IAAAA,gBAKnBsB,aAAG;AAAA,QAAA,IAACC,OAAI;AAAA,iBAAE7C,MAAM4B;AAAAA,QAAK;AAAA,QAAAE,UAChBgB,WAAI,MAAA;AAAA,cAAAC,QAAAf,IAAAA,eAAAgB,OAAA,GAAAC,QAAAF,MAAAZ;AAAAQ,cAAAA,OAAAM,OAAA,MAGOxC,gBAAgBqC,IAAI,CAAC;AAAA,iBAAAC;AAAAA,QAAA,GAAA;AAAA,MAAA,CAGjC,CAAA;AAAAG,UAAAA,OAAAC,CAAAA,QAAA;AAAA,YAAAC,MApCD,kBAAkB7C,WAAAA,IAAe,iCAAiC,EAAE,IAAE8C,OAInE,4LAA4LnD,kBAAkB,sCAAsC,+BAA+B,IACpRoD,OASC,6LAA6LjD,eAAAA,IAAmB,sCAAsC,+BAA+B;AACtR+C,gBAAAD,IAAAI,KAAAC,IAAAA,UAAAzB,MAAAoB,IAAAI,IAAAH,GAAA;AAAAC,iBAAAF,IAAAM,KAAAD,IAAAA,UAAAtB,OAAAiB,IAAAM,IAAAJ,IAAA;AAAAC,iBAAAH,IAAAO,KAAAF,IAAAA,UAAApB,OAAAe,IAAAO,IAAAJ,IAAA;AAAA,eAAAH;AAAAA,MAAA,GAAA;AAAA,QAAAI,GAAAI;AAAAA,QAAAF,GAAAE;AAAAA,QAAAD,GAAAC;AAAAA,MAAAA,CAAA;AAAAC,6BAAAA;AAAA,aAAA7B;AAAAA,IAAA;AAAA,EAAA,CAAA;AA2BtB;AAAC8B,IAAAA,eAAA,CAAA,OAAA,CAAA;;"}
1
+ {"version":3,"file":"CarouselRenderer.cjs","sources":["../../src/components/CarouselRenderer.tsx"],"sourcesContent":["import { Component, For, createSignal } from 'solid-js'\nimport { isServer } from 'solid-js/web'\nimport { useRenderContext } from './RenderContext'\nimport type { UIComponent } from '../types'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\n\nexport interface CarouselRendererProps {\n items: UIComponent[]\n height?: string\n /**\n * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).\n * @see ExpandableWrapperProps.toolbarVariant\n */\n toolbarVariant?: 'hover' | 'always-visible'\n}\n\nexport const CarouselRenderer: Component<CarouselRendererProps> = (props) => {\n let scrollContainer: HTMLDivElement | undefined\n const [canScrollLeft, setCanScrollLeft] = createSignal(false)\n const [canScrollRight, setCanScrollRight] = createSignal(true)\n const isExpanded = useExpanded()\n\n // Use render context to avoid circular dependency\n const { renderComponent } = useRenderContext()\n\n const checkScroll = () => {\n if (isServer || !scrollContainer) return\n setCanScrollLeft(scrollContainer.scrollLeft > 0)\n setCanScrollRight(\n scrollContainer.scrollLeft < scrollContainer.scrollWidth - scrollContainer.clientWidth - 10\n )\n }\n\n const scroll = (direction: 'left' | 'right') => {\n if (isServer || !scrollContainer) return\n const scrollAmount = scrollContainer.clientWidth * 0.8\n scrollContainer.scrollBy({\n left: direction === 'left' ? -scrollAmount : scrollAmount,\n behavior: 'smooth'\n })\n }\n\n return (\n <ExpandableWrapper\n title={'Carousel'}\n copyData={JSON.stringify(props.items, null, 2)}\n copyLabel=\"Copy items (JSON)\"\n toolbarVariant={props.toolbarVariant}\n >\n <div class={`relative group ${isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''}`}>\n {/* Navigation Buttons */}\n <button\n onClick={() => scroll('left')}\n class={`absolute left-2 top-1/2 -translate-y-1/2 z-10 p-2 bg-white/80 dark:bg-gray-800/80 backdrop-blur-sm rounded-full shadow-md border border-gray-200 dark:border-gray-700 transition-opacity ${canScrollLeft() ? 'opacity-0 group-hover:opacity-100' : 'opacity-0 pointer-events-none'\n }`}\n >\n <svg class=\"w-5 h-5 text-gray-700 dark:text-gray-300\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\" />\n </svg>\n </button>\n\n <button\n onClick={() => scroll('right')}\n class={`absolute right-2 top-1/2 -translate-y-1/2 z-10 p-2 bg-white/80 dark:bg-gray-800/80 backdrop-blur-sm rounded-full shadow-md border border-gray-200 dark:border-gray-700 transition-opacity ${canScrollRight() ? 'opacity-0 group-hover:opacity-100' : 'opacity-0 pointer-events-none'\n }`}\n >\n <svg class=\"w-5 h-5 text-gray-700 dark:text-gray-300\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n\n {/* Scroll Container */}\n <div\n ref={scrollContainer}\n onScroll={checkScroll}\n class=\"flex gap-4 overflow-x-auto snap-x snap-mandatory pb-4 scrollbar-hide\"\n style={{ \"scroll-behavior\": \"smooth\" }}\n >\n <For each={props.items}>\n {(item) => (\n <div class=\"flex-none w-[85%] sm:w-[45%] snap-center\">\n <div class=\"h-full border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden bg-white dark:bg-gray-800\">\n {renderComponent(item)}\n </div>\n </div>\n )}\n </For>\n </div>\n </div>\n </ExpandableWrapper>\n )\n}\n"],"names":["CarouselRenderer","props","scrollContainer","canScrollLeft","setCanScrollLeft","createSignal","canScrollRight","setCanScrollRight","isExpanded","useExpanded","renderComponent","useRenderContext","checkScroll","isServer","scrollLeft","scrollWidth","clientWidth","scroll","direction","scrollAmount","scrollBy","left","behavior","_$createComponent","ExpandableWrapper","title","copyData","JSON","stringify","items","copyLabel","toolbarVariant","children","_el$","_$getNextElement","_tmpl$","_el$2","firstChild","_el$3","nextSibling","_el$4","$$click","addEventListener","_ref$","_$use","_$insert","For","each","item","_el$5","_tmpl$2","_el$6","_$effect","_p$","_v$","_v$2","_v$3","e","_$className","t","a","undefined","_$runHydrationEvents","_$delegateEvents"],"mappings":";;;;;;;AAgBO,MAAMA,mBAAsDC,CAAAA,UAAU;AACzE,MAAIC;AACJ,QAAM,CAACC,eAAeC,gBAAgB,IAAIC,QAAAA,aAAa,KAAK;AAC5D,QAAM,CAACC,gBAAgBC,iBAAiB,IAAIF,QAAAA,aAAa,IAAI;AAC7D,QAAMG,aAAaC,kBAAAA,YAAAA;AAGnB,QAAM;AAAA,IAAEC;AAAAA,EAAAA,IAAoBC,+BAAAA;AAE5B,QAAMC,cAAcA,MAAM;AACtB,QAAIC,IAAAA,YAAY,CAACX,gBAAiB;AAClCE,qBAAiBF,gBAAgBY,aAAa,CAAC;AAC/CP,sBACIL,gBAAgBY,aAAaZ,gBAAgBa,cAAcb,gBAAgBc,cAAc,EAC7F;AAAA,EACJ;AAEA,QAAMC,SAASA,CAACC,cAAgC;AAC5C,QAAIL,IAAAA,YAAY,CAACX,gBAAiB;AAClC,UAAMiB,eAAejB,gBAAgBc,cAAc;AACnDd,oBAAgBkB,SAAS;AAAA,MACrBC,MAAMH,cAAc,SAAS,CAACC,eAAeA;AAAAA,MAC7CG,UAAU;AAAA,IAAA,CACb;AAAA,EACL;AAEA,SAAAC,IAAAA,gBACKC,kBAAAA,mBAAiB;AAAA,IACdC,OAAO;AAAA,IAAU,IACjBC,WAAQ;AAAA,aAAEC,KAAKC,UAAU3B,MAAM4B,OAAO,MAAM,CAAC;AAAA,IAAC;AAAA,IAC9CC,WAAS;AAAA,IAAA,IACTC,iBAAc;AAAA,aAAE9B,MAAM8B;AAAAA,IAAc;AAAA,IAAA,IAAAC,WAAA;AAAA,UAAAC,OAAAC,IAAAA,eAAAC,MAAA,GAAAC,QAAAH,KAAAI,YAAAC,QAAAF,MAAAG,aAAAC,QAAAF,MAAAC;AAAAH,YAAAK,UAKvB,MAAMxB,OAAO,MAAM;AAACqB,YAAAG,UAUpB,MAAMxB,OAAO,OAAO;AAACuB,YAAAE,iBAAA,UAYpB9B,WAAW;AAAA,UAAA+B,QADhBzC;AAAe,aAAAyC,UAAA,aAAAC,IAAAA,IAAAD,OAAAH,KAAA,IAAftC,kBAAesC;AAAAK,iBAAAL,OAAAjB,IAAAA,gBAKnBuB,aAAG;AAAA,QAAA,IAACC,OAAI;AAAA,iBAAE9C,MAAM4B;AAAAA,QAAK;AAAA,QAAAG,UAChBgB,WAAI,MAAA;AAAA,cAAAC,QAAAf,IAAAA,eAAAgB,OAAA,GAAAC,QAAAF,MAAAZ;AAAAQ,cAAAA,OAAAM,OAAA,MAGOzC,gBAAgBsC,IAAI,CAAC;AAAA,iBAAAC;AAAAA,QAAA,GAAA;AAAA,MAAA,CAGjC,CAAA;AAAAG,UAAAA,OAAAC,CAAAA,QAAA;AAAA,YAAAC,MApCD,kBAAkB9C,WAAAA,IAAe,iCAAiC,EAAE,IAAE+C,OAInE,4LAA4LpD,kBAAkB,sCAAsC,+BAA+B,IACpRqD,OASC,6LAA6LlD,eAAAA,IAAmB,sCAAsC,+BAA+B;AACtRgD,gBAAAD,IAAAI,KAAAC,IAAAA,UAAAzB,MAAAoB,IAAAI,IAAAH,GAAA;AAAAC,iBAAAF,IAAAM,KAAAD,IAAAA,UAAAtB,OAAAiB,IAAAM,IAAAJ,IAAA;AAAAC,iBAAAH,IAAAO,KAAAF,IAAAA,UAAApB,OAAAe,IAAAO,IAAAJ,IAAA;AAAA,eAAAH;AAAAA,MAAA,GAAA;AAAA,QAAAI,GAAAI;AAAAA,QAAAF,GAAAE;AAAAA,QAAAD,GAAAC;AAAAA,MAAAA,CAAA;AAAAC,6BAAAA;AAAA,aAAA7B;AAAAA,IAAA;AAAA,EAAA,CAAA;AA2BtB;AAAC8B,IAAAA,eAAA,CAAA,OAAA,CAAA;;"}
@@ -3,6 +3,11 @@ import type { UIComponent } from '../types';
3
3
  export interface CarouselRendererProps {
4
4
  items: UIComponent[];
5
5
  height?: string;
6
+ /**
7
+ * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).
8
+ * @see ExpandableWrapperProps.toolbarVariant
9
+ */
10
+ toolbarVariant?: 'hover' | 'always-visible';
6
11
  }
7
12
  export declare const CarouselRenderer: Component<CarouselRendererProps>;
8
13
  //# sourceMappingURL=CarouselRenderer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CarouselRenderer.d.ts","sourceRoot":"","sources":["../../src/components/CarouselRenderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAqB,MAAM,UAAU,CAAA;AAGvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAG3C,MAAM,WAAW,qBAAqB;IAClC,KAAK,EAAE,WAAW,EAAE,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,eAAO,MAAM,gBAAgB,EAAE,SAAS,CAAC,qBAAqB,CA0E7D,CAAA"}
1
+ {"version":3,"file":"CarouselRenderer.d.ts","sourceRoot":"","sources":["../../src/components/CarouselRenderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAqB,MAAM,UAAU,CAAA;AAGvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAG3C,MAAM,WAAW,qBAAqB;IAClC,KAAK,EAAE,WAAW,EAAE,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAA;CAC9C;AAED,eAAO,MAAM,gBAAgB,EAAE,SAAS,CAAC,qBAAqB,CA2E7D,CAAA"}
@@ -30,6 +30,9 @@ const CarouselRenderer = (props) => {
30
30
  return JSON.stringify(props.items, null, 2);
31
31
  },
32
32
  copyLabel: "Copy items (JSON)",
33
+ get toolbarVariant() {
34
+ return props.toolbarVariant;
35
+ },
33
36
  get children() {
34
37
  var _el$ = getNextElement(_tmpl$), _el$2 = _el$.firstChild, _el$3 = _el$2.nextSibling, _el$4 = _el$3.nextSibling;
35
38
  _el$2.$$click = () => scroll("left");
@@ -1 +1 @@
1
- {"version":3,"file":"CarouselRenderer.js","sources":["../../src/components/CarouselRenderer.tsx"],"sourcesContent":["import { Component, For, createSignal } from 'solid-js'\nimport { isServer } from 'solid-js/web'\nimport { useRenderContext } from './RenderContext'\nimport type { UIComponent } from '../types'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\n\nexport interface CarouselRendererProps {\n items: UIComponent[]\n height?: string\n}\n\nexport const CarouselRenderer: Component<CarouselRendererProps> = (props) => {\n let scrollContainer: HTMLDivElement | undefined\n const [canScrollLeft, setCanScrollLeft] = createSignal(false)\n const [canScrollRight, setCanScrollRight] = createSignal(true)\n const isExpanded = useExpanded()\n\n // Use render context to avoid circular dependency\n const { renderComponent } = useRenderContext()\n\n const checkScroll = () => {\n if (isServer || !scrollContainer) return\n setCanScrollLeft(scrollContainer.scrollLeft > 0)\n setCanScrollRight(\n scrollContainer.scrollLeft < scrollContainer.scrollWidth - scrollContainer.clientWidth - 10\n )\n }\n\n const scroll = (direction: 'left' | 'right') => {\n if (isServer || !scrollContainer) return\n const scrollAmount = scrollContainer.clientWidth * 0.8\n scrollContainer.scrollBy({\n left: direction === 'left' ? -scrollAmount : scrollAmount,\n behavior: 'smooth'\n })\n }\n\n return (\n <ExpandableWrapper\n title={'Carousel'}\n copyData={JSON.stringify(props.items, null, 2)}\n copyLabel=\"Copy items (JSON)\"\n >\n <div class={`relative group ${isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''}`}>\n {/* Navigation Buttons */}\n <button\n onClick={() => scroll('left')}\n class={`absolute left-2 top-1/2 -translate-y-1/2 z-10 p-2 bg-white/80 dark:bg-gray-800/80 backdrop-blur-sm rounded-full shadow-md border border-gray-200 dark:border-gray-700 transition-opacity ${canScrollLeft() ? 'opacity-0 group-hover:opacity-100' : 'opacity-0 pointer-events-none'\n }`}\n >\n <svg class=\"w-5 h-5 text-gray-700 dark:text-gray-300\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\" />\n </svg>\n </button>\n\n <button\n onClick={() => scroll('right')}\n class={`absolute right-2 top-1/2 -translate-y-1/2 z-10 p-2 bg-white/80 dark:bg-gray-800/80 backdrop-blur-sm rounded-full shadow-md border border-gray-200 dark:border-gray-700 transition-opacity ${canScrollRight() ? 'opacity-0 group-hover:opacity-100' : 'opacity-0 pointer-events-none'\n }`}\n >\n <svg class=\"w-5 h-5 text-gray-700 dark:text-gray-300\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n\n {/* Scroll Container */}\n <div\n ref={scrollContainer}\n onScroll={checkScroll}\n class=\"flex gap-4 overflow-x-auto snap-x snap-mandatory pb-4 scrollbar-hide\"\n style={{ \"scroll-behavior\": \"smooth\" }}\n >\n <For each={props.items}>\n {(item) => (\n <div class=\"flex-none w-[85%] sm:w-[45%] snap-center\">\n <div class=\"h-full border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden bg-white dark:bg-gray-800\">\n {renderComponent(item)}\n </div>\n </div>\n )}\n </For>\n </div>\n </div>\n </ExpandableWrapper>\n )\n}\n"],"names":["CarouselRenderer","props","scrollContainer","canScrollLeft","setCanScrollLeft","createSignal","canScrollRight","setCanScrollRight","isExpanded","useExpanded","renderComponent","useRenderContext","checkScroll","isServer","scrollLeft","scrollWidth","clientWidth","scroll","direction","scrollAmount","scrollBy","left","behavior","_$createComponent","ExpandableWrapper","title","copyData","JSON","stringify","items","copyLabel","children","_el$","_$getNextElement","_tmpl$","_el$2","firstChild","_el$3","nextSibling","_el$4","$$click","addEventListener","_ref$","_$use","_$insert","For","each","item","_el$5","_tmpl$2","_el$6","_$effect","_p$","_v$","_v$2","_v$3","e","_$className","t","a","undefined","_$runHydrationEvents","_$delegateEvents"],"mappings":";;;;;AAWO,MAAMA,mBAAsDC,CAAAA,UAAU;AACzE,MAAIC;AACJ,QAAM,CAACC,eAAeC,gBAAgB,IAAIC,aAAa,KAAK;AAC5D,QAAM,CAACC,gBAAgBC,iBAAiB,IAAIF,aAAa,IAAI;AAC7D,QAAMG,aAAaC,YAAAA;AAGnB,QAAM;AAAA,IAAEC;AAAAA,EAAAA,IAAoBC,iBAAAA;AAE5B,QAAMC,cAAcA,MAAM;AACtB,QAAIC,YAAY,CAACX,gBAAiB;AAClCE,qBAAiBF,gBAAgBY,aAAa,CAAC;AAC/CP,sBACIL,gBAAgBY,aAAaZ,gBAAgBa,cAAcb,gBAAgBc,cAAc,EAC7F;AAAA,EACJ;AAEA,QAAMC,SAASA,CAACC,cAAgC;AAC5C,QAAIL,YAAY,CAACX,gBAAiB;AAClC,UAAMiB,eAAejB,gBAAgBc,cAAc;AACnDd,oBAAgBkB,SAAS;AAAA,MACrBC,MAAMH,cAAc,SAAS,CAACC,eAAeA;AAAAA,MAC7CG,UAAU;AAAA,IAAA,CACb;AAAA,EACL;AAEA,SAAAC,gBACKC,mBAAiB;AAAA,IACdC,OAAO;AAAA,IAAU,IACjBC,WAAQ;AAAA,aAAEC,KAAKC,UAAU3B,MAAM4B,OAAO,MAAM,CAAC;AAAA,IAAC;AAAA,IAC9CC,WAAS;AAAA,IAAA,IAAAC,WAAA;AAAA,UAAAC,OAAAC,eAAAC,MAAA,GAAAC,QAAAH,KAAAI,YAAAC,QAAAF,MAAAG,aAAAC,QAAAF,MAAAC;AAAAH,YAAAK,UAKI,MAAMvB,OAAO,MAAM;AAACoB,YAAAG,UAUpB,MAAMvB,OAAO,OAAO;AAACsB,YAAAE,iBAAA,UAYpB7B,WAAW;AAAA,UAAA8B,QADhBxC;AAAe,aAAAwC,UAAA,aAAAC,IAAAD,OAAAH,KAAA,IAAfrC,kBAAeqC;AAAAK,aAAAL,OAAAhB,gBAKnBsB,KAAG;AAAA,QAAA,IAACC,OAAI;AAAA,iBAAE7C,MAAM4B;AAAAA,QAAK;AAAA,QAAAE,UAChBgB,WAAI,MAAA;AAAA,cAAAC,QAAAf,eAAAgB,OAAA,GAAAC,QAAAF,MAAAZ;AAAAQ,iBAAAM,OAAA,MAGOxC,gBAAgBqC,IAAI,CAAC;AAAA,iBAAAC;AAAAA,QAAA,GAAA;AAAA,MAAA,CAGjC,CAAA;AAAAG,aAAAC,CAAAA,QAAA;AAAA,YAAAC,MApCD,kBAAkB7C,WAAAA,IAAe,iCAAiC,EAAE,IAAE8C,OAInE,4LAA4LnD,kBAAkB,sCAAsC,+BAA+B,IACpRoD,OASC,6LAA6LjD,eAAAA,IAAmB,sCAAsC,+BAA+B;AACtR+C,gBAAAD,IAAAI,KAAAC,UAAAzB,MAAAoB,IAAAI,IAAAH,GAAA;AAAAC,iBAAAF,IAAAM,KAAAD,UAAAtB,OAAAiB,IAAAM,IAAAJ,IAAA;AAAAC,iBAAAH,IAAAO,KAAAF,UAAApB,OAAAe,IAAAO,IAAAJ,IAAA;AAAA,eAAAH;AAAAA,MAAA,GAAA;AAAA,QAAAI,GAAAI;AAAAA,QAAAF,GAAAE;AAAAA,QAAAD,GAAAC;AAAAA,MAAAA,CAAA;AAAAC,yBAAAA;AAAA,aAAA7B;AAAAA,IAAA;AAAA,EAAA,CAAA;AA2BtB;AAAC8B,eAAA,CAAA,OAAA,CAAA;"}
1
+ {"version":3,"file":"CarouselRenderer.js","sources":["../../src/components/CarouselRenderer.tsx"],"sourcesContent":["import { Component, For, createSignal } from 'solid-js'\nimport { isServer } from 'solid-js/web'\nimport { useRenderContext } from './RenderContext'\nimport type { UIComponent } from '../types'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\n\nexport interface CarouselRendererProps {\n items: UIComponent[]\n height?: string\n /**\n * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).\n * @see ExpandableWrapperProps.toolbarVariant\n */\n toolbarVariant?: 'hover' | 'always-visible'\n}\n\nexport const CarouselRenderer: Component<CarouselRendererProps> = (props) => {\n let scrollContainer: HTMLDivElement | undefined\n const [canScrollLeft, setCanScrollLeft] = createSignal(false)\n const [canScrollRight, setCanScrollRight] = createSignal(true)\n const isExpanded = useExpanded()\n\n // Use render context to avoid circular dependency\n const { renderComponent } = useRenderContext()\n\n const checkScroll = () => {\n if (isServer || !scrollContainer) return\n setCanScrollLeft(scrollContainer.scrollLeft > 0)\n setCanScrollRight(\n scrollContainer.scrollLeft < scrollContainer.scrollWidth - scrollContainer.clientWidth - 10\n )\n }\n\n const scroll = (direction: 'left' | 'right') => {\n if (isServer || !scrollContainer) return\n const scrollAmount = scrollContainer.clientWidth * 0.8\n scrollContainer.scrollBy({\n left: direction === 'left' ? -scrollAmount : scrollAmount,\n behavior: 'smooth'\n })\n }\n\n return (\n <ExpandableWrapper\n title={'Carousel'}\n copyData={JSON.stringify(props.items, null, 2)}\n copyLabel=\"Copy items (JSON)\"\n toolbarVariant={props.toolbarVariant}\n >\n <div class={`relative group ${isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''}`}>\n {/* Navigation Buttons */}\n <button\n onClick={() => scroll('left')}\n class={`absolute left-2 top-1/2 -translate-y-1/2 z-10 p-2 bg-white/80 dark:bg-gray-800/80 backdrop-blur-sm rounded-full shadow-md border border-gray-200 dark:border-gray-700 transition-opacity ${canScrollLeft() ? 'opacity-0 group-hover:opacity-100' : 'opacity-0 pointer-events-none'\n }`}\n >\n <svg class=\"w-5 h-5 text-gray-700 dark:text-gray-300\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\" />\n </svg>\n </button>\n\n <button\n onClick={() => scroll('right')}\n class={`absolute right-2 top-1/2 -translate-y-1/2 z-10 p-2 bg-white/80 dark:bg-gray-800/80 backdrop-blur-sm rounded-full shadow-md border border-gray-200 dark:border-gray-700 transition-opacity ${canScrollRight() ? 'opacity-0 group-hover:opacity-100' : 'opacity-0 pointer-events-none'\n }`}\n >\n <svg class=\"w-5 h-5 text-gray-700 dark:text-gray-300\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n\n {/* Scroll Container */}\n <div\n ref={scrollContainer}\n onScroll={checkScroll}\n class=\"flex gap-4 overflow-x-auto snap-x snap-mandatory pb-4 scrollbar-hide\"\n style={{ \"scroll-behavior\": \"smooth\" }}\n >\n <For each={props.items}>\n {(item) => (\n <div class=\"flex-none w-[85%] sm:w-[45%] snap-center\">\n <div class=\"h-full border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden bg-white dark:bg-gray-800\">\n {renderComponent(item)}\n </div>\n </div>\n )}\n </For>\n </div>\n </div>\n </ExpandableWrapper>\n )\n}\n"],"names":["CarouselRenderer","props","scrollContainer","canScrollLeft","setCanScrollLeft","createSignal","canScrollRight","setCanScrollRight","isExpanded","useExpanded","renderComponent","useRenderContext","checkScroll","isServer","scrollLeft","scrollWidth","clientWidth","scroll","direction","scrollAmount","scrollBy","left","behavior","_$createComponent","ExpandableWrapper","title","copyData","JSON","stringify","items","copyLabel","toolbarVariant","children","_el$","_$getNextElement","_tmpl$","_el$2","firstChild","_el$3","nextSibling","_el$4","$$click","addEventListener","_ref$","_$use","_$insert","For","each","item","_el$5","_tmpl$2","_el$6","_$effect","_p$","_v$","_v$2","_v$3","e","_$className","t","a","undefined","_$runHydrationEvents","_$delegateEvents"],"mappings":";;;;;AAgBO,MAAMA,mBAAsDC,CAAAA,UAAU;AACzE,MAAIC;AACJ,QAAM,CAACC,eAAeC,gBAAgB,IAAIC,aAAa,KAAK;AAC5D,QAAM,CAACC,gBAAgBC,iBAAiB,IAAIF,aAAa,IAAI;AAC7D,QAAMG,aAAaC,YAAAA;AAGnB,QAAM;AAAA,IAAEC;AAAAA,EAAAA,IAAoBC,iBAAAA;AAE5B,QAAMC,cAAcA,MAAM;AACtB,QAAIC,YAAY,CAACX,gBAAiB;AAClCE,qBAAiBF,gBAAgBY,aAAa,CAAC;AAC/CP,sBACIL,gBAAgBY,aAAaZ,gBAAgBa,cAAcb,gBAAgBc,cAAc,EAC7F;AAAA,EACJ;AAEA,QAAMC,SAASA,CAACC,cAAgC;AAC5C,QAAIL,YAAY,CAACX,gBAAiB;AAClC,UAAMiB,eAAejB,gBAAgBc,cAAc;AACnDd,oBAAgBkB,SAAS;AAAA,MACrBC,MAAMH,cAAc,SAAS,CAACC,eAAeA;AAAAA,MAC7CG,UAAU;AAAA,IAAA,CACb;AAAA,EACL;AAEA,SAAAC,gBACKC,mBAAiB;AAAA,IACdC,OAAO;AAAA,IAAU,IACjBC,WAAQ;AAAA,aAAEC,KAAKC,UAAU3B,MAAM4B,OAAO,MAAM,CAAC;AAAA,IAAC;AAAA,IAC9CC,WAAS;AAAA,IAAA,IACTC,iBAAc;AAAA,aAAE9B,MAAM8B;AAAAA,IAAc;AAAA,IAAA,IAAAC,WAAA;AAAA,UAAAC,OAAAC,eAAAC,MAAA,GAAAC,QAAAH,KAAAI,YAAAC,QAAAF,MAAAG,aAAAC,QAAAF,MAAAC;AAAAH,YAAAK,UAKvB,MAAMxB,OAAO,MAAM;AAACqB,YAAAG,UAUpB,MAAMxB,OAAO,OAAO;AAACuB,YAAAE,iBAAA,UAYpB9B,WAAW;AAAA,UAAA+B,QADhBzC;AAAe,aAAAyC,UAAA,aAAAC,IAAAD,OAAAH,KAAA,IAAftC,kBAAesC;AAAAK,aAAAL,OAAAjB,gBAKnBuB,KAAG;AAAA,QAAA,IAACC,OAAI;AAAA,iBAAE9C,MAAM4B;AAAAA,QAAK;AAAA,QAAAG,UAChBgB,WAAI,MAAA;AAAA,cAAAC,QAAAf,eAAAgB,OAAA,GAAAC,QAAAF,MAAAZ;AAAAQ,iBAAAM,OAAA,MAGOzC,gBAAgBsC,IAAI,CAAC;AAAA,iBAAAC;AAAAA,QAAA,GAAA;AAAA,MAAA,CAGjC,CAAA;AAAAG,aAAAC,CAAAA,QAAA;AAAA,YAAAC,MApCD,kBAAkB9C,WAAAA,IAAe,iCAAiC,EAAE,IAAE+C,OAInE,4LAA4LpD,kBAAkB,sCAAsC,+BAA+B,IACpRqD,OASC,6LAA6LlD,eAAAA,IAAmB,sCAAsC,+BAA+B;AACtRgD,gBAAAD,IAAAI,KAAAC,UAAAzB,MAAAoB,IAAAI,IAAAH,GAAA;AAAAC,iBAAAF,IAAAM,KAAAD,UAAAtB,OAAAiB,IAAAM,IAAAJ,IAAA;AAAAC,iBAAAH,IAAAO,KAAAF,UAAApB,OAAAe,IAAAO,IAAAJ,IAAA;AAAA,eAAAH;AAAAA,MAAA,GAAA;AAAA,QAAAI,GAAAI;AAAAA,QAAAF,GAAAE;AAAAA,QAAAD,GAAAC;AAAAA,MAAAA,CAAA;AAAAC,yBAAAA;AAAA,aAAA7B;AAAAA,IAAA;AAAA,EAAA,CAAA;AA2BtB;AAAC8B,eAAA,CAAA,OAAA,CAAA;"}
@@ -142,6 +142,9 @@ const ChartJSRenderer = (props) => {
142
142
  return copyDataJSON();
143
143
  },
144
144
  copyLabel: "Copy chart data (JSON)",
145
+ get toolbarVariant() {
146
+ return props.toolbarVariant;
147
+ },
145
148
  get children() {
146
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;
147
150
  web.insert(_el$, web.createComponent(solidJs.Show, {
@@ -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/**\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 >\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","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'\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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"}
@@ -22,6 +22,11 @@ export interface ChartJSRendererProps {
22
22
  * Error callback
23
23
  */
24
24
  onError?: (error: Error) => void;
25
+ /**
26
+ * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).
27
+ * @see ExpandableWrapperProps.toolbarVariant
28
+ */
29
+ toolbarVariant?: 'hover' | 'always-visible';
25
30
  }
26
31
  /**
27
32
  * Native Chart.js renderer component
@@ -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;CACjC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,CAAC,oBAAoB,CAwL3D,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,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"}
@@ -118,6 +118,9 @@ const ChartJSRenderer = (props) => {
118
118
  return copyDataJSON();
119
119
  },
120
120
  copyLabel: "Copy chart data (JSON)",
121
+ get toolbarVariant() {
122
+ return props.toolbarVariant;
123
+ },
121
124
  get children() {
122
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;
123
126
  insert(_el$, createComponent(Show, {
@@ -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/**\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 >\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","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":";;;;AAeA,IAAIA,UAAe;AACnB,IAAIC,qBAA0C;AAE9C,MAAMC,cAAc,YAAY;AAC9B,MAAIF,QAAS,QAAOA;AAEpB,MAAI,CAACC,oBAAoB;AACvBA,yBAAqB,OAAO,eAAe,EACxCE,KAAMC,CAAAA,WAAW;AAChBJ,gBAAUI,OAAOC,WAAWD,OAAOE;AACnC,aAAON;AAAAA,IACT,CAAC,EACAO,MAAOC,CAAAA,QAAQ;AACdP,2BAAqB;AACrB,YAAMO;AAAAA,IACR,CAAC;AAAA,EACL;AAEA,SAAOP;AACT;AAKA,eAAsBQ,qBAAuC;AAC3D,MAAI;AACF,UAAMP,YAAAA;AACN,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAoCO,MAAMQ,kBAAoDC,CAAAA,UAAU;AACzE,QAAM,CAACC,WAAWC,YAAY,IAAIC,aAAa,IAAI;AACnD,QAAM,CAACC,OAAOC,QAAQ,IAAIF,aAAAA;AAC1B,MAAIG;AACJ,MAAIC;AAEJ,QAAMC,SAASA,MAAMR,MAAMS,UAAUD;AACrC,QAAME,aAAaC,YAAAA;AAKnB,QAAMC,gBAAgBA,MAAMJ,OAAAA,EAASK,eAAe;AAIpD,QAAMC,eAAeA,MAAMC,KAAKC,UAAU;AAAA,IAAEC,MAAMT,SAASS;AAAAA,IAAMC,MAAMV,SAASU;AAAAA,EAAAA,GAAQ,MAAM,CAAC;AAG/F,QAAMC,kBAAkBA,MAAM;AAC5B,QAAI,CAACb,UAAW;AAChB,UAAMc,MAAMd,UAAUe,UAAU,WAAW;AAC3C,UAAMC,IAAIC,SAASC,cAAc,GAAG;AACpCF,MAAEG,OAAOL;AACTE,MAAEI,WAAW,IAAIlB,OAAAA,EAASmB,SAAS,SAASC,QAAQ,QAAQ,GAAG,EAAEC,YAAAA,CAAa;AAC9EP,MAAEQ,MAAAA;AAAAA,EACJ;AAGAC,eAAa,YAAY;;AACvB,QAAI,CAACzB,UAAW;AAGhB,UAAM0B,cAAcxB,OAAAA;AAEpBN,iBAAa,IAAI;AACjBG,aAAS4B,MAAS;AAElB,QAAI;AACF,YAAMtC,QAAQ,MAAMJ,YAAAA;AAGpB,UAAIgB,eAAe;AACjBA,sBAAc2B,QAAAA;AACd3B,wBAAgB;AAAA,MAClB;AAGA,YAAM4B,cAAmB;AAAA,QACvBC,YAAY;AAAA,QACZC,qBAAqB;AAAA,QACrB,GAAGL,YAAYM;AAAAA,QACfC,SAAS;AAAA,UACP,IAAGP,iBAAYM,YAAZN,mBAAqBO;AAAAA,UACxBC,QAAQ;AAAA,YACNC,SAAS;AAAA,YACTC,UAAU;AAAA,YACV,IAAGV,uBAAYM,YAAZN,mBAAqBO,YAArBP,mBAA8BQ;AAAAA,UAAAA;AAAAA,QACnC;AAAA,MACF;AAIF,UAAIR,YAAYW,UAAU;AACxB,cAAMC,KAAKZ,YAAYW;AACvBR,oBAAYU,SAAS;AAAA,UACnB,GAAGV,YAAYU;AAAAA,UACfC,GAAG;AAAA,YACD,IAAGX,iBAAYU,WAAZV,mBAAoBW;AAAAA,YACvB7B,MAAM;AAAA,YACN8B,MAAM;AAAA,cACJC,QAAQJ,GAAGI;AAAAA,cACXC,MAAML,GAAGK;AAAAA,cACTC,eAAeN,GAAGM;AAAAA,YAAAA;AAAAA,YAEpB,GAAIN,GAAGO,MAAM;AAAA,cAAEA,KAAKP,GAAGO;AAAAA,YAAAA,IAAQ,CAAA;AAAA,YAC/B,GAAIP,GAAGQ,MAAM;AAAA,cAAEA,KAAKR,GAAGQ;AAAAA,YAAAA,IAAQ,CAAA;AAAA,UAAC;AAAA,QAClC;AAAA,MAEJ;AAGA7C,sBAAgB,IAAIZ,MAAMW,WAAW;AAAA,QACnCW,MAAMe,YAAYf;AAAAA,QAClBC,MAAMc,YAAYd;AAAAA,QAClBoB,SAASH;AAAAA,MAAAA,CACV;AAEDjC,mBAAa,KAAK;AAAA,IACpB,SAASL,KAAK;AACZ,YAAMO,SAAQP,eAAewD,QAAQxD,MAAM,IAAIwD,MAAM,wBAAwB;AAC7EhD,eAASD,OAAMkD,OAAO;AACtBpD,mBAAa,KAAK;AAClBF,kBAAMuD,YAANvD,+BAAgBI;AAAAA,IAClB;AAAA,EACF,CAAC;AAGDoD,YAAU,MAAM;AACd,QAAIjD,eAAe;AACjBA,oBAAc2B,QAAAA;AACd3B,sBAAgB;AAAA,IAClB;AAAA,EACF,CAAC;AAED,SAAAkD,gBACGC,mBAAiB;AAAA,IAAA,IAChB/B,QAAK;AAAA,aAAEnB,OAAAA,EAASmB,SAAS;AAAA,IAAO;AAAA,IAAA,IAChCgC,WAAQ;AAAA,aAAE7C,aAAAA;AAAAA,IAAc;AAAA,IACxB8C,WAAS;AAAA,IAAA,IAAAC,WAAA;AAAA,UAAAC,OAAAC,eAAAC,OAAA,GAAAC,SAAAH,KAAAI,YAAA,CAAAC,QAAAC,KAAA,IAAAC,cAAAJ,OAAAK,WAAA,GAAAC,SAAAJ,OAAAG,aAAA,CAAAE,QAAAC,KAAA,IAAAJ,cAAAE,OAAAD,WAAA,GAAAI,SAAAF,OAAAF,aAAA,CAAAK,QAAAC,KAAA,IAAAP,cAAAK,OAAAJ,WAAA,GAAAO,SAAAF,OAAAL,aAAAQ,SAAAD,OAAAX;AAAAa,aAAAjB,MAAAL,gBAKNuB,MAAI;AAAA,QAAA,IAACC,OAAI;AAAA,iBAAEzE,OAAAA,EAASmB,SAASf,cAAAA;AAAAA,QAAe;AAAA,QAAA,IAAAiD,WAAA;AAAA,cAAAqB,QAAAnB,eAAAoB,OAAA,GAAAC,QAAAF,MAAAhB,YAAA,CAAAmB,OAAAC,IAAA,IAAAjB,cAAAe,MAAAd,WAAA,GAAAiB,QAAAF,MAAAf,aAAA,CAAAkB,OAAAC,KAAA,IAAApB,cAAAkB,MAAAjB,WAAA;AAAAS,iBAAAG,OAAAzB,gBAExCuB,MAAI;AAAA,YAAA,IAACC,OAAI;AAAA,qBAAEzE,SAASmB;AAAAA,YAAK;AAAA,YAAA,IAAAkC,WAAA;AAAA,kBAAA6B,QAAA3B,eAAA4B,MAAA;AAAAZ,qBAAAW,OAAA,MAErBlF,OAAAA,EAASmB,KAAK;AAAA,qBAAA+D;AAAAA,YAAA;AAAA,UAAA,CAAA,GAAAL,OAAAC,IAAA;AAAAP,iBAAAG,OAAAzB,gBAGlBuB,MAAI;AAAA,YAAA,IAACC,OAAI;AAAA,qBAAErE,cAAAA;AAAAA,YAAe;AAAA,YAAA,IAAAiD,WAAA;AAAA,kBAAA+B,QAAA7B,eAAA8B,OAAA;AAAAD,oBAAAE,UAEd3E;AAAe4E,iCAAAA;AAAA,qBAAAH;AAAAA,YAAA;AAAA,UAAA,CAAA,GAAAJ,OAAAC,KAAA;AAAA,iBAAAP;AAAAA,QAAA;AAAA,MAAA,CAAA,GAAAf,QAAAC,KAAA;AAAAW,aAAAjB,MAAAL,gBAa/BuB,MAAI;AAAA,QAAA,IAACC,OAAI;AAAA,iBAAEhF,UAAAA;AAAAA,QAAW;AAAA,QAAA,IAAA4D,WAAA;AAAA,iBAAAE,eAAAiC,OAAA;AAAA,QAAA;AAAA,MAAA,CAAA,GAAAxB,QAAAC,KAAA;AAAAM,aAAAjB,MAAAL,gBAStBuB,MAAI;AAAA,QAAA,IAACC,OAAI;AAAA,iBAAE7E,MAAAA;AAAAA,QAAO;AAAA,QAAA,IAAAyD,WAAA;AAAA,cAAAoC,QAAAlC,eAAAmC,OAAA,GAAAC,QAAAF,MAAA/B,YAAAkC,SAAAD,MAAAjC,YAAAmC,SAAAD,OAAA9B,aAAAgC,SAAAD,OAAA/B;AAAAS,iBAAAuB,QAmBsDlG,KAAK;AAAA,iBAAA6F;AAAAA,QAAA;AAAA,MAAA,CAAA,GAAAtB,QAAAC,KAAA;AAAA,UAAA2B,QAe/DjG;AAAS,aAAAiG,UAAA,aAAAC,IAAAD,OAAAzB,MAAA,IAATxE,YAASwE;AAAA2B,aAAAC,CAAAA,QAAA;AAAA,YAAAC,MApEd,wIACVjG,WAAAA,IAAe,iCAAiC,EAAE,IAClDkG,OAyDS,UAAUlG,WAAAA,IAAe,mBAAmB,EAAE,IAAEmG,OAErDzG,UACI;AAAA,UAAEqC,SAAS;AAAA,QAAA,IACX/B,eACE;AAAA,UAAEoG,QAAQ;AAAA,UAAQrE,SAAS;AAAA,QAAA,IAC3B;AAAA,UAAEqE,QAAQtG,SAASsG,UAAU;AAAA,UAASrE,SAAS;AAAA,QAAA;AAASkE,gBAAAD,IAAAK,KAAAC,UAAAlD,MAAA4C,IAAAK,IAAAJ,GAAA;AAAAC,iBAAAF,IAAAO,KAAAD,UAAAnC,QAAA6B,IAAAO,IAAAL,IAAA;AAAAF,YAAApF,IAAA4F,MAAArC,QAAAgC,MAAAH,IAAApF,CAAA;AAAA,eAAAoF;AAAAA,MAAA,GAAA;AAAA,QAAAK,GAAA9E;AAAAA,QAAAgF,GAAAhF;AAAAA,QAAAX,GAAAW;AAAAA,MAAAA,CAAA;AAAA,aAAA6B;AAAAA,IAAA;AAAA,EAAA,CAAA;AAQ1E;AAACqD,eAAA,CAAA,OAAA,CAAA;"}
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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"}
@@ -174,6 +174,9 @@ const CodeBlockRenderer = (props) => {
174
174
  return (_a = params()) == null ? void 0 : _a.code;
175
175
  },
176
176
  copyLabel: "Copy code",
177
+ get toolbarVariant() {
178
+ return props.toolbarVariant;
179
+ },
177
180
  get children() {
178
181
  var _el$ = web.getNextElement(_tmpl$3), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$4 = _el$3.nextSibling, _el$5 = _el$4.firstChild, _el$6 = _el$5.nextSibling, _el$7 = _el$6.nextSibling, _el$8 = _el$7.nextSibling, _el$0 = _el$2.nextSibling, _el$12 = _el$0.firstChild, [_el$13, _co$] = web.getNextMarker(_el$12.nextSibling), _el$10 = _el$13.nextSibling, _el$11 = _el$10.firstChild;
179
182
  web.insert(_el$3, () => {
@@ -1 +1 @@
1
- {"version":3,"file":"CodeBlockRenderer.cjs","sources":["../../src/components/CodeBlockRenderer.tsx"],"sourcesContent":["/**\n * CodeBlockRenderer - Syntax highlighted code block\n * Sprint 6: Code & Maps\n * Sprint Ultimate: Theme Synchronization (U.1)\n */\n\nimport { Component, createEffect, onCleanup, createSignal, Show, For } from 'solid-js'\nimport { isServer } from 'solid-js/web'\nimport type { UIComponent, CodeComponentParams } from '../types'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\nimport { highlightQuery } from './UIResourceRenderer'\n\n/** Map of `params.language` → file extension for the v6.2.0 download button. */\nconst LANGUAGE_EXTENSIONS: Record<string, string> = {\n typescript: 'ts', tsx: 'tsx', javascript: 'js', jsx: 'jsx',\n python: 'py', ruby: 'rb', go: 'go', rust: 'rs', java: 'java',\n kotlin: 'kt', swift: 'swift', php: 'php', csharp: 'cs', cpp: 'cpp',\n c: 'c', sql: 'sql', json: 'json', yaml: 'yml', toml: 'toml',\n bash: 'sh', shell: 'sh', html: 'html', css: 'css', scss: 'scss',\n markdown: 'md', xml: 'xml', graphql: 'graphql',\n}\n\n// Lazy load highlight.js\nlet hljs: any = null\n// Track if styles have been loaded globally\nlet stylesLoaded = false\n\nexport interface CodeBlockRendererProps {\n /**\n * UIComponent containing code params\n */\n component?: UIComponent\n\n /**\n * Direct code params\n */\n params?: CodeComponentParams\n}\n\nexport const CodeBlockRenderer: Component<CodeBlockRendererProps> = (props) => {\n const [highlightedCode, setHighlightedCode] = createSignal<string>('')\n const [isCopied, setIsCopied] = createSignal(false)\n const [isHljsLoaded, setIsHljsLoaded] = createSignal(false)\n const [activeTheme, setActiveTheme] = createSignal<'light' | 'dark'>('dark')\n const [wordWrap, setWordWrap] = createSignal(false)\n\n const params = () => props.params || (props.component?.params as CodeComponentParams)\n const isExpanded = useExpanded()\n const [searchQuery, setSearchQuery] = createSignal('')\n\n // v6.2.0 — search highlight: re-wraps `<mark>` around matches in the\n // already-highlighted (hljs) HTML output. `highlightQuery` is the same\n // helper TableRenderer uses (only wraps text outside of HTML tags so\n // syntax span colors stay intact).\n const displayedHTML = () => {\n const q = searchQuery().trim()\n return q ? highlightQuery(highlightedCode(), q) : highlightedCode()\n }\n\n const handleDownload = () => {\n const code = params()?.code\n if (!code) return\n const lang = (params()?.language || '').toLowerCase()\n const ext = LANGUAGE_EXTENSIONS[lang] || 'txt'\n const stem = (params()?.filename || `code-${Date.now()}`).replace(/\\.[^.]+$/, '')\n const filename = stem.endsWith(`.${ext}`) ? stem : `${stem}.${ext}`\n const blob = new Blob([code], { type: 'text/plain' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = filename\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n URL.revokeObjectURL(url)\n }\n\n // Load highlight.js on mount\n createEffect(async () => {\n if (!hljs) {\n try {\n // Use the full highlight.js bundle with all languages for simplicity\n const module = await import('highlight.js')\n const resolved = module.default || module\n // Guard: verify the resolved module has the expected API\n if (typeof resolved?.highlight === 'function') {\n hljs = resolved\n } else {\n console.warn('highlight.js loaded but missing highlight() method')\n }\n setIsHljsLoaded(true)\n } catch (e) {\n console.warn('Failed to load highlight.js', e)\n // Continue without highlighting - fallback to plain text\n setIsHljsLoaded(true)\n }\n } else {\n setIsHljsLoaded(true)\n }\n })\n\n // Theme management - Sprint Ultimate U.1: Reactive theme synchronization\n // Load both theme stylesheets once, then toggle via data-attribute\n createEffect(async () => {\n if (isServer || stylesLoaded) return\n\n // Load both themes upfront for instant switching\n try {\n await Promise.all([\n import('highlight.js/styles/github.css'),\n import('highlight.js/styles/github-dark.css')\n ])\n stylesLoaded = true\n } catch (e) {\n console.warn('Failed to load highlight.js themes', e)\n }\n })\n\n // Reactive theme detection - listens to system preference changes\n createEffect(() => {\n if (isServer) return\n\n // Priority 1: Explicit theme from params\n const paramTheme = params()?.theme\n if (paramTheme) {\n setActiveTheme(paramTheme)\n return\n }\n\n // Priority 2: System preference with live updates\n // Check if matchMedia is available (not in all test environments)\n if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n setActiveTheme(mediaQuery.matches ? 'dark' : 'light')\n\n const handleChange = (e: MediaQueryListEvent) => {\n // Only update if no explicit theme in params\n if (!params()?.theme) {\n setActiveTheme(e.matches ? 'dark' : 'light')\n }\n }\n\n mediaQuery.addEventListener('change', handleChange)\n onCleanup(() => mediaQuery.removeEventListener('change', handleChange))\n }\n })\n\n // Apply highlighting\n createEffect(() => {\n const code = params()?.code || ''\n const language = params()?.language || ''\n\n if (hljs && isHljsLoaded()) {\n try {\n let result\n if (language && hljs.getLanguage(language)) {\n result = hljs.highlight(code, { language }).value\n } else {\n result = hljs.highlightAuto(code).value\n }\n setHighlightedCode(result)\n } catch (e) {\n setHighlightedCode(code.replace(/</g, '&lt;').replace(/>/g, '&gt;'))\n }\n } else {\n // Fallback: simple escaping\n setHighlightedCode(code.replace(/</g, '&lt;').replace(/>/g, '&gt;'))\n }\n })\n\n // Line numbers generation\n const lineNumbers = () => {\n if (params()?.showLineNumbers === false) return []\n const code = params()?.code || ''\n const lines = code.split('\\n')\n const start = params()?.startLine || 1\n return lines.map((_, i) => start + i)\n }\n\n const handleCopy = async () => {\n const code = params()?.code\n if (code) {\n try {\n await navigator.clipboard.writeText(code)\n setIsCopied(true)\n setTimeout(() => setIsCopied(false), 2000)\n } catch (e) {\n console.error('Failed to copy code', e)\n }\n }\n }\n\n return (\n <ExpandableWrapper title={params()?.filename || params()?.language || 'Code'} copyData={params()?.code} copyLabel=\"Copy code\">\n <div class={`w-full bg-gray-50 dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden text-sm flex flex-col ${isExpanded() ? 'flex-1 min-h-0' : ''}`}>\n {/* Header */}\n <div class=\"flex items-center justify-between px-4 py-2 bg-gray-100 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 shrink-0\">\n <div class=\"font-mono text-xs text-gray-600 dark:text-gray-400\">\n {params()?.filename || params()?.language || 'Code'}\n </div>\n <div class=\"flex items-center gap-2\">\n {/* Search input (v6.2.0) */}\n <input\n type=\"text\"\n value={searchQuery()}\n onInput={(e) => setSearchQuery(e.currentTarget.value)}\n placeholder=\"Search…\"\n class=\"px-2 py-0.5 text-xs border border-gray-200 dark:border-gray-600 rounded bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-400 focus:border-blue-400 focus:ring-1 focus:ring-blue-400 outline-none w-32\"\n aria-label=\"Search in code\"\n />\n {/* Download button (v6.2.0) */}\n <button\n onClick={handleDownload}\n class=\"text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 focus:outline-none transition-colors\"\n aria-label=\"Download code as file\"\n title=\"Download code\"\n >\n <svg class=\"w-4 h-4\" 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 {/* Word wrap toggle */}\n <button\n onClick={() => setWordWrap(!wordWrap())}\n class={`focus:outline-none transition-colors ${wordWrap() ? 'text-blue-500 dark:text-blue-400' : 'text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200'}`}\n aria-label=\"Toggle word wrap\"\n title={wordWrap() ? 'Disable word wrap' : 'Enable word wrap'}\n >\n <svg class=\"w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 10h10a4 4 0 010 8H9m4 0l-3-3m3 3l-3 3M3 6h18M3 14h4\" />\n </svg>\n </button>\n {/* Copy button */}\n <button\n onClick={handleCopy}\n class=\"text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 focus:outline-none transition-colors\"\n aria-label=\"Copy code\"\n title=\"Copy code\"\n >\n <Show when={isCopied()} fallback={\n <svg class=\"w-4 h-4\" 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 }>\n <svg class=\"w-4 h-4 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 </Show>\n </button>\n </div>\n </div>\n\n {/* Code Area */}\n <div\n class={`relative overflow-auto flex ${isExpanded() ? 'flex-1 min-h-0' : ''}`}\n style={!isExpanded() && params()?.maxHeight ? { 'max-height': params()?.maxHeight } : {}}\n >\n {/* Line Numbers */}\n <Show when={params()?.showLineNumbers !== false}>\n <div class=\"flex-none text-right select-none bg-gray-100 dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 py-4 px-2 text-gray-400 font-mono text-xs leading-5\">\n <For each={lineNumbers()}>\n {(num) => <div class=\"px-2\">{num}</div>}\n </For>\n </div>\n </Show>\n\n {/* Code Content - Sprint Ultimate U.1: data-theme for reactive theming */}\n <pre\n class=\"flex-1 m-0 p-4 font-mono text-gray-800 dark:text-gray-100 bg-transparent leading-5\"\n style={wordWrap() ? { 'white-space': 'pre-wrap', 'word-break': 'break-all' } : {}}\n data-theme={activeTheme()}\n >\n <code\n class={`hljs ${params()?.language ? `language-${params()?.language}` : ''}`}\n innerHTML={displayedHTML()}\n />\n </pre>\n </div>\n </div>\n </ExpandableWrapper>\n )\n}\n"],"names":["LANGUAGE_EXTENSIONS","typescript","tsx","javascript","jsx","python","ruby","go","rust","java","kotlin","swift","php","csharp","cpp","c","sql","json","yaml","toml","bash","shell","html","css","scss","markdown","xml","graphql","hljs","stylesLoaded","CodeBlockRenderer","props","highlightedCode","setHighlightedCode","createSignal","isCopied","setIsCopied","isHljsLoaded","setIsHljsLoaded","activeTheme","setActiveTheme","wordWrap","setWordWrap","params","component","isExpanded","useExpanded","searchQuery","setSearchQuery","displayedHTML","q","trim","highlightQuery","handleDownload","code","lang","language","toLowerCase","ext","stem","filename","Date","now","replace","endsWith","blob","Blob","type","url","URL","createObjectURL","a","document","createElement","href","download","body","appendChild","click","removeChild","revokeObjectURL","createEffect","module","resolved","default","highlight","console","warn","e","isServer","Promise","all","paramTheme","theme","window","matchMedia","mediaQuery","matches","handleChange","addEventListener","onCleanup","removeEventListener","result","getLanguage","value","highlightAuto","lineNumbers","showLineNumbers","lines","split","start","startLine","map","_","i","handleCopy","navigator","clipboard","writeText","setTimeout","error","_$createComponent","ExpandableWrapper","title","copyData","copyLabel","children","_el$","_$getNextElement","_tmpl$3","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$5","_el$6","_el$7","_el$8","_el$0","_el$12","_el$13","_co$","_$getNextMarker","_el$10","_el$11","_$insert","$$input","currentTarget","$$click","Show","when","fallback","_tmpl$4","_tmpl$","_el$1","_tmpl$2","For","each","num","_el$15","_tmpl$5","_$effect","_p$","_v$","_v$2","_v$3","_v$4","_v$5","maxHeight","_v$6","_v$7","_v$8","_v$9","_$className","t","_$setAttribute","o","_$style","n","s","h","r","_$setProperty","undefined","_$runHydrationEvents","_$delegateEvents"],"mappings":"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"}
1
+ {"version":3,"file":"CodeBlockRenderer.cjs","sources":["../../src/components/CodeBlockRenderer.tsx"],"sourcesContent":["/**\n * CodeBlockRenderer - Syntax highlighted code block\n * Sprint 6: Code & Maps\n * Sprint Ultimate: Theme Synchronization (U.1)\n */\n\nimport { Component, createEffect, onCleanup, createSignal, Show, For } from 'solid-js'\nimport { isServer } from 'solid-js/web'\nimport type { UIComponent, CodeComponentParams } from '../types'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\nimport { highlightQuery } from './UIResourceRenderer'\n\n/** Map of `params.language` → file extension for the v6.2.0 download button. */\nconst LANGUAGE_EXTENSIONS: Record<string, string> = {\n typescript: 'ts', tsx: 'tsx', javascript: 'js', jsx: 'jsx',\n python: 'py', ruby: 'rb', go: 'go', rust: 'rs', java: 'java',\n kotlin: 'kt', swift: 'swift', php: 'php', csharp: 'cs', cpp: 'cpp',\n c: 'c', sql: 'sql', json: 'json', yaml: 'yml', toml: 'toml',\n bash: 'sh', shell: 'sh', html: 'html', css: 'css', scss: 'scss',\n markdown: 'md', xml: 'xml', graphql: 'graphql',\n}\n\n// Lazy load highlight.js\nlet hljs: any = null\n// Track if styles have been loaded globally\nlet stylesLoaded = false\n\nexport interface CodeBlockRendererProps {\n /**\n * UIComponent containing code params\n */\n component?: UIComponent\n\n /**\n * Direct code params\n */\n params?: CodeComponentParams\n\n /**\n * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).\n * @see ExpandableWrapperProps.toolbarVariant\n */\n toolbarVariant?: 'hover' | 'always-visible'\n}\n\nexport const CodeBlockRenderer: Component<CodeBlockRendererProps> = (props) => {\n const [highlightedCode, setHighlightedCode] = createSignal<string>('')\n const [isCopied, setIsCopied] = createSignal(false)\n const [isHljsLoaded, setIsHljsLoaded] = createSignal(false)\n const [activeTheme, setActiveTheme] = createSignal<'light' | 'dark'>('dark')\n const [wordWrap, setWordWrap] = createSignal(false)\n\n const params = () => props.params || (props.component?.params as CodeComponentParams)\n const isExpanded = useExpanded()\n const [searchQuery, setSearchQuery] = createSignal('')\n\n // v6.2.0 — search highlight: re-wraps `<mark>` around matches in the\n // already-highlighted (hljs) HTML output. `highlightQuery` is the same\n // helper TableRenderer uses (only wraps text outside of HTML tags so\n // syntax span colors stay intact).\n const displayedHTML = () => {\n const q = searchQuery().trim()\n return q ? highlightQuery(highlightedCode(), q) : highlightedCode()\n }\n\n const handleDownload = () => {\n const code = params()?.code\n if (!code) return\n const lang = (params()?.language || '').toLowerCase()\n const ext = LANGUAGE_EXTENSIONS[lang] || 'txt'\n const stem = (params()?.filename || `code-${Date.now()}`).replace(/\\.[^.]+$/, '')\n const filename = stem.endsWith(`.${ext}`) ? stem : `${stem}.${ext}`\n const blob = new Blob([code], { type: 'text/plain' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = filename\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n URL.revokeObjectURL(url)\n }\n\n // Load highlight.js on mount\n createEffect(async () => {\n if (!hljs) {\n try {\n // Use the full highlight.js bundle with all languages for simplicity\n const module = await import('highlight.js')\n const resolved = module.default || module\n // Guard: verify the resolved module has the expected API\n if (typeof resolved?.highlight === 'function') {\n hljs = resolved\n } else {\n console.warn('highlight.js loaded but missing highlight() method')\n }\n setIsHljsLoaded(true)\n } catch (e) {\n console.warn('Failed to load highlight.js', e)\n // Continue without highlighting - fallback to plain text\n setIsHljsLoaded(true)\n }\n } else {\n setIsHljsLoaded(true)\n }\n })\n\n // Theme management - Sprint Ultimate U.1: Reactive theme synchronization\n // Load both theme stylesheets once, then toggle via data-attribute\n createEffect(async () => {\n if (isServer || stylesLoaded) return\n\n // Load both themes upfront for instant switching\n try {\n await Promise.all([\n import('highlight.js/styles/github.css'),\n import('highlight.js/styles/github-dark.css')\n ])\n stylesLoaded = true\n } catch (e) {\n console.warn('Failed to load highlight.js themes', e)\n }\n })\n\n // Reactive theme detection - listens to system preference changes\n createEffect(() => {\n if (isServer) return\n\n // Priority 1: Explicit theme from params\n const paramTheme = params()?.theme\n if (paramTheme) {\n setActiveTheme(paramTheme)\n return\n }\n\n // Priority 2: System preference with live updates\n // Check if matchMedia is available (not in all test environments)\n if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n setActiveTheme(mediaQuery.matches ? 'dark' : 'light')\n\n const handleChange = (e: MediaQueryListEvent) => {\n // Only update if no explicit theme in params\n if (!params()?.theme) {\n setActiveTheme(e.matches ? 'dark' : 'light')\n }\n }\n\n mediaQuery.addEventListener('change', handleChange)\n onCleanup(() => mediaQuery.removeEventListener('change', handleChange))\n }\n })\n\n // Apply highlighting\n createEffect(() => {\n const code = params()?.code || ''\n const language = params()?.language || ''\n\n if (hljs && isHljsLoaded()) {\n try {\n let result\n if (language && hljs.getLanguage(language)) {\n result = hljs.highlight(code, { language }).value\n } else {\n result = hljs.highlightAuto(code).value\n }\n setHighlightedCode(result)\n } catch (e) {\n setHighlightedCode(code.replace(/</g, '&lt;').replace(/>/g, '&gt;'))\n }\n } else {\n // Fallback: simple escaping\n setHighlightedCode(code.replace(/</g, '&lt;').replace(/>/g, '&gt;'))\n }\n })\n\n // Line numbers generation\n const lineNumbers = () => {\n if (params()?.showLineNumbers === false) return []\n const code = params()?.code || ''\n const lines = code.split('\\n')\n const start = params()?.startLine || 1\n return lines.map((_, i) => start + i)\n }\n\n const handleCopy = async () => {\n const code = params()?.code\n if (code) {\n try {\n await navigator.clipboard.writeText(code)\n setIsCopied(true)\n setTimeout(() => setIsCopied(false), 2000)\n } catch (e) {\n console.error('Failed to copy code', e)\n }\n }\n }\n\n return (\n <ExpandableWrapper title={params()?.filename || params()?.language || 'Code'} copyData={params()?.code} copyLabel=\"Copy code\" toolbarVariant={props.toolbarVariant}>\n <div class={`w-full bg-gray-50 dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden text-sm flex flex-col ${isExpanded() ? 'flex-1 min-h-0' : ''}`}>\n {/* Header */}\n <div class=\"flex items-center justify-between px-4 py-2 bg-gray-100 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 shrink-0\">\n <div class=\"font-mono text-xs text-gray-600 dark:text-gray-400\">\n {params()?.filename || params()?.language || 'Code'}\n </div>\n <div class=\"flex items-center gap-2\">\n {/* Search input (v6.2.0) */}\n <input\n type=\"text\"\n value={searchQuery()}\n onInput={(e) => setSearchQuery(e.currentTarget.value)}\n placeholder=\"Search…\"\n class=\"px-2 py-0.5 text-xs border border-gray-200 dark:border-gray-600 rounded bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-400 focus:border-blue-400 focus:ring-1 focus:ring-blue-400 outline-none w-32\"\n aria-label=\"Search in code\"\n />\n {/* Download button (v6.2.0) */}\n <button\n onClick={handleDownload}\n class=\"text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 focus:outline-none transition-colors\"\n aria-label=\"Download code as file\"\n title=\"Download code\"\n >\n <svg class=\"w-4 h-4\" 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 {/* Word wrap toggle */}\n <button\n onClick={() => setWordWrap(!wordWrap())}\n class={`focus:outline-none transition-colors ${wordWrap() ? 'text-blue-500 dark:text-blue-400' : 'text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200'}`}\n aria-label=\"Toggle word wrap\"\n title={wordWrap() ? 'Disable word wrap' : 'Enable word wrap'}\n >\n <svg class=\"w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 10h10a4 4 0 010 8H9m4 0l-3-3m3 3l-3 3M3 6h18M3 14h4\" />\n </svg>\n </button>\n {/* Copy button */}\n <button\n onClick={handleCopy}\n class=\"text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 focus:outline-none transition-colors\"\n aria-label=\"Copy code\"\n title=\"Copy code\"\n >\n <Show when={isCopied()} fallback={\n <svg class=\"w-4 h-4\" 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 }>\n <svg class=\"w-4 h-4 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 </Show>\n </button>\n </div>\n </div>\n\n {/* Code Area */}\n <div\n class={`relative overflow-auto flex ${isExpanded() ? 'flex-1 min-h-0' : ''}`}\n style={!isExpanded() && params()?.maxHeight ? { 'max-height': params()?.maxHeight } : {}}\n >\n {/* Line Numbers */}\n <Show when={params()?.showLineNumbers !== false}>\n <div class=\"flex-none text-right select-none bg-gray-100 dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 py-4 px-2 text-gray-400 font-mono text-xs leading-5\">\n <For each={lineNumbers()}>\n {(num) => <div class=\"px-2\">{num}</div>}\n </For>\n </div>\n </Show>\n\n {/* Code Content - Sprint Ultimate U.1: data-theme for reactive theming */}\n <pre\n class=\"flex-1 m-0 p-4 font-mono text-gray-800 dark:text-gray-100 bg-transparent leading-5\"\n style={wordWrap() ? { 'white-space': 'pre-wrap', 'word-break': 'break-all' } : {}}\n data-theme={activeTheme()}\n >\n <code\n class={`hljs ${params()?.language ? `language-${params()?.language}` : ''}`}\n innerHTML={displayedHTML()}\n />\n </pre>\n </div>\n </div>\n </ExpandableWrapper>\n )\n}\n"],"names":["LANGUAGE_EXTENSIONS","typescript","tsx","javascript","jsx","python","ruby","go","rust","java","kotlin","swift","php","csharp","cpp","c","sql","json","yaml","toml","bash","shell","html","css","scss","markdown","xml","graphql","hljs","stylesLoaded","CodeBlockRenderer","props","highlightedCode","setHighlightedCode","createSignal","isCopied","setIsCopied","isHljsLoaded","setIsHljsLoaded","activeTheme","setActiveTheme","wordWrap","setWordWrap","params","component","isExpanded","useExpanded","searchQuery","setSearchQuery","displayedHTML","q","trim","highlightQuery","handleDownload","code","lang","language","toLowerCase","ext","stem","filename","Date","now","replace","endsWith","blob","Blob","type","url","URL","createObjectURL","a","document","createElement","href","download","body","appendChild","click","removeChild","revokeObjectURL","createEffect","module","resolved","default","highlight","console","warn","e","isServer","Promise","all","paramTheme","theme","window","matchMedia","mediaQuery","matches","handleChange","addEventListener","onCleanup","removeEventListener","result","getLanguage","value","highlightAuto","lineNumbers","showLineNumbers","lines","split","start","startLine","map","_","i","handleCopy","navigator","clipboard","writeText","setTimeout","error","_$createComponent","ExpandableWrapper","title","copyData","copyLabel","toolbarVariant","children","_el$","_$getNextElement","_tmpl$3","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$5","_el$6","_el$7","_el$8","_el$0","_el$12","_el$13","_co$","_$getNextMarker","_el$10","_el$11","_$insert","$$input","currentTarget","$$click","Show","when","fallback","_tmpl$4","_tmpl$","_el$1","_tmpl$2","For","each","num","_el$15","_tmpl$5","_$effect","_p$","_v$","_v$2","_v$3","_v$4","_v$5","maxHeight","_v$6","_v$7","_v$8","_v$9","_$className","t","_$setAttribute","o","_$style","n","s","h","r","_$setProperty","undefined","_$runHydrationEvents","_$delegateEvents"],"mappings":"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"}
@@ -14,6 +14,11 @@ export interface CodeBlockRendererProps {
14
14
  * Direct code params
15
15
  */
16
16
  params?: CodeComponentParams;
17
+ /**
18
+ * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).
19
+ * @see ExpandableWrapperProps.toolbarVariant
20
+ */
21
+ toolbarVariant?: 'hover' | 'always-visible';
17
22
  }
18
23
  export declare const CodeBlockRenderer: Component<CodeBlockRendererProps>;
19
24
  //# sourceMappingURL=CodeBlockRenderer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CodeBlockRenderer.d.ts","sourceRoot":"","sources":["../../src/components/CodeBlockRenderer.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAoD,MAAM,UAAU,CAAA;AAEtF,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AAmBhE,MAAM,WAAW,sBAAsB;IACnC;;OAEG;IACH,SAAS,CAAC,EAAE,WAAW,CAAA;IAEvB;;OAEG;IACH,MAAM,CAAC,EAAE,mBAAmB,CAAA;CAC/B;AAED,eAAO,MAAM,iBAAiB,EAAE,SAAS,CAAC,sBAAsB,CAkP/D,CAAA"}
1
+ {"version":3,"file":"CodeBlockRenderer.d.ts","sourceRoot":"","sources":["../../src/components/CodeBlockRenderer.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAoD,MAAM,UAAU,CAAA;AAEtF,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AAmBhE,MAAM,WAAW,sBAAsB;IACnC;;OAEG;IACH,SAAS,CAAC,EAAE,WAAW,CAAA;IAEvB;;OAEG;IACH,MAAM,CAAC,EAAE,mBAAmB,CAAA;IAE5B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAA;CAC9C;AAED,eAAO,MAAM,iBAAiB,EAAE,SAAS,CAAC,sBAAsB,CAkP/D,CAAA"}
@@ -172,6 +172,9 @@ const CodeBlockRenderer = (props) => {
172
172
  return (_a = params()) == null ? void 0 : _a.code;
173
173
  },
174
174
  copyLabel: "Copy code",
175
+ get toolbarVariant() {
176
+ return props.toolbarVariant;
177
+ },
175
178
  get children() {
176
179
  var _el$ = getNextElement(_tmpl$3), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$4 = _el$3.nextSibling, _el$5 = _el$4.firstChild, _el$6 = _el$5.nextSibling, _el$7 = _el$6.nextSibling, _el$8 = _el$7.nextSibling, _el$0 = _el$2.nextSibling, _el$12 = _el$0.firstChild, [_el$13, _co$] = getNextMarker(_el$12.nextSibling), _el$10 = _el$13.nextSibling, _el$11 = _el$10.firstChild;
177
180
  insert(_el$3, () => {