@seed-ship/mcp-ui-solid 2.1.3 → 2.2.4

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 (62) hide show
  1. package/dist/components/ChartJSRenderer.cjs +79 -36
  2. package/dist/components/ChartJSRenderer.cjs.map +1 -1
  3. package/dist/components/ChartJSRenderer.d.ts.map +1 -1
  4. package/dist/components/ChartJSRenderer.js +80 -37
  5. package/dist/components/ChartJSRenderer.js.map +1 -1
  6. package/dist/components/CodeBlockRenderer.cjs +79 -56
  7. package/dist/components/CodeBlockRenderer.cjs.map +1 -1
  8. package/dist/components/CodeBlockRenderer.d.ts.map +1 -1
  9. package/dist/components/CodeBlockRenderer.js +80 -57
  10. package/dist/components/CodeBlockRenderer.js.map +1 -1
  11. package/dist/components/ExpandableWrapper.cjs +136 -0
  12. package/dist/components/ExpandableWrapper.cjs.map +1 -0
  13. package/dist/components/ExpandableWrapper.d.ts +31 -0
  14. package/dist/components/ExpandableWrapper.d.ts.map +1 -0
  15. package/dist/components/ExpandableWrapper.js +136 -0
  16. package/dist/components/ExpandableWrapper.js.map +1 -0
  17. package/dist/components/UIResourceRenderer.cjs +369 -242
  18. package/dist/components/UIResourceRenderer.cjs.map +1 -1
  19. package/dist/components/UIResourceRenderer.d.ts +4 -0
  20. package/dist/components/UIResourceRenderer.d.ts.map +1 -1
  21. package/dist/components/UIResourceRenderer.js +370 -243
  22. package/dist/components/UIResourceRenderer.js.map +1 -1
  23. package/dist/index.cjs +3 -0
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +2 -0
  26. package/dist/index.d.ts +2 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +3 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/node_modules/.pnpm/{dompurify@3.3.0 → dompurify@3.3.3}/node_modules/dompurify/dist/purify.es.cjs +19 -4
  31. package/dist/node_modules/.pnpm/dompurify@3.3.3/node_modules/dompurify/dist/purify.es.cjs.map +1 -0
  32. package/dist/node_modules/.pnpm/{dompurify@3.3.0 → dompurify@3.3.3}/node_modules/dompurify/dist/purify.es.js +19 -4
  33. package/dist/node_modules/.pnpm/dompurify@3.3.3/node_modules/dompurify/dist/purify.es.js.map +1 -0
  34. package/dist/services/component-registry.cjs.map +1 -1
  35. package/dist/services/component-registry.d.ts +1 -0
  36. package/dist/services/component-registry.d.ts.map +1 -1
  37. package/dist/services/component-registry.js.map +1 -1
  38. package/dist/services/validation.cjs +29 -5
  39. package/dist/services/validation.cjs.map +1 -1
  40. package/dist/services/validation.d.ts.map +1 -1
  41. package/dist/services/validation.js +29 -5
  42. package/dist/services/validation.js.map +1 -1
  43. package/dist/types/index.d.ts +17 -0
  44. package/dist/types/index.d.ts.map +1 -1
  45. package/dist/types.d.cts +17 -0
  46. package/dist/types.d.ts +17 -0
  47. package/package.json +3 -3
  48. package/src/components/ChartJSRenderer.tsx +71 -42
  49. package/src/components/CodeBlockRenderer.tsx +33 -14
  50. package/src/components/ExpandableWrapper.test.tsx +229 -0
  51. package/src/components/ExpandableWrapper.tsx +201 -0
  52. package/src/components/UIResourceRenderer.tsx +165 -62
  53. package/src/components/renderCellValue.test.ts +122 -0
  54. package/src/index.ts +2 -0
  55. package/src/services/component-registry.test.ts +81 -0
  56. package/src/services/component-registry.ts +3 -2
  57. package/src/services/validation.test.ts +134 -0
  58. package/src/services/validation.ts +21 -5
  59. package/src/types/index.ts +17 -0
  60. package/tsconfig.tsbuildinfo +1 -1
  61. package/dist/node_modules/.pnpm/dompurify@3.3.0/node_modules/dompurify/dist/purify.es.cjs.map +0 -1
  62. package/dist/node_modules/.pnpm/dompurify@3.3.0/node_modules/dompurify/dist/purify.es.js.map +0 -1
