diff --git a/implementation-plans/_index.md b/implementation-plans/_index.md index 589d322..b3c13cd 100644 --- a/implementation-plans/_index.md +++ b/implementation-plans/_index.md @@ -13,3 +13,4 @@ | #9 | Intermediate results display | COMPLETED | [issue-009.md](issue-009.md) | | #10 | Final result rendering with artifacts | COMPLETED | [issue-010.md](issue-010.md) | | #11 | Session creation and ID management | COMPLETED | [issue-011.md](issue-011.md) | +| #12 | Session history sidebar | COMPLETED | [issue-012.md](issue-012.md) | diff --git a/implementation-plans/issue-012.md b/implementation-plans/issue-012.md new file mode 100644 index 0000000..4a46170 --- /dev/null +++ b/implementation-plans/issue-012.md @@ -0,0 +1,17 @@ +--- +--- + +# Issue #12: Session history sidebar + +**Status:** COMPLETED +**Issue:** https://git.shahondin1624.de/llm-multiverse/llm-multiverse-ui/issues/12 +**Branch:** `feature/issue-12-session-sidebar` + +## Acceptance Criteria + +- [x] Left sidebar component listing past sessions +- [x] Each entry shows timestamp and first-message preview +- [x] Click on a session loads its message history +- [x] Session message history persisted in localStorage +- [x] Sessions sorted by most recent first +- [x] Delete session option (with confirmation) diff --git a/src/lib/components/SessionSidebar.svelte b/src/lib/components/SessionSidebar.svelte new file mode 100644 index 0000000..57fd54f --- /dev/null +++ b/src/lib/components/SessionSidebar.svelte @@ -0,0 +1,81 @@ + + + diff --git a/src/lib/stores/sessions.svelte.ts b/src/lib/stores/sessions.svelte.ts index 1b0a8bb..3a7646a 100644 --- a/src/lib/stores/sessions.svelte.ts +++ b/src/lib/stores/sessions.svelte.ts @@ -91,6 +91,16 @@ function createSessionStore() { } } + function deleteSession(id: string) { + sessions.delete(id); + if (activeSessionId === id) { + const remaining = getAllSessions(); + activeSessionId = remaining.length > 0 ? remaining[0].id : null; + if (activeSessionId) saveActiveSessionId(activeSessionId); + } + saveSessions(sessions); + } + function getAllSessions(): Session[] { return [...sessions.values()].sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()); } @@ -102,6 +112,7 @@ function createSessionStore() { getOrCreateSession, updateMessages, switchSession, + deleteSession, getAllSessions }; } diff --git a/src/routes/chat/+page.svelte b/src/routes/chat/+page.svelte index 4c7610f..fb52cb5 100644 --- a/src/routes/chat/+page.svelte +++ b/src/routes/chat/+page.svelte @@ -9,6 +9,7 @@ import OrchestrationProgress from '$lib/components/OrchestrationProgress.svelte'; import ThinkingSection from '$lib/components/ThinkingSection.svelte'; import FinalResult from '$lib/components/FinalResult.svelte'; + import SessionSidebar from '$lib/components/SessionSidebar.svelte'; import { processRequest, OrchestratorError } from '$lib/services/orchestrator'; import { OrchestrationState } from '$lib/proto/llm_multiverse/v1/orchestrator_pb'; import { sessionStore } from '$lib/stores/sessions.svelte'; @@ -20,15 +21,18 @@ let intermediateResult: string = $state(''); let finalResult: SubagentResult | null = $state(null); - // Initialize session from URL or create new one + function navigateToSession(sessionId: string, replace = false) { + const url = `${resolveRoute('/chat')}?session=${sessionId}`; + // eslint-disable-next-line svelte/no-navigation-without-resolve + goto(url, { replaceState: replace }); + } + $effect(() => { const sessionParam = $page.url.searchParams.get('session'); const session = sessionStore.getOrCreateSession(sessionParam ?? undefined); messages = [...session.messages]; if (!sessionParam || sessionParam !== session.id) { - const url = `${resolveRoute('/chat')}?session=${session.id}`; - // eslint-disable-next-line svelte/no-navigation-without-resolve - goto(url, { replaceState: true }); + navigateToSession(session.id, true); } }); @@ -37,9 +41,18 @@ messages = []; error = null; finalResult = null; - const url = `${resolveRoute('/chat')}?session=${session.id}`; - // eslint-disable-next-line svelte/no-navigation-without-resolve - goto(url); + navigateToSession(session.id); + } + + function handleSelectSession(id: string) { + sessionStore.switchSession(id); + const session = sessionStore.activeSession; + if (session) { + messages = [...session.messages]; + error = null; + finalResult = null; + navigateToSession(id); + } } async function handleSend(content: string) { @@ -102,47 +115,46 @@ } -
-
-

Chat

- -
+
+ - +
+
+

Chat

+
- {#if isStreaming} - - - {/if} + - {#if isStreaming && messages.length > 0 && messages[messages.length - 1].content === ''} -
-
- - - + {#if isStreaming} + + + {/if} + + {#if isStreaming && messages.length > 0 && messages[messages.length - 1].content === ''} +
+
+ + + +
-
- {/if} + {/if} - {#if finalResult && !isStreaming} - - {/if} + {#if finalResult && !isStreaming} + + {/if} - {#if error} -
- {error} -
- {/if} + {#if error} +
+ {error} +
+ {/if} - + +