/** * @file Steuert die Websuche-Auswahl und rendert Quellenlinks zu Chat-Antworten. */ /** @typedef {{title: string, url: string, snippet: string}} WebSource */ /** @typedef {{message: string, model: string, chatId: number, webSearch?: boolean, reasoning?: boolean}} ChatRequest */ /** @typedef {{role: string, content: string, thinking?: string, sources?: WebSource[]}} ChatMessage */ export function createWebSearchToggle(button, storage = globalThis.localStorage) { const key = "krishd-web-search-enabled"; let enabled = true; try { enabled = storage?.getItem(key) !== "false"; } catch { /* Storage may be unavailable. */ } const render = () => { button.setAttribute("aria-pressed", String(enabled)); button.title = enabled ? "Web search allowed. The assistant searches when useful. Click to turn off." : "Web search off. Click to allow the assistant to search when useful."; button.setAttribute("aria-label", button.title); }; button.addEventListener("click", () => { enabled = !enabled; try { storage?.setItem(key, String(enabled)); } catch { /* Keep this session's choice. */ } render(); }); render(); return {get enabled() { return enabled; }}; } /** @param {HTMLElement} container @param {WebSource[] | undefined} sources */ export function appendSources(container, sources) { if (!Array.isArray(sources) || !sources.length) return; const section = document.createElement("section"); section.className = "msg-sources"; section.setAttribute("aria-label", "Sources"); const heading = document.createElement("div"); heading.className = "msg-sources-heading"; heading.textContent = "Used web results ยท Sources"; section.appendChild(heading); sources.slice(0, 5).forEach((source, index) => { let url; try { url = new URL(source.url); } catch { return; } if (!["https:", "http:"].includes(url.protocol) || url.username || url.password) return; const link = document.createElement("a"); link.href = url.href; link.target = "_blank"; link.rel = "noopener noreferrer"; link.className = "msg-source"; const title = document.createElement("span"); title.textContent = `[${index + 1}] ${source.title}`; title.title = source.title; const domain = document.createElement("small"); domain.textContent = url.hostname; link.append(title, domain); section.appendChild(link); }); if (section.childElementCount > 1) container.appendChild(section); }