@@ -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'\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\n const params = () => props.params || (props.component?.params as CodeComponentParams)\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 hljs = module.default || module\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 <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\">\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 <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\n {/* Code Area */}\n <div\n class=\"relative overflow-auto flex\"\n style={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 data-theme={activeTheme()}\n >\n <code\n class={`hljs ${params()?.language ? `language-${params()?.language}` : ''}`}\n innerHTML={highlightedCode()}\n />\n </pre>\n </div>\n </div>\n )\n}\n"],"names":["hljs","stylesLoaded","CodeBlockRenderer","props","highlightedCode","setHighlightedCode","createSignal","isCopied","setIsCopied","isHljsLoaded","setIsHljsLoaded","activeTheme","setActiveTheme","params","component","createEffect","module","default","e","console","warn","isServer","Promise","all","paramTheme","theme","window","matchMedia","mediaQuery","matches","handleChange","addEventListener","onCleanup","removeEventListener","code","language","result","getLanguage","highlight","value","highlightAuto","replace","lineNumbers","showLineNumbers","lines","split","start","startLine","map","_","i","handleCopy","navigator","clipboard","writeText","setTimeout","error","_el$","_$getNextElement","_tmpl$3","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$6","_el$0","_el$1","_co$","_$getNextMarker","_el$8","_el$9","_$insert","filename","$$click","_$createComponent","Show","when","fallback","_tmpl$4","children","_tmpl$","_el$7","_tmpl$2","For","each","num","_el$11","_tmpl$5","_$effect","_p$","_v$","maxHeight","_v$2","_v$3","_v$4","_$style","t","_$setAttribute","a","_$className","o","_$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 } from './ExpandableWrapper'\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\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 hljs = module.default || module\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\">\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 {/* 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\"\n style={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={highlightedCode()}\n />\n </pre>\n </div>\n </div>\n </ExpandableWrapper>\n )\n}\n"],"names":["hljs","stylesLoaded","CodeBlockRenderer","props","highlightedCode","setHighlightedCode","createSignal","isCopied","setIsCopied","isHljsLoaded","setIsHljsLoaded","activeTheme","setActiveTheme","wordWrap","setWordWrap","params","component","createEffect","module","default","e","console","warn","isServer","Promise","all","paramTheme","theme","window","matchMedia","mediaQuery","matches","handleChange","addEventListener","onCleanup","removeEventListener","code","language","result","getLanguage","highlight","value","highlightAuto","replace","lineNumbers","showLineNumbers","lines","split","start","startLine","map","_","i","handleCopy","navigator","clipboard","writeText","setTimeout","error","_$createComponent","ExpandableWrapper","title","filename","copyData","copyLabel","children","_el$","_$getNextElement","_tmpl$3","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$5","_el$6","_el$8","_el$10","_el$11","_co$","_$getNextMarker","_el$0","_el$1","_$insert","$$click","Show","when","fallback","_tmpl$4","_tmpl$","_el$9","_tmpl$2","For","each","num","_el$13","_tmpl$5","_$effect","_p$","_v$","_v$2","_v$3","maxHeight","_v$4","_v$5","_v$6","_v$7","_$className","t","_$setAttribute","a","_$style","o","n","s","_$setProperty","undefined","_$runHydrationEvents","_$delegateEvents"],"mappings":"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"}
@@ -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;AAOhE,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,CAyK/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;AAQhE,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,CA2L/D,CAAA"}
@@ -1,6 +1,7 @@
1
- import { delegateEvents, isServer, getNextElement, template, getNextMarker, insert, createComponent, effect, style, setAttribute, className, setProperty, runHydrationEvents } from "solid-js/web";
1
+ import { delegateEvents, isServer, createComponent, getNextElement, template, getNextMarker, insert, effect, className, setAttribute, style, setProperty, runHydrationEvents } from "solid-js/web";
2
2
  import { createSignal, createEffect, onCleanup, Show, For } from "solid-js";
3
- var _tmpl$ = /* @__PURE__ */ template(`<svg class="w-4 h-4 text-green-500"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M5 13l4 4L19 7">`), _tmpl$2 = /* @__PURE__ */ template(`<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">`), _tmpl$3 = /* @__PURE__ */ template(`<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"><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"><div class="font-mono text-xs text-gray-600 dark:text-gray-400"></div><button class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 focus:outline-none transition-colors"aria-label="Copy code"title="Copy code"></button></div><div class="relative overflow-auto flex"><!$><!/><pre class="flex-1 m-0 p-4 font-mono text-gray-800 dark:text-gray-100 bg-transparent leading-5"><code>`), _tmpl$4 = /* @__PURE__ */ template(`<svg class="w-4 h-4"fill=none viewBox="0 0 24 24"stroke=currentColor><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">`), _tmpl$5 = /* @__PURE__ */ template(`<div class=px-2>`);
3
+ import { ExpandableWrapper } from "./ExpandableWrapper.js";
4
+ var _tmpl$ = /* @__PURE__ */ template(`<svg class="w-4 h-4 text-green-500"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M5 13l4 4L19 7">`), _tmpl$2 = /* @__PURE__ */ template(`<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">`), _tmpl$3 = /* @__PURE__ */ template(`<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"><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"><div class="font-mono text-xs text-gray-600 dark:text-gray-400"></div><div class="flex items-center gap-2"><button aria-label="Toggle word wrap"><svg class="w-4 h-4"fill=none viewBox="0 0 24 24"stroke=currentColor><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"></path></svg></button><button class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 focus:outline-none transition-colors"aria-label="Copy code"title="Copy code"></button></div></div><div class="relative overflow-auto flex"><!$><!/><pre class="flex-1 m-0 p-4 font-mono text-gray-800 dark:text-gray-100 bg-transparent leading-5"><code>`), _tmpl$4 = /* @__PURE__ */ template(`<svg class="w-4 h-4"fill=none viewBox="0 0 24 24"stroke=currentColor><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">`), _tmpl$5 = /* @__PURE__ */ template(`<div class=px-2>`);
4
5
  let hljs = null;
