webui: Stop generation from chat sidebar (#17806)
* feat: Add stop generation button for Conversation Item * chore: update webui build output
This commit is contained in:
parent
e31b5c55c3
commit
a28e3c7567
Binary file not shown.
|
|
@ -8,6 +8,7 @@
|
||||||
import * as AlertDialog from '$lib/components/ui/alert-dialog';
|
import * as AlertDialog from '$lib/components/ui/alert-dialog';
|
||||||
import Input from '$lib/components/ui/input/input.svelte';
|
import Input from '$lib/components/ui/input/input.svelte';
|
||||||
import { conversationsStore, conversations } from '$lib/stores/conversations.svelte';
|
import { conversationsStore, conversations } from '$lib/stores/conversations.svelte';
|
||||||
|
import { chatStore } from '$lib/stores/chat.svelte';
|
||||||
import ChatSidebarActions from './ChatSidebarActions.svelte';
|
import ChatSidebarActions from './ChatSidebarActions.svelte';
|
||||||
|
|
||||||
const sidebar = Sidebar.useSidebar();
|
const sidebar = Sidebar.useSidebar();
|
||||||
|
|
@ -98,6 +99,10 @@
|
||||||
|
|
||||||
await goto(`#/chat/${id}`);
|
await goto(`#/chat/${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleStopGeneration(id: string) {
|
||||||
|
chatStore.stopGenerationForChat(id);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ScrollArea class="h-[100vh]">
|
<ScrollArea class="h-[100vh]">
|
||||||
|
|
@ -132,6 +137,7 @@
|
||||||
onSelect={selectConversation}
|
onSelect={selectConversation}
|
||||||
onEdit={handleEditConversation}
|
onEdit={handleEditConversation}
|
||||||
onDelete={handleDeleteConversation}
|
onDelete={handleDeleteConversation}
|
||||||
|
onStop={handleStopGeneration}
|
||||||
/>
|
/>
|
||||||
</Sidebar.MenuItem>
|
</Sidebar.MenuItem>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Trash2, Pencil, MoreHorizontal, Download, Loader2 } from '@lucide/svelte';
|
import { Trash2, Pencil, MoreHorizontal, Download, Loader2, Square } from '@lucide/svelte';
|
||||||
import { ActionDropdown } from '$lib/components/app';
|
import { ActionDropdown } from '$lib/components/app';
|
||||||
|
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||||
import { getAllLoadingChats } from '$lib/stores/chat.svelte';
|
import { getAllLoadingChats } from '$lib/stores/chat.svelte';
|
||||||
import { conversationsStore } from '$lib/stores/conversations.svelte';
|
import { conversationsStore } from '$lib/stores/conversations.svelte';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
|
@ -12,6 +13,7 @@
|
||||||
onDelete?: (id: string) => void;
|
onDelete?: (id: string) => void;
|
||||||
onEdit?: (id: string) => void;
|
onEdit?: (id: string) => void;
|
||||||
onSelect?: (id: string) => void;
|
onSelect?: (id: string) => void;
|
||||||
|
onStop?: (id: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
|
|
@ -20,6 +22,7 @@
|
||||||
onDelete,
|
onDelete,
|
||||||
onEdit,
|
onEdit,
|
||||||
onSelect,
|
onSelect,
|
||||||
|
onStop,
|
||||||
isActive = false
|
isActive = false
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
|
|
@ -38,8 +41,14 @@
|
||||||
onDelete?.(conversation.id);
|
onDelete?.(conversation.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleStop(event: Event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
onStop?.(conversation.id);
|
||||||
|
}
|
||||||
|
|
||||||
function handleGlobalEditEvent(event: Event) {
|
function handleGlobalEditEvent(event: Event) {
|
||||||
const customEvent = event as CustomEvent<{ conversationId: string }>;
|
const customEvent = event as CustomEvent<{ conversationId: string }>;
|
||||||
|
|
||||||
if (customEvent.detail.conversationId === conversation.id && isActive) {
|
if (customEvent.detail.conversationId === conversation.id && isActive) {
|
||||||
handleEdit(event);
|
handleEdit(event);
|
||||||
}
|
}
|
||||||
|
|
@ -88,8 +97,28 @@
|
||||||
>
|
>
|
||||||
<div class="flex min-w-0 flex-1 items-center gap-2">
|
<div class="flex min-w-0 flex-1 items-center gap-2">
|
||||||
{#if isLoading}
|
{#if isLoading}
|
||||||
<Loader2 class="h-3.5 w-3.5 shrink-0 animate-spin text-muted-foreground" />
|
<Tooltip.Root>
|
||||||
|
<Tooltip.Trigger>
|
||||||
|
<div
|
||||||
|
class="stop-button flex h-4 w-4 shrink-0 cursor-pointer items-center justify-center rounded text-muted-foreground transition-colors hover:text-foreground"
|
||||||
|
onclick={handleStop}
|
||||||
|
onkeydown={(e) => e.key === 'Enter' && handleStop(e)}
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
aria-label="Stop generation"
|
||||||
|
>
|
||||||
|
<Loader2 class="loading-icon h-3.5 w-3.5 animate-spin" />
|
||||||
|
|
||||||
|
<Square class="stop-icon hidden h-3 w-3 fill-current text-destructive" />
|
||||||
|
</div>
|
||||||
|
</Tooltip.Trigger>
|
||||||
|
|
||||||
|
<Tooltip.Content>
|
||||||
|
<p>Stop generation</p>
|
||||||
|
</Tooltip.Content>
|
||||||
|
</Tooltip.Root>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||||
<span class="truncate text-sm font-medium" onclick={handleMobileSidebarItemClick}>
|
<span class="truncate text-sm font-medium" onclick={handleMobileSidebarItemClick}>
|
||||||
|
|
@ -147,5 +176,25 @@
|
||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stop-button {
|
||||||
|
:global(.stop-icon) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.loading-icon) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:is(:hover) .stop-button {
|
||||||
|
:global(.stop-icon) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.loading-icon) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -701,13 +701,17 @@ class ChatStore {
|
||||||
|
|
||||||
if (!activeConv) return;
|
if (!activeConv) return;
|
||||||
|
|
||||||
await this.savePartialResponseIfNeeded(activeConv.id);
|
await this.stopGenerationForChat(activeConv.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async stopGenerationForChat(convId: string): Promise<void> {
|
||||||
|
await this.savePartialResponseIfNeeded(convId);
|
||||||
|
|
||||||
this.stopStreaming();
|
this.stopStreaming();
|
||||||
this.abortRequest(activeConv.id);
|
this.abortRequest(convId);
|
||||||
this.setChatLoading(activeConv.id, false);
|
this.setChatLoading(convId, false);
|
||||||
this.clearChatStreaming(activeConv.id);
|
this.clearChatStreaming(convId);
|
||||||
this.clearProcessingState(activeConv.id);
|
this.clearProcessingState(convId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue