refactor(gui): extract SessionSelector component and address PR feedback

- Extract session UI into dedicated SessionSelector.svelte component
- Use Select component instead of native <select>
- Add session message loading when selecting existing session
- Fix placeholder selection behavior to preserve current session
- Rename "Session ID" to "Session Name" for consistency
- Add proper error handling for session loading
- Simplify reactive statements with nullish coalescing
- Use ?? instead of || in ChatService.ts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
lif
2025-12-28 10:26:04 +08:00
parent b3993238d5
commit e0b70d2d90
4 changed files with 93 additions and 48 deletions

View File

@@ -2,35 +2,14 @@
import Patterns from "./Patterns.svelte";
import Models from "./Models.svelte";
import ModelConfig from "./ModelConfig.svelte";
import SessionSelector from "./SessionSelector.svelte";
import { Select } from "$lib/components/ui/select";
import { Input } from "$lib/components/ui/input";
import { Label } from "$lib/components/ui/label";
import { languageStore } from '$lib/store/language-store';
import { strategies, selectedStrategy, fetchStrategies } from '$lib/store/strategy-store';
import { patternVariables } from '$lib/store/pattern-store';
import { currentSession, setSession } from '$lib/store/chat-store';
import { sessionAPI, sessions } from '$lib/store/session-store';
import { onMount } from 'svelte';
let sessionInput = '';
let sessionsList: string[] = [];
// Update sessions list when sessions store changes
$: if ($sessions) {
sessionsList = $sessions.map(s => s.Name);
}
function handleSessionInput() {
setSession(sessionInput.trim() || null);
}
function handleSessionSelect(event: Event) {
const target = event.target as HTMLSelectElement;
const value = target.value;
sessionInput = value;
setSession(value || null);
}
const languages = [
{ code: '', name: 'Default Language' },
{ code: 'en', name: 'English' },
@@ -63,8 +42,6 @@
onMount(() => {
fetchStrategies();
sessionAPI.loadSessions();
sessionInput = $currentSession || '';
});
</script>
@@ -98,29 +75,7 @@
{/each}
</Select>
</div>
<div>
<Label for="session-input" class="text-xs text-white/70 mb-1 block">Session ID</Label>
<input
id="session-input"
type="text"
bind:value={sessionInput}
on:blur={handleSessionInput}
on:keydown={(e) => e.key === 'Enter' && handleSessionInput()}
placeholder="Enter session name..."
class="w-full px-3 py-2 text-sm bg-primary-800/30 border-none rounded-md hover:bg-primary-800/40 transition-colors text-white placeholder-white/50 focus:ring-1 focus:ring-white/20 focus:outline-none"
/>
{#if sessionsList.length > 0}
<select
on:change={handleSessionSelect}
class="w-full mt-2 px-3 py-2 text-sm bg-primary-800/30 border-none rounded-md hover:bg-primary-800/40 transition-colors text-white focus:ring-1 focus:ring-white/20 focus:outline-none"
>
<option value="">Load existing session...</option>
{#each sessionsList as session}
<option value={session}>{session}</option>
{/each}
</select>
{/if}
</div>
<SessionSelector />
<div>
<Label for="pattern-variables" class="text-xs text-white/70 mb-1 block">Pattern Variables (JSON)</Label>
<textarea

View File

@@ -0,0 +1,75 @@
<script lang="ts">
import { Select } from "$lib/components/ui/select";
import { Label } from "$lib/components/ui/label";
import { currentSession, setSession, messageStore } from '$lib/store/chat-store';
import { sessionAPI, sessions } from '$lib/store/session-store';
import { onMount } from 'svelte';
let sessionInput = '';
$: sessionsList = $sessions?.map(s => s.Name) ?? [];
function handleSessionInput() {
const trimmed = sessionInput.trim();
if (trimmed) {
setSession(trimmed);
}
}
async function handleSessionSelect(event: Event) {
const target = event.target as HTMLSelectElement;
const value = target.value;
// If the placeholder option (empty value) is selected, do not clear the session.
// Instead, keep the current session and restore the input field to it.
if (!value) {
sessionInput = $currentSession ?? '';
return;
}
sessionInput = value;
setSession(value);
// Load the selected session's message history so the chat reflects prior context
try {
const messages = await sessionAPI.loadSessionMessages(value);
messageStore.set(messages);
} catch (error) {
console.error('Failed to load session messages:', error);
}
}
onMount(async () => {
try {
await sessionAPI.loadSessions();
} catch (error) {
console.error('Failed to load sessions:', error);
}
sessionInput = $currentSession ?? '';
});
</script>
<div>
<Label for="session-input" class="text-xs text-white/70 mb-1 block">Session Name</Label>
<input
id="session-input"
type="text"
bind:value={sessionInput}
on:blur={handleSessionInput}
on:keydown={(e) => e.key === 'Enter' && handleSessionInput()}
placeholder="Enter session name..."
class="w-full px-3 py-2 text-sm bg-primary-800/30 border-none rounded-md hover:bg-primary-800/40 transition-colors text-white placeholder-white/50 focus:ring-1 focus:ring-white/20 focus:outline-none"
/>
{#if sessionsList.length > 0}
<Select
value={sessionInput}
on:change={handleSessionSelect}
class="mt-2 bg-primary-800/30 border-none hover:bg-primary-800/40 transition-colors"
>
<option value="">Load existing session...</option>
{#each sessionsList as session}
<option value={session}>{session}</option>
{/each}
</Select>
{/if}
</div>

View File

@@ -211,7 +211,7 @@ export class ChatService {
model: config.model,
patternName: get(selectedPatternName),
strategyName: get(selectedStrategy), // Add selected strategy to prompt
sessionName: get(currentSession) || undefined, // Session name for multi-turn conversations
sessionName: get(currentSession) ?? undefined, // Session name for multi-turn conversations
variables: get(patternVariables), // Add pattern variables
};
}

View File

@@ -89,5 +89,20 @@ export const sessionAPI = {
toastService.error(error instanceof Error ? error.message : 'Failed to import session');
throw error;
}
},
async loadSessionMessages(sessionName: string): Promise<Message[]> {
try {
const response = await fetch(`/api/sessions/${sessionName}`);
if (!response.ok) {
throw new Error(`Failed to load session: ${response.statusText}`);
}
const data = await response.json();
const messages = Array.isArray(data.Message) ? data.Message : [];
return messages;
} catch (error) {
console.error(`Error loading session messages for ${sessionName}:`, error);
throw error;
}
}
};