import MarkdownIt from 'markdown-it' export function formatSize(n: number) { if (n < 1024) return `${n} B` if (n < 1048576) return `${(n / 1024).toFixed(1)} KB` return `${(n / 1048576).toFixed(1)} MB` } export function basename(p: string) { return p.split('/').pop() ?? p } const TOOL_LABELS: Record = { 'mcp__gateway__convert_document': 'Convert', 'mcp__gateway__recalc_workbook': 'Recalc', 'mcp__gateway__web_fetch': 'Fetch', 'mcp__gateway__kb_list_roots': 'Knowledge', 'AskUserQuestion': 'Question', 'WebFetch': 'Fetch', 'WebSearch': 'Search', 'TodoWrite': 'Todo', } export function toolLabel(name: string) { if (TOOL_LABELS[name]) return TOOL_LABELS[name] if (name.startsWith('mcp__')) return name.split('__').slice(2).join('.') return name } export function fileIcon(mime: string, path: string) { const ext = path.split('.').pop()?.toLowerCase() ?? '' if (mime.startsWith('image/')) return 'codicon-file-media' if (['xlsx', 'xls', 'csv', 'ods'].includes(ext)) return 'codicon-table' if (ext === 'pdf') return 'codicon-file-pdf' if (['docx', 'doc', 'md', 'txt', 'odt'].includes(ext)) return 'codicon-file-text' if (['zip', 'tar', 'gz'].includes(ext)) return 'codicon-file-zip' if (['py', 'js', 'ts', 'json', 'html', 'sql', 'sh'].includes(ext)) return 'codicon-file-code' return 'codicon-file' } const md = new MarkdownIt({ html: false, linkify: true, breaks: false, typographer: false }) const defaultLink = md.renderer.rules.link_open ?? ((tokens, idx, options, _env, self) => self.renderToken(tokens, idx, options)) md.renderer.rules.link_open = (tokens, idx, options, env, self) => { tokens[idx]!.attrSet('target', '_blank') tokens[idx]!.attrSet('rel', 'noopener noreferrer') return defaultLink(tokens, idx, options, env, self) } export function renderMarkdown(src: string) { return md.render(src) } export function stringifyInput(input: unknown) { if (input === null || input === undefined) return '' if (typeof input === 'string') return input try { return JSON.stringify(input, null, 2) } catch { return String(input) } }