124 lines
4.2 KiB
Svelte
124 lines
4.2 KiB
Svelte
<script>
|
|
import { onMount } from "svelte";
|
|
import { get, post } from "../../lib/api.js";
|
|
|
|
let dates = $state([]);
|
|
let selectedDate = $state("");
|
|
let summary = $state(null);
|
|
let messages = $state([]);
|
|
let showMessages = $state(false);
|
|
let loading = $state(false);
|
|
let triggering = $state(false);
|
|
let error = $state("");
|
|
|
|
onMount(async () => {
|
|
try {
|
|
dates = await get("/api/chat-summary/dates");
|
|
if (dates.length) selectDate(dates[0]);
|
|
} catch (e) {
|
|
error = e.body?.detail || "Failed to load dates";
|
|
}
|
|
});
|
|
|
|
async function selectDate(d) {
|
|
selectedDate = d;
|
|
showMessages = false;
|
|
loading = true;
|
|
error = "";
|
|
try {
|
|
summary = await get(`/api/chat-summary/summary/${d}`);
|
|
} catch (e) {
|
|
summary = null;
|
|
if (e.status !== 404) error = e.body?.detail || "Failed to load summary";
|
|
} finally {
|
|
loading = false;
|
|
}
|
|
}
|
|
|
|
async function loadMessages() {
|
|
if (showMessages) { showMessages = false; return; }
|
|
try {
|
|
messages = await get(`/api/chat-summary/messages/${selectedDate}`);
|
|
showMessages = true;
|
|
} catch (e) {
|
|
error = "Failed to load messages";
|
|
}
|
|
}
|
|
|
|
async function trigger() {
|
|
triggering = true;
|
|
try {
|
|
await post("/api/chat-summary/trigger");
|
|
} catch (e) {
|
|
error = "Trigger failed";
|
|
} finally {
|
|
triggering = false;
|
|
}
|
|
}
|
|
|
|
function markdownToHtml(md) {
|
|
if (!md) return "";
|
|
return md
|
|
.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")
|
|
.replace(/^### (.+)$/gm, '<h3 class="text-lg font-semibold mt-4 mb-2">$1</h3>')
|
|
.replace(/^## (.+)$/gm, '<h2 class="text-xl font-bold mt-5 mb-2">$1</h2>')
|
|
.replace(/^# (.+)$/gm, '<h1 class="text-2xl font-bold mt-6 mb-3">$1</h1>')
|
|
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
|
.replace(/\*(.+?)\*/g, '<em>$1</em>')
|
|
.replace(/^- (.+)$/gm, '<li class="ml-4">$1</li>')
|
|
.replace(/\n{2,}/g, '<br><br>')
|
|
.replace(/\n/g, '<br>');
|
|
}
|
|
</script>
|
|
|
|
<div>
|
|
<div class="flex items-center justify-between mb-6">
|
|
<h2 class="text-xl font-bold text-surface-800 dark:text-surface-100">Chat Digest</h2>
|
|
<button onclick={trigger} disabled={triggering}
|
|
class="px-3 py-1.5 text-sm font-medium rounded-lg bg-primary-600 text-white hover:bg-primary-700 disabled:opacity-50">
|
|
{triggering ? "Generating..." : "Generate Now"}
|
|
</button>
|
|
</div>
|
|
|
|
{#if error}
|
|
<div class="mb-4 p-3 rounded-lg bg-red-50 text-red-700 text-sm dark:bg-red-900/20 dark:text-red-400">{error}</div>
|
|
{/if}
|
|
|
|
<div class="flex gap-2 mb-6 flex-wrap">
|
|
{#each dates as d}
|
|
<button onclick={() => selectDate(d)}
|
|
class="px-3 py-1.5 text-sm rounded-lg transition-colors {selectedDate === d ? 'bg-primary-600 text-white' : 'bg-surface-100 text-surface-600 hover:bg-surface-200 dark:bg-surface-700 dark:text-surface-300 dark:hover:bg-surface-600'}">
|
|
{d}
|
|
</button>
|
|
{/each}
|
|
{#if !dates.length && !error}
|
|
<p class="text-sm text-surface-400">No summaries yet.</p>
|
|
{/if}
|
|
</div>
|
|
|
|
{#if loading}
|
|
<div class="flex justify-center py-12"><div class="animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600"></div></div>
|
|
{:else if summary}
|
|
<div class="prose dark:prose-invert max-w-none bg-white dark:bg-surface-800 rounded-xl p-6 shadow-sm border border-surface-200 dark:border-surface-700">
|
|
{@html markdownToHtml(summary.content)}
|
|
</div>
|
|
|
|
<button onclick={loadMessages}
|
|
class="mt-4 text-sm text-primary-600 hover:text-primary-700 dark:text-primary-400">
|
|
{showMessages ? "Hide raw messages" : "Show raw messages"}
|
|
</button>
|
|
|
|
{#if showMessages}
|
|
<div class="mt-3 bg-white dark:bg-surface-800 rounded-xl p-4 shadow-sm border border-surface-200 dark:border-surface-700 max-h-96 overflow-y-auto">
|
|
{#each messages as m}
|
|
<div class="py-1.5 border-b border-surface-100 dark:border-surface-700 last:border-0 text-sm">
|
|
<span class="text-surface-400 text-xs">{m.time?.slice(11,16)}</span>
|
|
<span class="font-medium text-primary-600 dark:text-primary-400 ml-1">[{m.group}] {m.sender}:</span>
|
|
<span class="text-surface-700 dark:text-surface-300 ml-1">{m.text}</span>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
{/if}
|
|
{/if}
|
|
</div>
|