// App state store const STORE = { currentDocId: null, currentSessionId: null, currentPage: 0, totalPages: 0, documents: [], sessions: [], polygons: [], viewerDoc: null, chunkCache: {}, chatMessages: [], }; // API helpers const API = { async get(url) { const r = await fetch(url); if (!r.ok) throw new Error(`API ${r.status}: ${r.statusText}`); return r.json(); }, async post(url, body) { const r = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); if (!r.ok) throw new Error(`API ${r.status}: ${r.statusText}`); return r.json(); }, }; // DOM helpers const $ = (sel) => document.querySelector(sel); const $$ = (sel) => document.querySelectorAll(sel); // Render markdown-style content function renderContent(text) { if (!text) return "

No content

"; let html = text .replace(/&/g, "&") .replace(//g, ">"); // Headers html = html.replace(/^### (.+)$/gm, '

$1

'); html = html.replace(/^## (.+)$/gm, '

$1

'); html = html.replace(/^# (.+)$/gm, '

$1

'); // Bold html = html.replace(/\*\*(.+?)\*\*/g, '$1'); // Italic html = html.replace(/\*(.+?)\*/g, '$1'); // Code blocks html = html.replace(/```([\s\S]*?)```/g, '
$1
'); // Inline code html = html.replace(/`(.+?)`/g, '$1'); // Unordered lists html = html.replace(/^- (.+)$/gm, '
  • $1
  • '); html = html.replace(/(
  • .*<\/li>\n?)+/g, ''); // Ordered lists html = html.replace(/^\d+\. (.+)$/gm, '
  • $1
  • '); // Horizontal rules html = html.replace(/^---$/gm, '
    '); // Blockquotes html = html.replace(/^> (.+)$/gm, '
    $1
    '); // Tables html = html.replace(/^\|(.+)\|$/gm, (match, content) => { const cells = content.split('|').map(c => c.trim()); if (cells.every(c => /^[-:]+$/.test(c))) { return ''; } if (cells.every(c => c.length < 30 && !c.includes(' '))) { return '' + cells.map(c => `${c}`).join('') + ''; } return `${cells.join('')}`; }); // Wrap rows in table let inTable = false; const lines = html.split('\n'); let result = []; let tableBuffer = []; for (let line of lines) { if (line.includes('')) { if (tableBuffer.length > 0) { result.push('' + tableBuffer.join('') + '
    '); tableBuffer = []; } continue; } if (line.startsWith('')) { tableBuffer.push(line); } else { if (tableBuffer.length > 0) { result.push('' + tableBuffer.join('') + '
    '); tableBuffer = []; } result.push(line); } } if (tableBuffer.length > 0) { result.push('' + tableBuffer.join('') + '
    '); } html = result.join('\n'); // Paragraphs html = html.replace(/^(?!<[huldtbr]|