// @ts-nocheck import katex from 'katex'; function normalizeShowImageSrc(src: string) { if (!src) return ''; const trimmed = src.trim(); if (/^https?:\/\//i.test(trimmed)) return trimmed; if (trimmed.startsWith('/user_upload/')) return trimmed; // 兼容容器内部路径:/workspace/.../user_upload/xxx.png 或 /workspace/user_upload/xxx const idx = trimmed.toLowerCase().indexOf('/user_upload/'); if (idx >= 0) { return '/user_upload/' + trimmed.slice(idx + '/user_upload/'.length); } if (trimmed.startsWith('/') || trimmed.startsWith('./') || trimmed.startsWith('../')) { return trimmed; } return ''; } function isSafeImageSrc(src: string) { return !!normalizeShowImageSrc(src); } function escapeHtml(input: string) { return input .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function renderShowImages(root: ParentNode | null = document) { if (!root) return; // 处理因自闭合解析导致的嵌套:把子 show_image 平铺到父后面 const nested = Array.from(root.querySelectorAll('show_image show_image')).reverse(); nested.forEach(child => { const parent = child.parentElement; if (parent && parent !== root) { parent.after(child); } }); const nodes = Array.from(root.querySelectorAll('show_image:not([data-rendered])')).reverse(); nodes.forEach(node => { // 将 show_image 内误被包裹的内容移动到当前节点之后,保持原有顺序 if (node.parentNode && node.firstChild) { const parent = node.parentNode; const ref = node.nextSibling; // 可能为 null,insertBefore 会当 append const children = Array.from(node.childNodes); children.forEach(child => parent.insertBefore(child, ref)); } const rawSrc = node.getAttribute('src') || ''; const mappedSrc = normalizeShowImageSrc(rawSrc); if (!mappedSrc) { node.setAttribute('data-rendered', '1'); node.setAttribute('data-rendered-error', 'invalid-src'); return; } const alt = node.getAttribute('alt') || ''; const safeAlt = escapeHtml(alt.trim()); const figure = document.createElement('figure'); figure.className = 'chat-inline-image'; const img = document.createElement('img'); img.loading = 'lazy'; img.src = mappedSrc; img.alt = safeAlt; img.onerror = () => { figure.classList.add('chat-inline-image--error'); const tip = document.createElement('div'); tip.className = 'chat-inline-image__error'; tip.textContent = '图片加载失败'; figure.appendChild(tip); }; figure.appendChild(img); if (safeAlt) { const caption = document.createElement('figcaption'); caption.innerHTML = safeAlt; figure.appendChild(caption); } node.replaceChildren(figure); node.setAttribute('data-rendered', '1'); }); } let showImageObserver: MutationObserver | null = null; export function setupShowImageObserver() { if (showImageObserver) return; const container = document.querySelector('.messages-area') || document.body; if (!container) return; renderShowImages(container); showImageObserver = new MutationObserver(() => renderShowImages(container)); showImageObserver.observe(container, { childList: true, subtree: true }); } export function teardownShowImageObserver() { if (showImageObserver) { showImageObserver.disconnect(); showImageObserver = null; } } function updateViewportHeightVar() { const docEl = document.documentElement; const visualViewport = window.visualViewport; if (visualViewport) { const vh = visualViewport.height; const bottomInset = Math.max( 0, (window.innerHeight || docEl.clientHeight || vh) - visualViewport.height - visualViewport.offsetTop ); docEl.style.setProperty('--app-viewport', `${vh}px`); docEl.style.setProperty('--app-bottom-inset', `${bottomInset}px`); } else { const height = window.innerHeight || docEl.clientHeight; if (height) { docEl.style.setProperty('--app-viewport', `${height}px`); } docEl.style.setProperty('--app-bottom-inset', 'env(safe-area-inset-bottom, 0px)'); } } if (typeof window !== 'undefined') { window.katex = katex; updateViewportHeightVar(); window.addEventListener('resize', updateViewportHeightVar); window.addEventListener('orientationchange', updateViewportHeightVar); window.addEventListener('pageshow', updateViewportHeightVar); if (window.visualViewport) { window.visualViewport.addEventListener('resize', updateViewportHeightVar); window.visualViewport.addEventListener('scroll', updateViewportHeightVar); } }