Skip to content

Commit f82af0b

Browse files
gary149nsarrazin
andauthored
Chat input UI updates (#1641)
* remove extra margin * chatinput * add missing auto * fix: lint * feat: add fixed to parent container to prevent bouncy scroll on ios --------- Co-authored-by: Nathan Sarrazin <sarrazin.nathan@gmail.com>
1 parent d70dfb9 commit f82af0b

File tree

3 files changed

+80
-71
lines changed

3 files changed

+80
-71
lines changed

src/lib/components/chat/ChatInput.svelte

Lines changed: 52 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script lang="ts">
22
import { browser } from "$app/environment";
3-
import { createEventDispatcher, onMount, tick } from "svelte";
3+
import { createEventDispatcher, onMount } from "svelte";
44
55
import HoverTooltip from "$lib/components/HoverTooltip.svelte";
66
import IconInternet from "$lib/components/icons/IconInternet.svelte";
@@ -31,7 +31,6 @@
3131
export let placeholder = "";
3232
export let loading = false;
3333
export let disabled = false;
34-
3534
export let assistant: Assistant | undefined = undefined;
3635
3736
export let modelHasTools = false;
@@ -54,6 +53,21 @@
5453
5554
const dispatch = createEventDispatcher<{ submit: void }>();
5655
56+
onMount(() => {
57+
if (!isVirtualKeyboard()) {
58+
textareaElement.focus();
59+
}
60+
function onFormSubmit() {
61+
adjustTextareaHeight();
62+
}
63+
64+
const formEl = textareaElement.closest("form");
65+
formEl?.addEventListener("submit", onFormSubmit);
66+
return () => {
67+
formEl?.removeEventListener("submit", onFormSubmit);
68+
};
69+
});
70+
5771
function isVirtualKeyboard(): boolean {
5872
if (!browser) return false;
5973
@@ -70,30 +84,24 @@
7084
}
7185
7286
function adjustTextareaHeight() {
73-
if (!textareaElement) return;
7487
textareaElement.style.height = "auto";
75-
const newHeight = Math.min(textareaElement.scrollHeight, parseInt("96em"));
76-
textareaElement.style.height = `${newHeight}px`;
77-
if (!textareaElement.parentElement) return;
78-
textareaElement.parentElement.style.height = `${newHeight}px`;
88+
textareaElement.style.height = `${textareaElement.scrollHeight}px`;
89+
90+
if (textareaElement.selectionStart === textareaElement.value.length) {
91+
textareaElement.scrollTop = textareaElement.scrollHeight;
92+
}
7993
}
8094
81-
async function handleKeydown(event: KeyboardEvent) {
82-
if (event.key === "Enter" && !event.shiftKey && !isCompositionOn) {
95+
function handleKeydown(event: KeyboardEvent) {
96+
if (
97+
event.key === "Enter" &&
98+
!event.shiftKey &&
99+
!isCompositionOn &&
100+
!isVirtualKeyboard() &&
101+
value.trim() !== ""
102+
) {
83103
event.preventDefault();
84-
if (isVirtualKeyboard()) {
85-
// Insert a newline at the cursor position
86-
const start = textareaElement.selectionStart;
87-
const end = textareaElement.selectionEnd;
88-
value = value.substring(0, start) + "\n" + value.substring(end);
89-
textareaElement.selectionStart = textareaElement.selectionEnd = start + 1;
90-
} else {
91-
if (value.trim() !== "") {
92-
dispatch("submit");
93-
await tick();
94-
adjustTextareaHeight();
95-
}
96-
}
104+
dispatch("submit");
97105
}
98106
}
99107
@@ -110,13 +118,6 @@
110118
$: documentParserIsOn =
111119
modelHasTools && files.length > 0 && files.some((file) => file.type.startsWith("application/"));
112120
113-
onMount(() => {
114-
if (!isVirtualKeyboard()) {
115-
textareaElement.focus();
116-
}
117-
adjustTextareaHeight();
118-
});
119-
120121
$: extraTools = $page.data.tools
121122
.filter((t: ToolFront) => $settings.tools?.includes(t._id))
122123
.filter(
@@ -125,29 +126,27 @@
125126
) satisfies ToolFront[];
126127
</script>
127128

128-
<div class="min-h-full flex-1" on:paste>
129-
<div class="relative w-full min-w-0">
130-
<textarea
131-
enterkeyhint={!isVirtualKeyboard() ? "enter" : "send"}
132-
tabindex="0"
133-
rows="1"
134-
class="scrollbar-custom max-h-[96em] w-full resize-none scroll-p-3 overflow-y-auto overflow-x-hidden border-0 bg-transparent px-3 py-2.5 outline-none focus:ring-0 focus-visible:ring-0 max-sm:p-2.5 max-sm:text-[16px]"
135-
class:text-gray-400={disabled}
136-
bind:value
137-
bind:this={textareaElement}
138-
{disabled}
139-
on:keydown={handleKeydown}
140-
on:compositionstart={() => (isCompositionOn = true)}
141-
on:compositionend={() => (isCompositionOn = false)}
142-
on:input={adjustTextareaHeight}
143-
on:beforeinput
144-
{placeholder}
145-
/>
146-
</div>
129+
<div class="flex min-h-full flex-1 flex-col" on:paste>
130+
<textarea
131+
rows="1"
132+
tabindex="0"
133+
inputmode="text"
134+
class="scrollbar-custom max-h-[4lh] w-full resize-none overflow-y-auto overflow-x-hidden border-0 bg-transparent px-2.5 py-2.5 outline-none focus:ring-0 focus-visible:ring-0 max-sm:text-[16px] sm:px-3"
135+
class:text-gray-400={disabled}
136+
bind:value
137+
bind:this={textareaElement}
138+
on:keydown={handleKeydown}
139+
on:compositionstart={() => (isCompositionOn = true)}
140+
on:compositionend={() => (isCompositionOn = false)}
141+
on:input={adjustTextareaHeight}
142+
on:beforeinput
143+
{placeholder}
144+
{disabled}
145+
/>
146+
147147
{#if !assistant}
148148
<div
149-
class="scrollbar-custom -ml-0.5 flex max-w-[calc(100%-40px)] flex-wrap items-center justify-start gap-2 px-3 pb-2.5 pt-0.5 text-gray-500
150-
dark:text-gray-400 max-md:flex-nowrap max-md:overflow-x-auto sm:gap-2.5"
149+
class="scrollbar-custom -ml-0.5 flex max-w-[calc(100%-40px)] flex-wrap items-center justify-start gap-2.5 px-3 pb-2.5 pt-1.5 text-gray-500 dark:text-gray-400 max-md:flex-nowrap max-md:overflow-x-auto sm:gap-2"
151150
>
152151
<HoverTooltip
153152
label="Search the web"
@@ -299,7 +298,7 @@
299298
TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 max-sm:hidden"
300299
>
301300
<a
302-
class="base-tool flex !size-[20px] items-center justify-center rounded-full bg-white/10"
301+
class="base-tool flex !size-[20px] items-center justify-center rounded-full border !border-gray-200 !bg-white !transition-none dark:!border-gray-500 dark:!bg-transparent"
303302
href={`${base}/tools`}
304303
title="Browse more tools"
305304
>
@@ -321,10 +320,10 @@
321320
}
322321
323322
.base-tool {
324-
@apply flex h-[1.6rem] items-center gap-[.2rem] whitespace-nowrap text-xs outline-none transition-all focus:outline-none active:outline-none dark:hover:text-gray-300 sm:hover:text-purple-600;
323+
@apply flex h-[1.6rem] items-center gap-[.2rem] whitespace-nowrap border border-transparent text-xs outline-none transition-all focus:outline-none active:outline-none dark:hover:text-gray-300 sm:hover:text-purple-600;
325324
}
326325
327326
.active-tool {
328-
@apply rounded-full bg-purple-500/15 pl-1 pr-2 text-purple-600 hover:text-purple-600 dark:bg-purple-600/40 dark:text-purple-300;
327+
@apply rounded-full !border-purple-200 bg-purple-100 pl-1 pr-2 text-purple-600 hover:text-purple-600 dark:!border-purple-700 dark:bg-purple-600/40 dark:text-purple-200;
329328
}
330329
</style>

src/lib/components/chat/ChatWindow.svelte

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import type { Message, MessageFile } from "$lib/types/Message";
33
import { createEventDispatcher, onDestroy, tick } from "svelte";
44
5-
import CarbonSendAltFilled from "~icons/carbon/send-alt-filled";
65
import CarbonExport from "~icons/carbon/export";
76
import CarbonCheckmark from "~icons/carbon/checkmark";
87
import CarbonCaretDown from "~icons/carbon/caret-down";
@@ -376,7 +375,7 @@
376375
{/if}
377376

378377
<div class="w-full">
379-
<div class="flex w-full pb-3">
378+
<div class="flex w-full *:mb-3">
380379
{#if loading}
381380
<StopGeneratingBtn classNames="ml-auto" on:click={() => dispatch("stop")} />
382381
{:else if lastIsError}
@@ -390,27 +389,25 @@
390389
}
391390
}}
392391
/>
393-
{:else}
392+
{:else if messages && lastMessage && lastMessage.interrupted && !isReadOnly}
394393
<div class="ml-auto gap-2">
395-
{#if messages && lastMessage && lastMessage.interrupted && !isReadOnly}
396-
<ContinueBtn
397-
on:click={() => {
398-
if (lastMessage && lastMessage.ancestors) {
399-
dispatch("continue", {
400-
id: lastMessage?.id,
401-
});
402-
}
403-
}}
404-
/>
405-
{/if}
394+
<ContinueBtn
395+
on:click={() => {
396+
if (lastMessage && lastMessage.ancestors) {
397+
dispatch("continue", {
398+
id: lastMessage?.id,
399+
});
400+
}
401+
}}
402+
/>
406403
</div>
407404
{/if}
408405
</div>
409406
<form
410407
tabindex="-1"
411408
aria-label={isFileUploadEnabled ? "file dropzone" : undefined}
412409
on:submit|preventDefault={handleSubmit}
413-
class="relative flex w-full max-w-4xl flex-1 items-center rounded-xl border bg-gray-100 focus-within:border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:focus-within:border-gray-500
410+
class="relative flex w-full max-w-4xl flex-1 items-center rounded-xl border bg-gray-100 dark:border-gray-600 dark:bg-gray-700
414411
{isReadOnly ? 'opacity-30' : ''}"
415412
>
416413
{#if onDrag && isFileUploadEnabled}
@@ -453,13 +450,26 @@
453450
</button>
454451
{:else}
455452
<button
456-
class="btn absolute bottom-1 right-0.5 size-10 self-end rounded-lg bg-transparent text-gray-400 enabled:hover:text-gray-700 disabled:opacity-60 enabled:dark:hover:text-gray-100 dark:disabled:opacity-40"
453+
class="btn absolute bottom-2 right-2 size-7 self-end rounded-full border bg-white text-black shadow transition-none enabled:hover:bg-white enabled:hover:shadow-inner disabled:opacity-60 dark:border-gray-600 dark:bg-gray-900 dark:text-white dark:hover:enabled:bg-black"
457454
disabled={!message || isReadOnly}
458455
type="submit"
459456
aria-label="Send message"
460457
name="submit"
461458
>
462-
<CarbonSendAltFilled />
459+
<svg
460+
width="1em"
461+
height="1em"
462+
viewBox="0 0 32 32"
463+
fill="none"
464+
xmlns="http://www.w3.org/2000/svg"
465+
>
466+
<path
467+
fill-rule="evenodd"
468+
clip-rule="evenodd"
469+
d="M17.0606 4.23197C16.4748 3.64618 15.525 3.64618 14.9393 4.23197L5.68412 13.4871C5.09833 14.0729 5.09833 15.0226 5.68412 15.6084C6.2699 16.1942 7.21965 16.1942 7.80544 15.6084L14.4999 8.91395V26.7074C14.4999 27.5359 15.1715 28.2074 15.9999 28.2074C16.8283 28.2074 17.4999 27.5359 17.4999 26.7074V8.91395L24.1944 15.6084C24.7802 16.1942 25.7299 16.1942 26.3157 15.6084C26.9015 15.0226 26.9015 14.0729 26.3157 13.4871L17.0606 4.23197Z"
470+
fill="currentColor"
471+
/>
472+
</svg>
463473
</button>
464474
{/if}
465475
</div>

src/routes/+layout.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@
234234
/>
235235

236236
<div
237-
class="grid h-full w-screen grid-cols-1 grid-rows-[auto,1fr] overflow-hidden text-smd {!isNavCollapsed
237+
class="fixed grid h-full w-screen grid-cols-1 grid-rows-[auto,1fr] overflow-hidden text-smd {!isNavCollapsed
238238
? 'md:grid-cols-[290px,1fr]'
239239
: 'md:grid-cols-[0px,1fr]'} transition-[300ms] [transition-property:grid-template-columns] dark:text-gray-300 md:grid-rows-[1fr]"
240240
>

0 commit comments

Comments
 (0)