diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatForm.svelte b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatForm.svelte index 6a8a06f4dd..3ed2987f97 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatForm.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatForm.svelte @@ -6,12 +6,13 @@ interface Props { class?: string; disabled?: boolean; + initialMessage?: string; isLoading?: boolean; onFileRemove?: (fileId: string) => void; onFileUpload?: (files: File[]) => void; onSend?: (message: string, files?: ChatUploadedFile[]) => Promise; onStop?: () => void; - onSystemPromptAdd?: () => void; + onSystemPromptAdd?: (draft: { message: string; files: ChatUploadedFile[] }) => void; showHelperText?: boolean; uploadedFiles?: ChatUploadedFile[]; } @@ -19,6 +20,7 @@ let { class: className, disabled = false, + initialMessage = '', isLoading = false, onFileRemove, onFileUpload, @@ -30,8 +32,21 @@ }: Props = $props(); let inputAreaRef: ChatFormInputArea | undefined = $state(undefined); - let message = $state(''); + let message = $state(initialMessage); let previousIsLoading = $state(isLoading); + let previousInitialMessage = $state(initialMessage); + + // Sync message when initialMessage prop changes (e.g., after draft restoration) + $effect(() => { + if (initialMessage !== previousInitialMessage) { + message = initialMessage; + previousInitialMessage = initialMessage; + } + }); + + function handleSystemPromptClick() { + onSystemPromptAdd?.({ message, files: uploadedFiles }); + } let hasLoadingAttachments = $derived(uploadedFiles.some((f) => f.isLoading)); @@ -99,7 +114,7 @@ onFilesAdd={handleFilesAdd} {onStop} onSubmit={handleSubmit} - onSystemPromptClick={onSystemPromptAdd} + onSystemPromptClick={handleSystemPromptClick} onUploadedFileRemove={handleUploadedFileRemove} /> diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormInputArea.svelte b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormInputArea.svelte index f9ff52d03b..b023f0428e 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormInputArea.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormInputArea.svelte @@ -39,7 +39,7 @@ onMcpPromptArgumentsChange?: (fileId: string, args: Record) => void; onStop?: () => void; onSubmit?: () => void; - onSystemPromptClick?: () => void; + onSystemPromptClick?: (draft: { message: string; files: ChatUploadedFile[] }) => void; onUploadedFileRemove?: (fileId: string) => void; onUploadedFilesChange?: (files: ChatUploadedFile[]) => void; onValueChange?: (value: string) => void; @@ -422,7 +422,7 @@ onFileUpload={handleFileUpload} onMicClick={handleMicClick} {onStop} - {onSystemPromptClick} + onSystemPromptClick={() => onSystemPromptClick?.({ message: value, files: uploadedFiles })} onMcpPromptClick={showMcpPromptButton ? () => (isPromptPickerOpen = true) : undefined} /> diff --git a/tools/server/webui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte b/tools/server/webui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte index 4fd9088e76..6c5fb2ce56 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte @@ -65,6 +65,8 @@ let emptyFileNames = $state([]); + let initialMessage = $state(''); + let isEmpty = $derived( showCenteredEmpty && !activeConversation() && activeMessages().length === 0 && !isLoading() ); @@ -215,6 +217,14 @@ } } + async function handleSystemPromptAdd(draft: { message: string; files: ChatUploadedFile[] }) { + if (draft.message || draft.files.length > 0) { + chatStore.savePendingDraft(draft.message, draft.files); + } + + await chatStore.addSystemPrompt(); + } + function handleScroll() { autoScroll.handleScroll(); } @@ -301,6 +311,12 @@ if (!disableAutoScroll) { setTimeout(() => autoScroll.scrollToBottom('instant'), INITIAL_SCROLL_DELAY); } + + const pendingDraft = chatStore.consumePendingDraft(); + if (pendingDraft) { + initialMessage = pendingDraft.message; + uploadedFiles = pendingDraft.files; + } }); $effect(() => { @@ -377,12 +393,13 @@
chatStore.stopGeneration()} - onSystemPromptAdd={() => chatStore.addSystemPrompt()} + onSystemPromptAdd={handleSystemPromptAdd} showHelperText={false} bind:uploadedFiles /> @@ -436,12 +453,13 @@
chatStore.stopGeneration()} - onSystemPromptAdd={() => chatStore.addSystemPrompt()} + onSystemPromptAdd={handleSystemPromptAdd} showHelperText={true} bind:uploadedFiles /> diff --git a/tools/server/webui/src/lib/stores/chat.svelte.ts b/tools/server/webui/src/lib/stores/chat.svelte.ts index 7f26f30953..7e7c8fdfaf 100644 --- a/tools/server/webui/src/lib/stores/chat.svelte.ts +++ b/tools/server/webui/src/lib/stores/chat.svelte.ts @@ -41,6 +41,10 @@ class ChatStore { private addFilesHandler: ((files: File[]) => void) | null = $state(null); pendingEditMessageId = $state(null); + // Draft preservation for navigation (e.g., when adding system prompt from welcome page) + private _pendingDraftMessage = $state(''); + private _pendingDraftFiles = $state([]); + constructor() { if (browser) { chatClient.setStoreCallbacks({ @@ -219,6 +223,31 @@ class ChatStore { this.pendingEditMessageId = null; } + savePendingDraft(message: string, files: ChatUploadedFile[]): void { + this._pendingDraftMessage = message; + this._pendingDraftFiles = [...files]; + } + + consumePendingDraft(): { message: string; files: ChatUploadedFile[] } | null { + if (!this._pendingDraftMessage && this._pendingDraftFiles.length === 0) { + return null; + } + + const draft = { + message: this._pendingDraftMessage, + files: [...this._pendingDraftFiles] + }; + + this._pendingDraftMessage = ''; + this._pendingDraftFiles = []; + + return draft; + } + + hasPendingDraft(): boolean { + return Boolean(this._pendingDraftMessage) || this._pendingDraftFiles.length > 0; + } + getAllLoadingChats(): string[] { return Array.from(this.chatLoadingStates.keys()); }