@seed-ship/mcp-ui-solid 6.1.0 → 6.2.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.
- package/CHANGELOG.md +65 -0
- package/dist/components/CarouselRenderer.cjs +41 -30
- package/dist/components/CarouselRenderer.cjs.map +1 -1
- package/dist/components/CarouselRenderer.d.ts.map +1 -1
- package/dist/components/CarouselRenderer.js +42 -31
- package/dist/components/CarouselRenderer.js.map +1 -1
- package/dist/components/CodeBlockRenderer.cjs +88 -25
- package/dist/components/CodeBlockRenderer.cjs.map +1 -1
- package/dist/components/CodeBlockRenderer.d.ts.map +1 -1
- package/dist/components/CodeBlockRenderer.js +89 -26
- package/dist/components/CodeBlockRenderer.js.map +1 -1
- package/dist/components/ImageGalleryRenderer.cjs +101 -77
- package/dist/components/ImageGalleryRenderer.cjs.map +1 -1
- package/dist/components/ImageGalleryRenderer.d.ts.map +1 -1
- package/dist/components/ImageGalleryRenderer.js +102 -78
- package/dist/components/ImageGalleryRenderer.js.map +1 -1
- package/dist/components/MapRenderer.cjs +94 -34
- package/dist/components/MapRenderer.cjs.map +1 -1
- package/dist/components/MapRenderer.d.ts.map +1 -1
- package/dist/components/MapRenderer.js +107 -47
- package/dist/components/MapRenderer.js.map +1 -1
- package/dist/components/VideoRenderer.cjs +95 -74
- package/dist/components/VideoRenderer.cjs.map +1 -1
- package/dist/components/VideoRenderer.d.ts.map +1 -1
- package/dist/components/VideoRenderer.js +96 -75
- package/dist/components/VideoRenderer.js.map +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/components/CarouselRenderer.tsx +9 -1
- package/src/components/CodeBlockRenderer.tsx +65 -5
- package/src/components/ImageGalleryRenderer.test.tsx +18 -7
- package/src/components/ImageGalleryRenderer.tsx +22 -3
- package/src/components/MapRenderer.tsx +68 -14
- package/src/components/VideoRenderer.tsx +14 -4
- package/tsconfig.tsbuildinfo +1 -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'\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 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, '<').replace(/>/g, '>'))\n }\n } else {\n // Fallback: simple escaping\n setHighlightedCode(code.replace(/</g, '<').replace(/>/g, '>'))\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","resolved","default","highlight","console","warn","e","isServer","Promise","all","paramTheme","theme","window","matchMedia","mediaQuery","matches","handleChange","addEventListener","onCleanup","removeEventListener","code","language","result","getLanguage","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
|
+
{"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, '<').replace(/>/g, '>'))\n }\n } else {\n // Fallback: simple escaping\n setHighlightedCode(code.replace(/</g, '<').replace(/>/g, '>'))\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 +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;
|
|
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,7 +1,37 @@
|
|
|
1
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
|
-
import { ExpandableWrapper } from "./ExpandableWrapper.js";
|
|
4
|
-
|
|
3
|
+
import { useExpanded, ExpandableWrapper } from "./ExpandableWrapper.js";
|
|
4
|
+
import { highlightQuery } from "./UIResourceRenderer.js";
|
|
5
|
+
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><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"><input type=text placeholder=Search… 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"aria-label="Search in code"><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="Download code as file"title="Download code"><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="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"></path></svg></button><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><!$><!/><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>`);
|
|
6
|
+
const LANGUAGE_EXTENSIONS = {
|
|
7
|
+
typescript: "ts",
|
|
8
|
+
tsx: "tsx",
|
|
9
|
+
javascript: "js",
|
|
10
|
+
jsx: "jsx",
|
|
11
|
+
python: "py",
|
|
12
|
+
ruby: "rb",
|
|
13
|
+
go: "go",
|
|
14
|
+
rust: "rs",
|
|
15
|
+
java: "java",
|
|
16
|
+
kotlin: "kt",
|
|
17
|
+
swift: "swift",
|
|
18
|
+
php: "php",
|
|
19
|
+
csharp: "cs",
|
|
20
|
+
cpp: "cpp",
|
|
21
|
+
c: "c",
|
|
22
|
+
sql: "sql",
|
|
23
|
+
json: "json",
|
|
24
|
+
yaml: "yml",
|
|
25
|
+
toml: "toml",
|
|
26
|
+
bash: "sh",
|
|
27
|
+
shell: "sh",
|
|
28
|
+
html: "html",
|
|
29
|
+
css: "css",
|
|
30
|
+
scss: "scss",
|
|
31
|
+
markdown: "md",
|
|
32
|
+
xml: "xml",
|
|
33
|
+
graphql: "graphql"
|
|
34
|
+
};
|
|
5
35
|
let hljs = null;
|
|
6
36
|
let stylesLoaded = false;
|
|
7
37
|
const CodeBlockRenderer = (props) => {
|
|
@@ -14,6 +44,32 @@ const CodeBlockRenderer = (props) => {
|
|
|
14
44
|
var _a;
|
|
15
45
|
return props.params || ((_a = props.component) == null ? void 0 : _a.params);
|
|
16
46
|
};
|
|
47
|
+
const isExpanded = useExpanded();
|
|
48
|
+
const [searchQuery, setSearchQuery] = createSignal("");
|
|
49
|
+
const displayedHTML = () => {
|
|
50
|
+
const q = searchQuery().trim();
|
|
51
|
+
return q ? highlightQuery(highlightedCode(), q) : highlightedCode();
|
|
52
|
+
};
|
|
53
|
+
const handleDownload = () => {
|
|
54
|
+
var _a, _b, _c;
|
|
55
|
+
const code = (_a = params()) == null ? void 0 : _a.code;
|
|
56
|
+
if (!code) return;
|
|
57
|
+
const lang = (((_b = params()) == null ? void 0 : _b.language) || "").toLowerCase();
|
|
58
|
+
const ext = LANGUAGE_EXTENSIONS[lang] || "txt";
|
|
59
|
+
const stem = (((_c = params()) == null ? void 0 : _c.filename) || `code-${Date.now()}`).replace(/\.[^.]+$/, "");
|
|
60
|
+
const filename = stem.endsWith(`.${ext}`) ? stem : `${stem}.${ext}`;
|
|
61
|
+
const blob = new Blob([code], {
|
|
62
|
+
type: "text/plain"
|
|
63
|
+
});
|
|
64
|
+
const url = URL.createObjectURL(blob);
|
|
65
|
+
const a = document.createElement("a");
|
|
66
|
+
a.href = url;
|
|
67
|
+
a.download = filename;
|
|
68
|
+
document.body.appendChild(a);
|
|
69
|
+
a.click();
|
|
70
|
+
document.body.removeChild(a);
|
|
71
|
+
URL.revokeObjectURL(url);
|
|
72
|
+
};
|
|
17
73
|
createEffect(async () => {
|
|
18
74
|
if (!hljs) {
|
|
19
75
|
try {
|
|
@@ -117,14 +173,16 @@ const CodeBlockRenderer = (props) => {
|
|
|
117
173
|
},
|
|
118
174
|
copyLabel: "Copy code",
|
|
119
175
|
get children() {
|
|
120
|
-
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$
|
|
176
|
+
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;
|
|
121
177
|
insert(_el$3, () => {
|
|
122
178
|
var _a, _b;
|
|
123
179
|
return ((_a = params()) == null ? void 0 : _a.filename) || ((_b = params()) == null ? void 0 : _b.language) || "Code";
|
|
124
180
|
});
|
|
125
|
-
_el$5.$$
|
|
126
|
-
_el$6.$$click =
|
|
127
|
-
|
|
181
|
+
_el$5.$$input = (e) => setSearchQuery(e.currentTarget.value);
|
|
182
|
+
_el$6.$$click = handleDownload;
|
|
183
|
+
_el$7.$$click = () => setWordWrap(!wordWrap());
|
|
184
|
+
_el$8.$$click = handleCopy;
|
|
185
|
+
insert(_el$8, createComponent(Show, {
|
|
128
186
|
get when() {
|
|
129
187
|
return isCopied();
|
|
130
188
|
},
|
|
@@ -135,41 +193,43 @@ const CodeBlockRenderer = (props) => {
|
|
|
135
193
|
return getNextElement(_tmpl$);
|
|
136
194
|
}
|
|
137
195
|
}));
|
|
138
|
-
insert(_el$
|
|
196
|
+
insert(_el$0, createComponent(Show, {
|
|
139
197
|
get when() {
|
|
140
198
|
var _a;
|
|
141
199
|
return ((_a = params()) == null ? void 0 : _a.showLineNumbers) !== false;
|
|
142
200
|
},
|
|
143
201
|
get children() {
|
|
144
|
-
var _el$
|
|
145
|
-
insert(_el$
|
|
202
|
+
var _el$1 = getNextElement(_tmpl$2);
|
|
203
|
+
insert(_el$1, createComponent(For, {
|
|
146
204
|
get each() {
|
|
147
205
|
return lineNumbers();
|
|
148
206
|
},
|
|
149
207
|
children: (num) => (() => {
|
|
150
|
-
var _el$
|
|
151
|
-
insert(_el$
|
|
152
|
-
return _el$
|
|
208
|
+
var _el$15 = getNextElement(_tmpl$5);
|
|
209
|
+
insert(_el$15, num);
|
|
210
|
+
return _el$15;
|
|
153
211
|
})()
|
|
154
212
|
}));
|
|
155
|
-
return _el$
|
|
213
|
+
return _el$1;
|
|
156
214
|
}
|
|
157
|
-
}), _el$
|
|
215
|
+
}), _el$13, _co$);
|
|
158
216
|
effect((_p$) => {
|
|
159
217
|
var _a, _b, _c, _d;
|
|
160
|
-
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$
|
|
218
|
+
var _v$ = `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" : ""}`, _v$2 = `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$3 = wordWrap() ? "Disable word wrap" : "Enable word wrap", _v$4 = `relative overflow-auto flex ${isExpanded() ? "flex-1 min-h-0" : ""}`, _v$5 = !isExpanded() && ((_a = params()) == null ? void 0 : _a.maxHeight) ? {
|
|
161
219
|
"max-height": (_b = params()) == null ? void 0 : _b.maxHeight
|
|
162
|
-
} : {}, _v$
|
|
220
|
+
} : {}, _v$6 = wordWrap() ? {
|
|
163
221
|
"white-space": "pre-wrap",
|
|
164
222
|
"word-break": "break-all"
|
|
165
|
-
} : {}, _v$
|
|
166
|
-
_v$ !== _p$.e && className(_el
|
|
167
|
-
_v$2 !== _p$.t &&
|
|
168
|
-
_p$.a
|
|
169
|
-
_p$.o
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
_v$7 !== _p$.s &&
|
|
223
|
+
} : {}, _v$7 = activeTheme(), _v$8 = `hljs ${((_c = params()) == null ? void 0 : _c.language) ? `language-${(_d = params()) == null ? void 0 : _d.language}` : ""}`, _v$9 = displayedHTML();
|
|
224
|
+
_v$ !== _p$.e && className(_el$, _p$.e = _v$);
|
|
225
|
+
_v$2 !== _p$.t && className(_el$7, _p$.t = _v$2);
|
|
226
|
+
_v$3 !== _p$.a && setAttribute(_el$7, "title", _p$.a = _v$3);
|
|
227
|
+
_v$4 !== _p$.o && className(_el$0, _p$.o = _v$4);
|
|
228
|
+
_p$.i = style(_el$0, _v$5, _p$.i);
|
|
229
|
+
_p$.n = style(_el$10, _v$6, _p$.n);
|
|
230
|
+
_v$7 !== _p$.s && setAttribute(_el$10, "data-theme", _p$.s = _v$7);
|
|
231
|
+
_v$8 !== _p$.h && className(_el$11, _p$.h = _v$8);
|
|
232
|
+
_v$9 !== _p$.r && setProperty(_el$11, "innerHTML", _p$.r = _v$9);
|
|
173
233
|
return _p$;
|
|
174
234
|
}, {
|
|
175
235
|
e: void 0,
|
|
@@ -178,14 +238,17 @@ const CodeBlockRenderer = (props) => {
|
|
|
178
238
|
o: void 0,
|
|
179
239
|
i: void 0,
|
|
180
240
|
n: void 0,
|
|
181
|
-
s: void 0
|
|
241
|
+
s: void 0,
|
|
242
|
+
h: void 0,
|
|
243
|
+
r: void 0
|
|
182
244
|
});
|
|
245
|
+
effect(() => setProperty(_el$5, "value", searchQuery()));
|
|
183
246
|
runHydrationEvents();
|
|
184
247
|
return _el$;
|
|
185
248
|
}
|
|
186
249
|
});
|
|
187
250
|
};
|
|
188
|
-
delegateEvents(["click"]);
|
|
251
|
+
delegateEvents(["input", "click"]);
|
|
189
252
|
export {
|
|
190
253
|
CodeBlockRenderer
|
|
191
254
|
};
|
|
@@ -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'\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 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, '<').replace(/>/g, '>'))\n }\n } else {\n // Fallback: simple escaping\n setHighlightedCode(code.replace(/</g, '<').replace(/>/g, '>'))\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","resolved","default","highlight","console","warn","e","isServer","Promise","all","paramTheme","theme","window","matchMedia","mediaQuery","matches","handleChange","addEventListener","onCleanup","removeEventListener","code","language","result","getLanguage","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
|
+
{"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, 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, '<').replace(/>/g, '>'))\n }\n } else {\n // Fallback: simple escaping\n setHighlightedCode(code.replace(/</g, '<').replace(/>/g, '>'))\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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"}
|
|
@@ -3,9 +3,15 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
3
3
|
const web = require("solid-js/web");
|
|
4
4
|
const solidJs = require("solid-js");
|
|
5
5
|
const LightboxOverlay = require("./LightboxOverlay.cjs");
|
|
6
|
-
|
|
6
|
+
const ExpandableWrapper = require("./ExpandableWrapper.cjs");
|
|
7
|
+
var _tmpl$ = /* @__PURE__ */ web.template(`<div class="px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex-shrink-0"><h3 class="text-sm font-semibold text-gray-900 dark:text-white">`), _tmpl$2 = /* @__PURE__ */ web.template(`<div><!$><!/><div></div><!$><!/>`), _tmpl$3 = /* @__PURE__ */ web.template(`<div class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent text-white text-xs p-2 pt-4"><span class="truncate block">`), _tmpl$4 = /* @__PURE__ */ web.template(`<div class="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-200"><div class="bg-white/90 dark:bg-gray-800/90 rounded-full p-2"><svg class="w-5 h-5 text-gray-700 dark:text-gray-200"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7">`), _tmpl$5 = /* @__PURE__ */ web.template(`<button type=button><img class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-200"loading=lazy><div class="absolute inset-0 bg-black/0 group-hover:bg-black/20 transition-colors duration-200"></div><!$><!/><!$><!/>`, true, false, false);
|
|
8
|
+
function imagesToTextList(p) {
|
|
9
|
+
if (!p) return "";
|
|
10
|
+
return (p.images ?? []).map((img) => img.caption ? `${img.url} ${img.caption}` : img.url).join("\n");
|
|
11
|
+
}
|
|
7
12
|
const ImageGalleryRenderer = (props) => {
|
|
8
13
|
const [selectedIndex, setSelectedIndex] = solidJs.createSignal(null);
|
|
14
|
+
const isExpanded = ExpandableWrapper.useExpanded();
|
|
9
15
|
const params = () => {
|
|
10
16
|
var _a;
|
|
11
17
|
return props.params || ((_a = props.component) == null ? void 0 : _a.params);
|
|
@@ -57,82 +63,100 @@ const ImageGalleryRenderer = (props) => {
|
|
|
57
63
|
setSelectedIndex(index);
|
|
58
64
|
}
|
|
59
65
|
};
|
|
60
|
-
return (
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
66
|
+
return web.createComponent(ExpandableWrapper.ExpandableWrapper, {
|
|
67
|
+
get title() {
|
|
68
|
+
var _a;
|
|
69
|
+
return ((_a = params()) == null ? void 0 : _a.title) || "Gallery";
|
|
70
|
+
},
|
|
71
|
+
get copyData() {
|
|
72
|
+
return imagesToTextList(params());
|
|
73
|
+
},
|
|
74
|
+
copyLabel: "Copy image URLs",
|
|
75
|
+
get children() {
|
|
76
|
+
var _el$ = web.getNextElement(_tmpl$2), _el$5 = _el$.firstChild, [_el$6, _co$] = web.getNextMarker(_el$5.nextSibling), _el$4 = _el$6.nextSibling, _el$7 = _el$4.nextSibling, [_el$8, _co$2] = web.getNextMarker(_el$7.nextSibling);
|
|
77
|
+
web.insert(_el$, web.createComponent(solidJs.Show, {
|
|
78
|
+
get when() {
|
|
79
|
+
var _a;
|
|
80
|
+
return (_a = params()) == null ? void 0 : _a.title;
|
|
81
|
+
},
|
|
82
|
+
get children() {
|
|
83
|
+
var _el$2 = web.getNextElement(_tmpl$), _el$3 = _el$2.firstChild;
|
|
84
|
+
web.insert(_el$3, () => params().title);
|
|
85
|
+
return _el$2;
|
|
86
|
+
}
|
|
87
|
+
}), _el$6, _co$);
|
|
88
|
+
web.insert(_el$4, web.createComponent(solidJs.For, {
|
|
89
|
+
get each() {
|
|
90
|
+
var _a;
|
|
91
|
+
return (_a = params()) == null ? void 0 : _a.images;
|
|
92
|
+
},
|
|
93
|
+
children: (image, index) => (() => {
|
|
94
|
+
var _el$9 = web.getNextElement(_tmpl$5), _el$0 = _el$9.firstChild, _el$1 = _el$0.nextSibling, _el$13 = _el$1.nextSibling, [_el$14, _co$3] = web.getNextMarker(_el$13.nextSibling), _el$15 = _el$14.nextSibling, [_el$16, _co$4] = web.getNextMarker(_el$15.nextSibling);
|
|
95
|
+
_el$9.$$click = () => handleImageClick(index());
|
|
96
|
+
web.insert(_el$9, web.createComponent(solidJs.Show, {
|
|
97
|
+
get when() {
|
|
98
|
+
var _a;
|
|
99
|
+
return web.memo(() => !!image.caption)() && ((_a = params()) == null ? void 0 : _a.showCaptions);
|
|
100
|
+
},
|
|
101
|
+
get children() {
|
|
102
|
+
var _el$10 = web.getNextElement(_tmpl$3), _el$11 = _el$10.firstChild;
|
|
103
|
+
web.insert(_el$11, () => image.caption);
|
|
104
|
+
return _el$10;
|
|
105
|
+
}
|
|
106
|
+
}), _el$14, _co$3);
|
|
107
|
+
web.insert(_el$9, web.createComponent(solidJs.Show, {
|
|
108
|
+
get when() {
|
|
109
|
+
var _a;
|
|
110
|
+
return ((_a = params()) == null ? void 0 : _a.lightbox) !== false;
|
|
111
|
+
},
|
|
112
|
+
get children() {
|
|
113
|
+
return web.getNextElement(_tmpl$4);
|
|
114
|
+
}
|
|
115
|
+
}), _el$16, _co$4);
|
|
116
|
+
web.effect((_p$) => {
|
|
117
|
+
var _v$3 = `relative overflow-hidden rounded-md focus:ring-2 focus:ring-blue-500 focus:outline-none group ${aspectClass()}`, _v$4 = image.alt || `View image ${index() + 1}`, _v$5 = image.thumbnail || image.url, _v$6 = image.alt || `Image ${index() + 1}`, _v$7 = image.srcset, _v$8 = image.sizes;
|
|
118
|
+
_v$3 !== _p$.e && web.className(_el$9, _p$.e = _v$3);
|
|
119
|
+
_v$4 !== _p$.t && web.setAttribute(_el$9, "aria-label", _p$.t = _v$4);
|
|
120
|
+
_v$5 !== _p$.a && web.setAttribute(_el$0, "src", _p$.a = _v$5);
|
|
121
|
+
_v$6 !== _p$.o && web.setAttribute(_el$0, "alt", _p$.o = _v$6);
|
|
122
|
+
_v$7 !== _p$.i && web.setAttribute(_el$0, "srcset", _p$.i = _v$7);
|
|
123
|
+
_v$8 !== _p$.n && web.setAttribute(_el$0, "sizes", _p$.n = _v$8);
|
|
124
|
+
return _p$;
|
|
125
|
+
}, {
|
|
126
|
+
e: void 0,
|
|
127
|
+
t: void 0,
|
|
128
|
+
a: void 0,
|
|
129
|
+
o: void 0,
|
|
130
|
+
i: void 0,
|
|
131
|
+
n: void 0
|
|
132
|
+
});
|
|
133
|
+
web.runHydrationEvents();
|
|
134
|
+
return _el$9;
|
|
135
|
+
})()
|
|
136
|
+
}));
|
|
137
|
+
web.insert(_el$, web.createComponent(LightboxOverlay.LightboxOverlay, {
|
|
138
|
+
get images() {
|
|
139
|
+
var _a;
|
|
140
|
+
return ((_a = params()) == null ? void 0 : _a.images) || [];
|
|
141
|
+
},
|
|
142
|
+
get selectedIndex() {
|
|
143
|
+
return selectedIndex();
|
|
144
|
+
},
|
|
145
|
+
onClose: () => setSelectedIndex(null),
|
|
146
|
+
onNavigate: setSelectedIndex
|
|
147
|
+
}), _el$8, _co$2);
|
|
148
|
+
web.effect((_p$) => {
|
|
149
|
+
var _v$ = `w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${isExpanded() ? "flex-1 min-h-0 flex flex-col" : ""}`, _v$2 = `grid ${columnsClass()} ${gapClass()} p-4 ${isExpanded() ? "flex-1 min-h-0 overflow-auto" : ""}`;
|
|
150
|
+
_v$ !== _p$.e && web.className(_el$, _p$.e = _v$);
|
|
151
|
+
_v$2 !== _p$.t && web.className(_el$4, _p$.t = _v$2);
|
|
152
|
+
return _p$;
|
|
153
|
+
}, {
|
|
154
|
+
e: void 0,
|
|
155
|
+
t: void 0
|
|
156
|
+
});
|
|
157
|
+
return _el$;
|
|
158
|
+
}
|
|
159
|
+
});
|
|
136
160
|
};
|
|
137
161
|
web.delegateEvents(["click"]);
|
|
138
162
|
exports.ImageGalleryRenderer = ImageGalleryRenderer;
|