Download tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenForm.svelte from Parakon/parakon-runtime: direct link, hf CLI and curl.
- Browser
- Download file 4.24 kB
-
https://huggingface.co/Parakon/parakon-runtime/resolve/main/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenForm.svelte
- Command line
-
hf download hf://Parakon/parakon-runtime/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenForm.svelte
-
curl -L -o ChatScreenForm.svelte https://huggingface.co/Parakon/parakon-runtime/resolve/main/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenForm.svelte
4.24 kB
| <script lang="ts"> | |
| import { afterNavigate } from '$app/navigation'; | |
| import { page } from '$app/state'; | |
| import { ChatForm } from '$lib/components/app'; | |
| import { useDraftMessages } from '$lib/hooks/use-draft-messages.svelte'; | |
| import { deviceStore } from '$lib/stores'; | |
| import { onMount } from 'svelte'; | |
| interface Props { | |
| class?: string; | |
| disabled?: boolean; | |
| initialMessage?: string; | |
| isLoading?: boolean; | |
| onFileRemove?: (fileId: string) => void; | |
| onFileUpload?: (files: File[]) => void; | |
| onSend?: (message: string, files?: ChatUploadedFile[]) => Promise<boolean>; | |
| onStop?: () => void; | |
| onSystemPromptAdd?: (draft: { message: string; files: ChatUploadedFile[] }) => void; | |
| uploadedFiles?: ChatUploadedFile[]; | |
| } | |
| let { | |
| class: className, | |
| disabled = false, | |
| initialMessage = '', | |
| isLoading = false, | |
| onFileRemove, | |
| onFileUpload, | |
| onSend, | |
| onStop, | |
| onSystemPromptAdd, | |
| uploadedFiles = $bindable([]) | |
| }: Props = $props(); | |
| let chatFormRef: ChatForm | undefined = $state(undefined); | |
| let formWrapperEl: HTMLDivElement | undefined = $state(); | |
| let chatId = $derived(page.params.id as string | undefined); | |
| $effect(() => { | |
| if (!formWrapperEl) return; | |
| const formEl = formWrapperEl.querySelector('form') as HTMLElement | null; | |
| if (!formEl) return; | |
| const updateHeight = () => { | |
| const height = Math.round(formEl.getBoundingClientRect().height); | |
| document.documentElement.style.setProperty('--chat-form-height', `${height}px`); | |
| }; | |
| updateHeight(); | |
| const resizeObserver = new ResizeObserver(updateHeight); | |
| resizeObserver.observe(formEl); | |
| return () => { | |
| resizeObserver.disconnect(); | |
| document.documentElement.style.removeProperty('--chat-form-height'); | |
| }; | |
| }); | |
| let hasLoadingAttachments = $derived(uploadedFiles.some((f) => f.isLoading)); | |
| let message = $derived(initialMessage); | |
| let previousIsLoading = $derived(isLoading); | |
| let previousInitialMessage = $derived(initialMessage); | |
| const { clearDraft } = useDraftMessages({ | |
| getChatId: () => chatId, | |
| getFiles: () => uploadedFiles, | |
| getInitialMessage: () => initialMessage, | |
| getMessage: () => message, | |
| setFiles: (f) => (uploadedFiles = f), | |
| setMessage: (m) => (message = m) | |
| }); | |
| function handleFilesAdd(files: File[]) { | |
| onFileUpload?.(files); | |
| } | |
| async function handleSubmit() { | |
| if ((!message.trim() && uploadedFiles.length === 0) || disabled || hasLoadingAttachments) | |
| return; | |
| if (!chatFormRef?.checkModelSelected()) return; | |
| const messageToSend = message.trim(); | |
| const filesToSend = [...uploadedFiles]; | |
| message = ''; | |
| uploadedFiles = []; | |
| clearDraft(); | |
| chatFormRef?.resetTextareaHeight(); | |
| const success = await onSend?.(messageToSend, filesToSend); | |
| if (!success) { | |
| message = messageToSend; | |
| uploadedFiles = filesToSend; | |
| } | |
| } | |
| function handleSystemPromptClick() { | |
| onSystemPromptAdd?.({ files: uploadedFiles, message }); | |
| } | |
| function handleUploadedFileRemove(fileId: string) { | |
| onFileRemove?.(fileId); | |
| } | |
| // Auto-focus must not steal focus already claimed elsewhere (e.g. the system | |
| // message editor opened just before a navigation) | |
| function focusFormUnlessCaptured() { | |
| const active = document.activeElement; | |
| if (active instanceof HTMLTextAreaElement || active instanceof HTMLInputElement) return; | |
| chatFormRef?.focus(); | |
| } | |
| onMount(() => { | |
| if (!deviceStore.isMobile) { | |
| setTimeout(focusFormUnlessCaptured, 100); | |
| } | |
| }); | |
| afterNavigate((navigation) => { | |
| if (navigation?.from != null && !deviceStore.isMobile) { | |
| setTimeout(focusFormUnlessCaptured, 100); | |
| } | |
| }); | |
| $effect(() => { | |
| if (initialMessage !== previousInitialMessage) { | |
| message = initialMessage; | |
| previousInitialMessage = initialMessage; | |
| } | |
| }); | |
| $effect(() => { | |
| if (previousIsLoading && !isLoading) { | |
| setTimeout(focusFormUnlessCaptured, 10); | |
| } | |
| previousIsLoading = isLoading; | |
| }); | |
| </script> | |
| <div bind:this={formWrapperEl} class="chat-screen-form-wrapper"> | |
| <ChatForm | |
| bind:this={chatFormRef} | |
| bind:uploadedFiles | |
| bind:value={message} | |
| class="mx-auto max-w-3xl {className}" | |
| {disabled} | |
| {isLoading} | |
| onFilesAdd={handleFilesAdd} | |
| {onStop} | |
| onSubmit={handleSubmit} | |
| onSystemPromptClick={handleSystemPromptClick} | |
| onUploadedFileRemove={handleUploadedFileRemove} | |
| /> | |
| </div> | |