5
6
  let stylesLoaded = false;
6
7
  const CodeBlockRenderer = (props) => {
@@ -8,6 +9,7 @@ const CodeBlockRenderer = (props) => {
8
9
  const [isCopied, setIsCopied] = createSignal(false);
9
10
  const [isHljsLoaded, setIsHljsLoaded] = createSignal(false);
10
11
  const [activeTheme, setActiveTheme] = createSignal("dark");
12
+ const [wordWrap, setWordWrap] = createSignal(false);
11
13
  const params = () => {
12
14
  var _a;
13
15
  return props.params || ((_a = props.component) == null ? void 0 : _a.params);
@@ -99,63 +101,84 @@ const CodeBlockRenderer = (props) => {
99
101
  }
100
102
  }
101
103
  };
102
- return (() => {
103
- var _el$ = getNextElement(_tmpl$3), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$4 = _el$3.nextSibling, _el$6 = _el$2.nextSibling, _el$0 = _el$6.firstChild, [_el$1, _co$] = getNextMarker(_el$0.nextSibling), _el$8 = _el$1.nextSibling, _el$9 = _el$8.firstChild;
104
- insert(_el$3, () => {
104
+ return createComponent(ExpandableWrapper, {
105
+ get title() {
105
106
  var _a, _b;
106
107
  return ((_a = params()) == null ? void 0 : _a.filename) || ((_b = params()) == null ? void 0 : _b.language) || "Code";
107
- });
108
- _el$4.$$click = handleCopy;
109
- insert(_el$4, createComponent(Show, {
110
- get when() {
111
- return isCopied();
112
- },
113
- get fallback() {
114
- return getNextElement(_tmpl$4);
115
- },
116
- get children() {
117
- return getNextElement(_tmpl$);
118
- }
119
- }));
120
- insert(_el$6, createComponent(Show, {
121
- get when() {
122
- var _a;
123
- return ((_a = params()) == null ? void 0 : _a.showLineNumbers) !== false;
124
- },
125
- get children() {
126
- var _el$7 = getNextElement(_tmpl$2);
127
- insert(_el$7, createComponent(For, {
128
- get each() {
129
- return lineNumbers();
130
- },
131
- children: (num) => (() => {
132
- var _el$11 = getNextElement(_tmpl$5);
133
- insert(_el$11, num);
134
- return _el$11;
135
- })()
136
- }));
137
- return _el$7;
138
- }
139
- }), _el$1, _co$);
140
- effect((_p$) => {
141
- var _a, _b, _c, _d;
142
- var _v$ = ((_a = params()) == null ? void 0 : _a.maxHeight) ? {
143
- "max-height": (_b = params()) == null ? void 0 : _b.maxHeight
144
- } : {}, _v$2 = activeTheme(), _v$3 = `hljs ${((_c = params()) == null ? void 0 : _c.language) ? `language-${(_d = params()) == null ? void 0 : _d.language}` : ""}`, _v$4 = highlightedCode();
145
- _p$.e = style(_el$6, _v$, _p$.e);
146
- _v$2 !== _p$.t && setAttribute(_el$8, "data-theme", _p$.t = _v$2);
147
- _v$3 !== _p$.a && className(_el$9, _p$.a = _v$3);
148
- _v$4 !== _p$.o && setProperty(_el$9, "innerHTML", _p$.o = _v$4);
149
- return _p$;
150
- }, {
151
- e: void 0,
152
- t: void 0,
153
- a: void 0,
154
- o: void 0
155
- });
156
- runHydrationEvents();
157
- return _el$;
158
- })();
108
+ },
109
+ get copyData() {
110
+ var _a;
111
+ return (_a = params()) == null ? void 0 : _a.code;
112
+ },
113
+ copyLabel: "Copy code",
114
+ get children() {
115
+ 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$8 = _el$2.nextSibling, _el$10 = _el$8.firstChild, [_el$11, _co$] = getNextMarker(_el$10.nextSibling), _el$0 = _el$11.nextSibling, _el$1 = _el$0.firstChild;
116
+ insert(_el$3, () => {
117
+ var _a, _b;
118
+ return ((_a = params()) == null ? void 0 : _a.filename) || ((_b = params()) == null ? void 0 : _b.language) || "Code";
119
+ });
120
+ _el$5.$$click = () => setWordWrap(!wordWrap());
121
+ _el$6.$$click = handleCopy;
122
+ insert(_el$6, createComponent(Show, {
123
+ get when() {
124
+ return isCopied();
125
+ },
126
+ get fallback() {
127
+ return getNextElement(_tmpl$4);
128
+ },
129
+ get children() {
130
+ return getNextElement(_tmpl$);
131
+ }
132
+ }));
133
+ insert(_el$8, createComponent(Show, {
134
+ get when() {
135
+ var _a;
136
+ return ((_a = params()) == null ? void 0 : _a.showLineNumbers) !== false;
137
+ },
138
+ get children() {
139
+ var _el$9 = getNextElement(_tmpl$2);
140
+ insert(_el$9, createComponent(For, {
141
+ get each() {
142
+ return lineNumbers();
143
+ },
144
+ children: (num) => (() => {
145
+ var _el$13 = getNextElement(_tmpl$5);
146
+ insert(_el$13, num);
147
+ return _el$13;
148
+ })()
149
+ }));
150
+ return _el$9;
151
+ }
152
+ }), _el$11, _co$);
153
+ effect((_p$) => {
154
+ var _a, _b, _c, _d;
155
+ var _v$ = `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"}`, _v$2 = wordWrap() ? "Disable word wrap" : "Enable word wrap", _v$3 = ((_a = params()) == null ? void 0 : _a.maxHeight) ? {
156
+ "max-height": (_b = params()) == null ? void 0 : _b.maxHeight
157
+ } : {}, _v$4 = wordWrap() ? {
158
+ "white-space": "pre-wrap",
159
+ "word-break": "break-all"
160
+ } : {}, _v$5 = activeTheme(), _v$6 = `hljs ${((_c = params()) == null ? void 0 : _c.language) ? `language-${(_d = params()) == null ? void 0 : _d.language}` : ""}`, _v$7 = highlightedCode();
161
+ _v$ !== _p$.e && className(_el$5, _p$.e = _v$);
162
+ _v$2 !== _p$.t && setAttribute(_el$5, "title", _p$.t = _v$2);
163
+ _p$.a = style(_el$8, _v$3, _p$.a);
164
+ _p$.o = style(_el$0, _v$4, _p$.o);
165
+ _v$5 !== _p$.i && setAttribute(_el$0, "data-theme", _p$.i = _v$5);
166
+ _v$6 !== _p$.n && className(_el$1, _p$.n = _v$6);
167
+ _v$7 !== _p$.s && setProperty(_el$1, "innerHTML", _p$.s = _v$7);
168
+ return _p$;
169
+ }, {
170
+ e: void 0,
171
+ t: void 0,
172
+ a: void 0,
173
+ o: void 0,
174
+ i: void 0,
175
+ n: void 0,
176
+ s: void 0
177
+ });
178
+ runHydrationEvents();
179
+ return _el$;
180
+ }
181
+ });
159
182
  };
