-
- {#if !isEmpty} - { - autoScroll.enable(); - if (!autoScroll.userScrolledUp) { - autoScroll.scrollToBottom(); - } - }} - /> + {#if !isEmpty} + { + handleSendLikeScroll(); + }} + /> + {/if} + +
+ + + + + {#if page.params.id} + {/if} -
- - - - - - - - -
- chatStore.stopGeneration()} - onSystemPromptAdd={handleSystemPromptAdd} - bind:uploadedFiles +
+ {#if (isMobile.current ? mobileScrollDownHint || isMobileUserScrolledUp : autoScroll.userScrolledUp) && page.url.hash.includes(ROUTES.CHAT) && page.params.id} + { + mobileScrollDownHint = false; + scroll.chatScrollContainer?.scrollTo({ + top: scroll.chatScrollContainer.scrollHeight, + behavior: 'smooth' + }); + }} /> -
+ {/if}
+ + chatStore.stopGeneration()} + onSystemPromptAdd={handleSystemPromptAdd} + bind:uploadedFiles={fileUpload.uploadedFiles} + />
{/if} - - - (showDeleteDialog = false)} -/> - - { - if (!open) { - emptyFileNames = []; - } - }} -/> - - diff --git a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenActionScrollDown.svelte b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenActionScrollDown.svelte index a22c491adac6..dca24afd440f 100644 --- a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenActionScrollDown.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenActionScrollDown.svelte @@ -1,58 +1,19 @@ -
- + diff --git a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenDialogsAndAlerts.svelte b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenDialogsAndAlerts.svelte new file mode 100644 index 000000000000..6305a7438015 --- /dev/null +++ b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenDialogsAndAlerts.svelte @@ -0,0 +1,55 @@ + + + + + (showDeleteDialog = false)} +/> + + { + if (!open) { + emptyFileNames = []; + } + }} +/> + + diff --git a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenForm.svelte b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenForm.svelte index aa1c0536dbc3..8eb17eeae485 100644 --- a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenForm.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenForm.svelte @@ -2,6 +2,7 @@ import { afterNavigate } from '$app/navigation'; import { page } from '$app/state'; import { ChatForm } from '$lib/components/app'; + import { isMobile } from '$lib/stores/viewport.svelte'; import { onMount } from 'svelte'; import { useDraftMessages } from '$lib/hooks/use-draft-messages.svelte'; @@ -32,7 +33,30 @@ }: 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); @@ -82,13 +106,23 @@ 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(() => { - setTimeout(() => chatFormRef?.focus(), 10); + if (!isMobile.current) { + setTimeout(focusFormUnlessCaptured, 100); + } }); afterNavigate((navigation) => { - if (navigation?.from != null) { - setTimeout(() => chatFormRef?.focus(), 10); + if (navigation?.from != null && !isMobile.current) { + setTimeout(focusFormUnlessCaptured, 100); } }); @@ -101,19 +135,19 @@ $effect(() => { if (previousIsLoading && !isLoading) { - setTimeout(() => chatFormRef?.focus(), 10); + setTimeout(focusFormUnlessCaptured, 10); } previousIsLoading = isLoading; }); -
+
- import { fadeInView } from '$lib/actions/fade-in-view.svelte'; import { serverStore } from '$lib/stores/server.svelte'; interface Props { @@ -11,10 +10,9 @@