/** * IconHandler - unified icon rendering for SVG, PNG, and WebP * * SVGs use currentColor for theme-aware rendering * PNG/WebP are rendered as standard img elements */ const IconHandler = (() => { const ACTION_ICONS = { edit: '', 'edit-doc': '', overflow: '', wrench: '', }; function getFileExtension(path) { return path?.split('.')?.pop()?.toLowerCase() || ''; } async function fetchAndInlineSvg(container, iconPath, altText, options) { try { const response = await fetch(iconPath); if (!response.ok) throw new Error(`Failed to load ${iconPath}`); const svgText = await response.text(); container.textContent = ''; container.style.overflow = 'hidden'; container.style.display = 'flex'; container.style.alignItems = 'center'; container.style.justifyContent = 'center'; const svg = parseSvg(svgText); if (!svg) throw new Error(`Invalid SVG: ${iconPath}`); const svgNode = svg.ownerDocument === document ? svg : document.importNode(svg, true); normalizeSvg(svgNode, options); if (altText) { svgNode.setAttribute('aria-label', altText); } container.appendChild(svgNode); } catch (error) { console.error('Failed to render SVG icon:', error); fallbackImg(container, iconPath, altText); } } function fallbackImg(container, iconPath, altText) { const img = document.createElement('img'); img.src = iconPath; if (altText) { img.alt = altText; } sizeToContainer(img); container.appendChild(img); } function renderIcon(element, iconPath, altText, options = {}) { if (!element || !iconPath) return; const ext = getFileExtension(iconPath); if (ext === 'svg') { fetchAndInlineSvg(element, iconPath, altText, options); } else { const img = document.createElement('img'); img.src = iconPath; if (altText) { img.alt = altText; } sizeToContainer(img); element.appendChild(img); } } function sizeToContainer(node) { node.style.width = '100%'; node.style.height = '100%'; node.style.display = 'block'; node.style.objectFit = 'contain'; } function normalizeSvg(svg, options) { svg.removeAttribute('width'); svg.removeAttribute('height'); svg.setAttribute('preserveAspectRatio', 'xMidYMid meet'); svg.style.width = '100%'; svg.style.height = '100%'; svg.style.display = 'block'; svg.classList.add('icon-svg'); if (options.chrome) { svg.setAttribute('fill', 'currentColor'); svg.setAttribute('stroke', 'currentColor'); svg.style.color = 'currentColor'; } } function parseSvg(svgText) { const parser = new DOMParser(); const parsed = parser.parseFromString(svgText, 'image/svg+xml'); const root = parsed.documentElement; if (root?.tagName.toLowerCase().endsWith('svg')) { return root; } return null; } function getActionIcon(name) { return ACTION_ICONS[name] || ''; } return { getActionIcon, renderIcon, }; })(); window.IconHandler = IconHandler; window.monitorShared = window.monitorShared || {}; window.monitorShared.IconHandler = IconHandler;