Skip to content

Commit 2b1b1f8

Browse files
committed
feat: Improves chat form and file handling
Refactors the chat form to handle file uploads more robustly. - Changes `onSend` to be an async function that returns a boolean to indicate the success of sending the message. This allows for validation before clearing the message and uploaded files. - Adds client-side validation for WebP images to avoid server-side errors. - Moves text file utility functions and constants to dedicated files and utils. - Fixes a bug where chat messages were not being cleared after sending.
1 parent 839140b commit 2b1b1f8

File tree

8 files changed

+212
-141
lines changed

8 files changed

+212
-141
lines changed

tools/server/webui/src/lib/components/chat/ChatForm.svelte

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,13 @@
99
class?: string;
1010
disabled?: boolean;
1111
isLoading?: boolean;
12-
onSend?: (message: string, files?: ChatUploadedFile[]) => void;
12+
onSend?: (message: string, files?: ChatUploadedFile[]) => Promise<boolean>;
1313
onStop?: () => void;
1414
showHelperText?: boolean;
1515
uploadedFiles?: ChatUploadedFile[];
1616
onFileUpload?: (files: File[]) => void;
1717
onFileRemove?: (fileId: string) => void;
18-
// Paste configuration
19-
pasteLongTextToFileLen?: number;
18+
pasteLongTextToFileLength?: number;
2019
}
2120
2221
let {
@@ -29,38 +28,44 @@
2928
uploadedFiles = $bindable([]),
3029
onFileUpload,
3130
onFileRemove,
32-
pasteLongTextToFileLen = 2500
31+
pasteLongTextToFileLength = 2500
3332
}: Props = $props();
3433
3534
let message = $state('');
3635
let textareaElement: HTMLTextAreaElement | undefined;
3736
let fileInputElement: HTMLInputElement | undefined;
3837
39-
function handleSubmit(event: SubmitEvent) {
38+
async function handleSubmit(event: SubmitEvent) {
4039
event.preventDefault();
4140
if (!message.trim() || disabled || isLoading) return;
4241
43-
onSend?.(message.trim(), uploadedFiles);
44-
message = '';
45-
uploadedFiles = [];
42+
const success = await onSend?.(message.trim(), uploadedFiles);
4643
47-
if (textareaElement) {
48-
textareaElement.style.height = 'auto';
44+
if (success) {
45+
message = '';
46+
uploadedFiles = [];
47+
48+
if (textareaElement) {
49+
textareaElement.style.height = 'auto';
50+
}
4951
}
5052
}
5153
52-
function handleKeydown(event: KeyboardEvent) {
54+
async function handleKeydown(event: KeyboardEvent) {
5355
if (event.key === 'Enter' && !event.shiftKey) {
5456
event.preventDefault();
5557
5658
if (!message.trim() || disabled || isLoading) return;
5759
58-
onSend?.(message.trim(), uploadedFiles);
59-
message = '';
60-
uploadedFiles = [];
60+
const success = await onSend?.(message.trim(), uploadedFiles);
6161
62-
if (textareaElement) {
63-
textareaElement.style.height = 'auto';
62+
if (success) {
63+
message = '';
64+
uploadedFiles = [];
65+
66+
if (textareaElement) {
67+
textareaElement.style.height = 'auto';
68+
}
6469
}
6570
}
6671
}
@@ -83,7 +88,6 @@
8388
function handlePaste(event: ClipboardEvent) {
8489
if (!event.clipboardData) return;
8590
86-
// Handle pasted files
8791
const files = Array.from(event.clipboardData.items)
8892
.filter((item) => item.kind === 'file')
8993
.map((item) => item.getAsFile())
@@ -95,12 +99,14 @@
9599
return;
96100
}
97101
98-
// Handle long text conversion to file
99102
const text = event.clipboardData.getData('text/plain');
100-
if (text.length > 0 && pasteLongTextToFileLen > 0 && text.length > pasteLongTextToFileLen) {
103+
if (
104+
text.length > 0 &&
105+
pasteLongTextToFileLength > 0 &&
106+
text.length > pasteLongTextToFileLength
107+
) {
101108
event.preventDefault();
102109
103-
// Create a text file from the pasted content
104110
const textFile = new File([text], 'Pasted', {
105111
type: 'text/plain'
106112
});
@@ -110,7 +116,6 @@
110116
}
111117
</script>
112118

113-
<!-- Hidden file input -->
114119
<input
115120
bind:this={fileInputElement}
116121
type="file"

tools/server/webui/src/lib/components/chat/ChatMessage.svelte renamed to tools/server/webui/src/lib/components/chat/ChatMessages/ChatMessage.svelte

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,15 +134,15 @@
134134
</div>
135135
{:else}
136136
{#if message.extra && message.extra.length > 0}
137-
<div class="max-w-[80%] mb-2">
138-
<ChatAttachmentsList
137+
<div class="mb-2 max-w-[80%]">
138+
<ChatAttachmentsList
139139
attachments={message.extra}
140140
readonly={true}
141141
imageHeight="h-80"
142142
/>
143143
</div>
144144
{/if}
145-
145+
146146
<Card class="bg-primary text-primary-foreground max-w-[80%] rounded-2xl px-2.5 py-1.5">
147147
<div class="text-md whitespace-pre-wrap">
148148
{message.content}
@@ -163,7 +163,7 @@
163163
{#if thinkingContent}
164164
<ChatThinkingBlock thinking={thinkingContent} isStreaming={!message.timestamp} />
165165
{/if}
166-
166+
167167
{#if message.role === 'assistant'}
168168
<MarkdownContent content={messageContent} />
169169
{:else}
@@ -180,7 +180,6 @@
180180
</div>
181181
{/if}
182182

183-
184183
{#snippet messageActions(config?: { role: ChatRole })}
185184
<div
186185
class="pointer-events-none inset-0 flex items-center gap-1 opacity-0 transition-all duration-150 group-hover:pointer-events-auto group-hover:opacity-100"
@@ -235,4 +234,4 @@
235234
>
236235
{message.timestamp ? new Date(message.timestamp).toLocaleTimeString() : ''}
237236
</div>
238-
{/snippet}
237+
{/snippet}

0 commit comments

Comments
 (0)