160
183
  delegateEvents(["click"]);
161
184
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"CodeBlockRenderer.js","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'\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\n const params = () => props.params || (props.component?.params as CodeComponentParams)\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 hljs = module.default || module\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 <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\">\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 <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\n {/* Code Area */}\n <div\n class=\"relative overflow-auto flex\"\n style={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 data-theme={activeTheme()}\n >\n <code\n class={`hljs ${params()?.language ? `language-${params()?.language}` : ''}`}\n innerHTML={highlightedCode()}\n />\n </pre>\n </div>\n </div>\n )\n}\n"],"names":["hljs","stylesLoaded","CodeBlockRenderer","props","highlightedCode","setHighlightedCode","createSignal","isCopied","setIsCopied","isHljsLoaded","setIsHljsLoaded","activeTheme","setActiveTheme","params","component","createEffect","module","default","e","console","warn","isServer","Promise","all","paramTheme","theme","window","matchMedia","mediaQuery","matches","handleChange","addEventListener","onCleanup","removeEventListener","code","language","result","getLanguage","highlight","value","highlightAuto","replace","lineNumbers","showLineNumbers","lines","split","start","startLine","map","_","i","handleCopy","navigator","clipboard","writeText","setTimeout","error","_el$","_$getNextElement","_tmpl$3","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$6","_el$0","_el$1","_co$","_$getNextMarker","_el$8","_el$9","_$insert","filename","$$click","_$createComponent","Show","when","fallback","_tmpl$4","children","_tmpl$","_el$7","_tmpl$2","For","each","num","_el$11","_tmpl$5","_$effect","_p$","_v$","maxHeight","_v$2","_v$3","_v$4","_$style","t","_$setAttribute","a","_$className","o","_$setProperty","undefined","_$runHydrationEvents","_$delegateEvents"],"mappings":"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"}
1
+ {"version":3,"file":"CodeBlockRenderer.js","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 } from './ExpandableWrapper'\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\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 hljs = module.default || module\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\">\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 {/* 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\"\n style={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={highlightedCode()}\n />\n </pre>\n </div>\n </div>\n </ExpandableWrapper>\n )\n}\n"],"names":["hljs","stylesLoaded","CodeBlockRenderer","props","highlightedCode","setHighlightedCode","createSignal","isCopied","setIsCopied","isHljsLoaded","setIsHljsLoaded","activeTheme","setActiveTheme","wordWrap","setWordWrap","params","component","createEffect","module","default","e","console","warn","isServer","Promise","all","paramTheme","theme","window","matchMedia","mediaQuery","matches","handleChange","addEventListener","onCleanup","removeEventListener","code","language","result","getLanguage","highlight","value","highlightAuto","replace","lineNumbers","showLineNumbers","lines","split","start","startLine","map","_","i","handleCopy","navigator","clipboard","writeText","setTimeout","error","_$createComponent","ExpandableWrapper","title","filename","copyData","copyLabel","children","_el$","_$getNextElement","_tmpl$3","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$5","_el$6","_el$8","_el$10","_el$11","_co$","_$getNextMarker","_el$0","_el$1","_$insert","$$click","Show","when","fallback","_tmpl$4","_tmpl$","_el$9","_tmpl$2","For","each","num","_el$13","_tmpl$5","_$effect","_p$","_v$","_v$2","_v$3","maxHeight","_v$4","_v$5","_v$6","_v$7","_$className","t","_$setAttribute","a","_$style","o","n","s","_$setProperty","undefined","_$runHydrationEvents","_$delegateEvents"],"mappings":"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"}
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const web = require("solid-js/web");
4
+ const solidJs = require("solid-js");
5
+ var _tmpl$ = /* @__PURE__ */ web.template(`<svg class="w-5 h-5"fill=none viewBox="0 0 24 24"stroke=currentColor><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">`), _tmpl$2 = /* @__PURE__ */ web.template(`<button class="p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors">`), _tmpl$3 = /* @__PURE__ */ web.template(`<div class="fixed inset-0 z-50 flex flex-col bg-black/50 backdrop-blur-sm"role=dialog aria-modal=true tabindex=-1 style="animation:expandable-fade-in 0.15s ease-out"><div class="relative flex flex-col m-4 flex-1 bg-white dark:bg-gray-800 rounded-lg shadow-xl overflow-hidden"style="animation:expandable-scale-in 0.15s ease-out"><div class="flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex-shrink-0"><h2 class="text-lg font-semibold text-gray-900 dark:text-white truncate"></h2><div class="flex items-center gap-2"><!$><!/><button class="p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"aria-label="Close expanded view"><svg class="w-5 h-5"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M6 18L18 6M6 6l12 12"></path></svg></button></div></div><div class="flex-1 overflow-auto p-4">`), _tmpl$4 = /* @__PURE__ */ web.template(`<style>
6
+ @keyframes expandable-fade-in {
7
+ from { opacity: 0; }
8
+ to { opacity: 1; }
9
+ }
10
+ @keyframes expandable-scale-in {
11
+ from { opacity: 0; transform: scale(0.97); }
12
+ to { opacity: 1; transform: scale(1); }
13
+ }
14
+ `), _tmpl$5 = /* @__PURE__ */ web.template(`<div class="relative group"><div><div></div></div><button class="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-70 hover:!opacity-100 p-1.5 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm"title=Expand aria-label="Expand to fullscreen"><svg class="w-3.5 h-3.5 text-gray-500 dark:text-gray-400"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5v-4m0 4h-4m4 0l-5-5"></path></svg></button><!$><!/>`), _tmpl$6 = /* @__PURE__ */ web.template(`<svg class="w-5 h-5 text-green-500"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M5 13l4 4L19 7">`);
15
+ const ExpandableWrapper = (props) => {
16
+ const [isExpanded, setIsExpanded] = solidJs.createSignal(false);
17
+ const [copied, setCopied] = solidJs.createSignal(false);
18
+ let dialogRef;
19
+ let contentRef;
20
+ let inlineSlotRef;
21
+ let modalSlotRef;
22
+ const handleOpen = () => setIsExpanded(true);
23
+ const handleClose = () => setIsExpanded(false);
24
+ solidJs.createEffect(() => {
25
+ if (!contentRef) return;
26
+ if (isExpanded()) {
27
+ modalSlotRef == null ? void 0 : modalSlotRef.appendChild(contentRef);
28
+ } else {
29
+ inlineSlotRef == null ? void 0 : inlineSlotRef.appendChild(contentRef);
30
+ }
31
+ });
32
+ solidJs.createEffect(() => {
33
+ if (!isExpanded()) return;
34
+ const onKeyDown = (e) => {
35
+ if (e.key === "Escape") {
36
+ e.preventDefault();
37
+ handleClose();
38
+ }
39
+ };
40
+ document.addEventListener("keydown", onKeyDown);
41
+ solidJs.onCleanup(() => document.removeEventListener("keydown", onKeyDown));
42
+ });
43
+ solidJs.createEffect(() => {
44
+ if (isExpanded()) {
45
+ const prev = document.body.style.overflow;
46
+ document.body.style.overflow = "hidden";
47
+ setTimeout(() => dialogRef == null ? void 0 : dialogRef.focus(), 10);
48
+ solidJs.onCleanup(() => {
49
+ document.body.style.overflow = prev;
50
+ });
51
+ }
52
+ });
53
+ const handleBackdropClick = (e) => {
54
+ if (e.target === e.currentTarget) handleClose();
55
+ };
56
+ const handleCopy = async () => {
57
+ if (!props.copyData) return;
58
+ try {
59
+ await navigator.clipboard.writeText(props.copyData);
60
+ setCopied(true);
61
+ setTimeout(() => setCopied(false), 2e3);
62
+ } catch (err) {
63
+ console.error("Failed to copy:", err);
64
+ }
65
+ };
66
+ return (() => {
67
+ var _el$ = web.getNextElement(_tmpl$5), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$4 = _el$2.nextSibling, _el$15 = _el$4.nextSibling, [_el$16, _co$2] = web.getNextMarker(_el$15.nextSibling);
68
+ var _ref$ = inlineSlotRef;
69
+ typeof _ref$ === "function" ? web.use(_ref$, _el$2) : inlineSlotRef = _el$2;
70
+ var _ref$2 = contentRef;
71
+ typeof _ref$2 === "function" ? web.use(_ref$2, _el$3) : contentRef = _el$3;
72
+ web.insert(_el$3, () => props.children);
73
+ _el$4.$$click = handleOpen;
74
+ web.insert(_el$, web.createComponent(solidJs.Show, {
75
+ get when() {
76
+ return isExpanded();
77
+ },
78
+ get children() {
79
+ return web.createComponent(web.Portal, {
80
+ get children() {
81
+ return [(() => {
82
+ var _el$5 = web.getNextElement(_tmpl$3), _el$6 = _el$5.firstChild, _el$7 = _el$6.firstChild, _el$8 = _el$7.firstChild, _el$9 = _el$8.nextSibling, _el$11 = _el$9.firstChild, [_el$12, _co$] = web.getNextMarker(_el$11.nextSibling), _el$10 = _el$12.nextSibling, _el$13 = _el$7.nextSibling;
83
+ var _ref$3 = dialogRef;
84
+ typeof _ref$3 === "function" ? web.use(_ref$3, _el$5) : dialogRef = _el$5;
85
+ _el$5.$$click = handleBackdropClick;
86
+ _el$6.$$click = (e) => e.stopPropagation();
87
+ web.insert(_el$8, () => props.title || "Expanded View");
88
+ web.insert(_el$9, web.createComponent(solidJs.Show, {
89
+ get when() {
90
+ return props.copyData;
91
+ },
92
+ get children() {
93
+ var _el$0 = web.getNextElement(_tmpl$2);
94
+ _el$0.$$click = handleCopy;
95
+ web.insert(_el$0, web.createComponent(solidJs.Show, {
96
+ get when() {
97
+ return !copied();
98
+ },
99
+ get fallback() {
100
+ return web.getNextElement(_tmpl$6);
101
+ },
102
+ get children() {
103
+ return web.getNextElement(_tmpl$);
104
+ }
105
+ }));
106
+ web.effect((_p$) => {
107
+ var _v$ = props.copyLabel || "Copy to clipboard", _v$2 = props.copyLabel || "Copy to clipboard";
108
+ _v$ !== _p$.e && web.setAttribute(_el$0, "title", _p$.e = _v$);
109
+ _v$2 !== _p$.t && web.setAttribute(_el$0, "aria-label", _p$.t = _v$2);
110
+ return _p$;
111
+ }, {
112
+ e: void 0,
113
+ t: void 0
114
+ });
115
+ web.runHydrationEvents();
116
+ return _el$0;
117
+ }
118
+ }), _el$12, _co$);
119
+ _el$10.$$click = handleClose;
120
+ var _ref$4 = modalSlotRef;
121
+ typeof _ref$4 === "function" ? web.use(_ref$4, _el$13) : modalSlotRef = _el$13;
122
+ web.effect(() => web.setAttribute(_el$5, "aria-label", props.title || "Expanded view"));
123
+ web.runHydrationEvents();
124
+ return _el$5;
125
+ })(), web.getNextElement(_tmpl$4)];
126
+ }
127
+ });
128
+ }
129
+ }), _el$16, _co$2);
130
+ web.runHydrationEvents();
131
+ return _el$;
132
+ })();
133
+ };
134
+ web.delegateEvents(["click"]);
135
+ exports.ExpandableWrapper = ExpandableWrapper;
136
+ //# sourceMappingURL=ExpandableWrapper.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpandableWrapper.cjs","sources":["../../src/components/ExpandableWrapper.tsx"],"sourcesContent":["/**\n * ExpandableWrapper - Generic expand/fullscreen wrapper for components\n * v2.2.0: Reusable wrapper that adds expand button + fullscreen modal\n *\n * Uses DOM reparenting to avoid rendering children twice — critical for\n * imperative components like ChartJS that bind instances to DOM nodes.\n */\n\nimport { Component, Show, createSignal, createEffect, onCleanup, JSX } from 'solid-js'\nimport { Portal } from 'solid-js/web'\n\nexport interface ExpandableWrapperProps {\n /** Content to render inline (and in expanded view) */\n children: JSX.Element\n /** Title shown in the expanded modal header */\n title?: string\n /** Data string for copy-to-clipboard in expanded view */\n copyData?: string\n /** Label for copy button tooltip */\n copyLabel?: string\n}\n\n/**\n * Wraps any component with an expand button (top-right corner).\n * Opens a fullscreen Portal modal. The children's DOM is physically\n * reparented into the modal (not duplicated), so imperative bindings\n * like Chart.js canvas refs stay intact.\n *\n * @example\n * <ExpandableWrapper title=\"Sales Data\" copyData={tsvData}>\n * <TableRenderer ... />\n * </ExpandableWrapper>\n */\nexport const ExpandableWrapper: Component<ExpandableWrapperProps> = (props) => {\n const [isExpanded, setIsExpanded] = createSignal(false)\n const [copied, setCopied] = createSignal(false)\n let dialogRef: HTMLDivElement | undefined\n let contentRef: HTMLDivElement | undefined\n let inlineSlotRef: HTMLDivElement | undefined\n let modalSlotRef: HTMLDivElement | undefined\n\n const handleOpen = () => setIsExpanded(true)\n const handleClose = () => setIsExpanded(false)\n\n // Reparent content DOM between inline and modal slots\n createEffect(() => {\n if (!contentRef) return\n\n if (isExpanded()) {\n // Move content into modal\n modalSlotRef?.appendChild(contentRef)\n } else {\n // Move content back to inline\n inlineSlotRef?.appendChild(contentRef)\n }\n })\n\n // Keyboard: Escape to close\n createEffect(() => {\n if (!isExpanded()) return\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault()\n handleClose()\n }\n }\n\n document.addEventListener('keydown', onKeyDown)\n onCleanup(() => document.removeEventListener('keydown', onKeyDown))\n })\n\n // Prevent body scroll when expanded\n createEffect(() => {\n if (isExpanded()) {\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n // Focus the dialog\n setTimeout(() => dialogRef?.focus(), 10)\n onCleanup(() => {\n document.body.style.overflow = prev\n })\n }\n })\n\n const handleBackdropClick = (e: MouseEvent) => {\n if (e.target === e.currentTarget) handleClose()\n }\n\n const handleCopy = async () => {\n if (!props.copyData) return\n try {\n await navigator.clipboard.writeText(props.copyData)\n setCopied(true)\n setTimeout(() => setCopied(false), 2000)\n } catch (err) {\n console.error('Failed to copy:', err)\n }\n }\n\n return (\n <div class=\"relative group\">\n {/* Inline slot — content lives here when not expanded */}\n <div ref={inlineSlotRef}>\n <div ref={contentRef}>\n {props.children}\n </div>\n </div>\n\n {/* Expand button — visible on hover */}\n <button\n onClick={handleOpen}\n class=\"absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-70 hover:!opacity-100 p-1.5 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm\"\n title=\"Expand\"\n aria-label=\"Expand to fullscreen\"\n >\n <svg class=\"w-3.5 h-3.5 text-gray-500 dark:text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5v-4m0 4h-4m4 0l-5-5\" />\n </svg>\n </button>\n\n {/* Fullscreen modal via Portal */}\n <Show when={isExpanded()}>\n <Portal>\n <div\n class=\"fixed inset-0 z-50 flex flex-col bg-black/50 backdrop-blur-sm\"\n style={{ animation: 'expandable-fade-in 0.15s ease-out' }}\n onClick={handleBackdropClick}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={props.title || 'Expanded view'}\n tabIndex={-1}\n ref={dialogRef}\n >\n {/* Modal panel */}\n <div\n class=\"relative flex flex-col m-4 flex-1 bg-white dark:bg-gray-800 rounded-lg shadow-xl overflow-hidden\"\n style={{ animation: 'expandable-scale-in 0.15s ease-out' }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div class=\"flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex-shrink-0\">\n <h2 class=\"text-lg font-semibold text-gray-900 dark:text-white truncate\">\n {props.title || 'Expanded View'}\n </h2>\n <div class=\"flex items-center gap-2\">\n {/* Copy button */}\n <Show when={props.copyData}>\n <button\n onClick={handleCopy}\n class=\"p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n title={props.copyLabel || 'Copy to clipboard'}\n aria-label={props.copyLabel || 'Copy to clipboard'}\n >\n <Show\n when={!copied()}\n fallback={\n <svg class=\"w-5 h-5 text-green-500\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\" />\n </svg>\n }\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\" />\n </svg>\n </Show>\n </button>\n </Show>\n {/* Close button */}\n <button\n onClick={handleClose}\n class=\"p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n aria-label=\"Close expanded view\"\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n </div>\n\n {/* Modal slot — content is reparented here when expanded */}\n <div class=\"flex-1 overflow-auto p-4\" ref={modalSlotRef} />\n </div>\n </div>\n\n <style>{`\n @keyframes expandable-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes expandable-scale-in {\n from { opacity: 0; transform: scale(0.97); }\n to { opacity: 1; transform: scale(1); }\n }\n `}</style>\n </Portal>\n </Show>\n </div>\n )\n}\n"],"names":["ExpandableWrapper","props","isExpanded","setIsExpanded","createSignal","copied","setCopied","dialogRef","contentRef","inlineSlotRef","modalSlotRef","handleOpen","handleClose","createEffect","appendChild","onKeyDown","e","key","preventDefault","document","addEventListener","onCleanup","removeEventListener","prev","body","style","overflow","setTimeout","focus","handleBackdropClick","target","currentTarget","handleCopy","copyData","navigator","clipboard","writeText","err","console","error","_el$","_$getNextElement","_tmpl$5","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$15","_el$16","_co$2","_$getNextMarker","_ref$","_$use","_ref$2","_$insert","children","$$click","_$createComponent","Show","when","Portal","_el$5","_tmpl$3","_el$6","_el$7","_el$8","_el$9","_el$11","_el$12","_co$","_el$10","_el$13","_ref$3","stopPropagation","title","_el$0","_tmpl$2","fallback","_tmpl$6","_tmpl$","_$effect","_p$","_v$","copyLabel","_v$2","_$setAttribute","t","undefined","_$runHydrationEvents","_ref$4","_tmpl$4","_$delegateEvents"],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * ExpandableWrapper - Generic expand/fullscreen wrapper for components
3
+ * v2.2.0: Reusable wrapper that adds expand button + fullscreen modal
4
+ *
5
+ * Uses DOM reparenting to avoid rendering children twice — critical for
6
+ * imperative components like ChartJS that bind instances to DOM nodes.
7
+ */
8
+ import { Component, JSX } from 'solid-js';
9
+ export interface ExpandableWrapperProps {
10
+ /** Content to render inline (and in expanded view) */
11
+ children: JSX.Element;
12
+ /** Title shown in the expanded modal header */
13
+ title?: string;
14
+ /** Data string for copy-to-clipboard in expanded view */
15
+ copyData?: string;
16
+ /** Label for copy button tooltip */
17
+ copyLabel?: string;
18
+ }
19
+ /**
20
+ * Wraps any component with an expand button (top-right corner).
21
+ * Opens a fullscreen Portal modal. The children's DOM is physically
22
+ * reparented into the modal (not duplicated), so imperative bindings
23
+ * like Chart.js canvas refs stay intact.
24
+ *
25
+ * @example
26
+ * <ExpandableWrapper title="Sales Data" copyData={tsvData}>
27
+ * <TableRenderer ... />
28
+ * </ExpandableWrapper>
29
+ */
30
+ export declare const ExpandableWrapper: Component<ExpandableWrapperProps>;
31
+ //# sourceMappingURL=ExpandableWrapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpandableWrapper.d.ts","sourceRoot":"","sources":["../../src/components/ExpandableWrapper.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAA+C,GAAG,EAAE,MAAM,UAAU,CAAA;AAGtF,MAAM,WAAW,sBAAsB;IACrC,sDAAsD;IACtD,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAA;IACrB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,CAAC,sBAAsB,CAuK/D,CAAA"}