109 lines
5.1 KiB
HTML
109 lines
5.1 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Geteilte Recherche</title>
|
||
<link rel="icon" href="/icons/icon-192.png">
|
||
<meta name="theme-color" content="#0f172a">
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
<script>
|
||
tailwind.config = { darkMode: 'class' };
|
||
document.documentElement.classList.add('dark');
|
||
</script>
|
||
</head>
|
||
<body class="dark:bg-slate-950 bg-slate-100 dark:text-slate-100 text-slate-900 min-h-screen">
|
||
<div class="max-w-3xl mx-auto p-4 md:p-8">
|
||
<header class="flex items-center justify-between mb-6">
|
||
<div class="flex items-center gap-2">
|
||
<span class="text-2xl">🔎</span>
|
||
<span class="font-bold">Research Agent</span>
|
||
<span class="text-xs px-2 py-0.5 rounded-full bg-slate-700 text-slate-300">geteilt</span>
|
||
</div>
|
||
<span id="dateLabel" class="text-sm text-slate-400"></span>
|
||
</header>
|
||
|
||
<div id="loading" class="text-center py-16 text-slate-400">Lade Recherche…</div>
|
||
|
||
<div id="content" class="hidden space-y-4"></div>
|
||
|
||
<footer class="mt-8 text-center text-xs text-slate-500">
|
||
<p>Generiert mit dem Research Agent · DeepSeek v4 Flash</p>
|
||
</footer>
|
||
</div>
|
||
|
||
<script>
|
||
const content = document.getElementById('content');
|
||
const loading = document.getElementById('loading');
|
||
|
||
const escapeHtml = (s) => String(s ?? '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||
const mdLite = (text) => {
|
||
let s = escapeHtml(text);
|
||
s = s.replace(/`([^`]+)`/g, '<code class="px-1 py-0.5 rounded bg-slate-700 text-amber-200 text-xs">$1</code>');
|
||
s = s.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
||
s = s.replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer" class="text-blue-400 underline break-all">$1</a>');
|
||
s = s.replace(/https?:\/\/[^\s<]+/g, '<a href="$&" target="_blank" rel="noopener noreferrer" class="text-blue-400 underline break-all">$&</a>');
|
||
return s.replace(/\n/g, '<br>');
|
||
};
|
||
|
||
const toolLabel = (m) => {
|
||
const icons = { search: '🔍', browse: '🌐', verify: '✅', save_dashboard: '📊' };
|
||
const icon = icons[m.tool_name] || '🔧';
|
||
const text = m.tool_name === 'search' ? `Suche: ${m.content}` : m.content;
|
||
return { icon, text: (text || '').slice(0, 200) };
|
||
};
|
||
|
||
const renderDashboardCard = (url) => `
|
||
<div class="dark:bg-slate-800 bg-white rounded-2xl border dark:border-slate-700 border-slate-200 overflow-hidden">
|
||
<div class="p-4 flex flex-wrap items-center gap-3">
|
||
<div class="flex-1 min-w-[200px]">
|
||
<div class="text-sm font-semibold">📊 Dashboard</div>
|
||
<div class="text-xs text-slate-400">Analyse, verifizierte Quellen, Empfehlungen & Handlungsplan</div>
|
||
</div>
|
||
<a href="${url}" target="_blank" rel="noopener noreferrer"
|
||
class="px-4 py-2 rounded-lg bg-emerald-600 hover:bg-emerald-700 text-white text-sm font-semibold">Dashboard öffnen ↗</a>
|
||
<a href="${url}" download
|
||
class="px-4 py-2 rounded-lg bg-slate-600 hover:bg-slate-500 text-white text-sm">Download</a>
|
||
</div>
|
||
<iframe src="${url}" class="w-full border-t dark:border-slate-700 border-slate-200" style="height: 65vh; min-height: 400px;" loading="lazy"></iframe>
|
||
</div>`;
|
||
|
||
async function load() {
|
||
const token = location.pathname.split('/').pop();
|
||
const res = await fetch(`/api/share/${token}`);
|
||
if (!res.ok) {
|
||
loading.textContent = '⚠️ Dieser Link ist ungültig oder wurde gelöscht.';
|
||
return;
|
||
}
|
||
const { session, messages } = await res.json();
|
||
loading.classList.add('hidden');
|
||
content.classList.remove('hidden');
|
||
|
||
document.title = `${session.title} – Research Agent`;
|
||
document.getElementById('dateLabel').textContent = new Date(session.created_at).toLocaleDateString('de-DE');
|
||
|
||
content.innerHTML = `
|
||
<h1 class="text-2xl font-bold">${escapeHtml(session.title)}</h1>
|
||
${session.dashboard ? renderDashboardCard(session.dashboard) : ''}`;
|
||
|
||
const chat = document.createElement('div');
|
||
chat.className = 'space-y-3 mt-2';
|
||
for (const m of messages) {
|
||
if (m.role === 'user') {
|
||
chat.innerHTML += `<div class="flex justify-end"><div class="max-w-[80%] px-4 py-2.5 rounded-2xl bg-blue-600 text-white whitespace-pre-wrap break-words">${mdLite(m.content)}</div></div>`;
|
||
} else if (m.role === 'assistant') {
|
||
if (m.content)
|
||
chat.innerHTML += `<div class="flex"><div class="max-w-[85%] px-4 py-2.5 rounded-2xl bg-slate-200 dark:bg-slate-800 whitespace-pre-wrap break-words">${mdLite(m.content)}</div></div>`;
|
||
} else if (m.role === 'tool') {
|
||
const { icon, text } = toolLabel(m);
|
||
chat.innerHTML += `<div class="flex"><div class="text-xs text-slate-400 px-1 flex items-center gap-1"><span>${icon}</span><span class="break-all">${escapeHtml(text)}</span></div></div>`;
|
||
}
|
||
}
|
||
content.appendChild(chat);
|
||
}
|
||
|
||
load();
|
||
</script>
|
||
</body>
|
||
</html>
|