mirror of
https://github.com/danielmiessler/Fabric.git
synced 2026-01-10 06:48:04 -05:00
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:
@@ -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
|
||||
|
||||
75
web/src/lib/components/chat/SessionSelector.svelte
Normal file
75
web/src/lib/components/chat/SessionSelector.svelte
Normal 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>
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user