From dce4a6d3ce22d160296633a69b102666eea2d565 Mon Sep 17 00:00:00 2001 From: Aleksander Grygier Date: Thu, 24 Sep 2026 13:50:50 +0200 Subject: [PATCH] ui : close the manager and focus the composer on a new chat Assisted-by: pi:llama.cpp/DeepSeek-V4.1-Flash --- .../app/chat/ChatScreen/ChatScreenForm.svelte | 11 ++++++++++- .../app/dialogs/DialogManageModels.svelte | 3 ++- .../models/ModelsManager/ModelsManager.svelte | 2 ++ tools/ui/src/lib/stores/ui.svelte.ts | 18 +++++++++++++++++- 4 files changed, 31 insertions(+), 3 deletions(-) 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 962b6774fc..cc11851fa4 100644 --- a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenForm.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenForm.svelte @@ -3,7 +3,7 @@ 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 { deviceStore, uiStore } from '$lib/stores'; import { onMount } from 'svelte'; interface Props { @@ -33,6 +33,15 @@ }: Props = $props(); let chatFormRef: ChatForm | undefined = $state(undefined); + + // a dialog that closed onto the chat asks for the composer to take focus + $effect(() => { + if (!uiStore.consumeComposerFocus()) return; + + const frame = requestAnimationFrame(() => chatFormRef?.focus()); + + return () => cancelAnimationFrame(frame); + }); let formWrapperEl: HTMLDivElement | undefined = $state(); let chatId = $derived(page.params.id as string | undefined); diff --git a/tools/ui/src/lib/components/app/dialogs/DialogManageModels.svelte b/tools/ui/src/lib/components/app/dialogs/DialogManageModels.svelte index 50d8351c97..8c5217f628 100644 --- a/tools/ui/src/lib/components/app/dialogs/DialogManageModels.svelte +++ b/tools/ui/src/lib/components/app/dialogs/DialogManageModels.svelte @@ -21,6 +21,7 @@ event.preventDefault()} onOpenAutoFocus={(event) => event.preventDefault()} > @@ -37,6 +38,6 @@ - + handleOpenChange(false)} /> diff --git a/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManager.svelte b/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManager.svelte index 6f842ee4be..f4d4711e59 100644 --- a/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManager.svelte +++ b/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManager.svelte @@ -260,6 +260,8 @@ async function useInNewChat(option: ModelOption): Promise { await modelsStore.selectModelById(option.id); await conversationsStore.openNewChat(); + // the chat is behind the dialog, so it takes focus once the dialog is out of the way + uiStore.requestComposerFocus(); onClose?.(); } diff --git a/tools/ui/src/lib/stores/ui.svelte.ts b/tools/ui/src/lib/stores/ui.svelte.ts index 4b481d0b85..a232a0d598 100644 --- a/tools/ui/src/lib/stores/ui.svelte.ts +++ b/tools/ui/src/lib/stores/ui.svelte.ts @@ -7,14 +7,25 @@ */ class UiStore { - discoverModelsOpen = $state(false); + /** Set when a flow outside the chat wants the composer focused once it can take it. */ + composerFocusRequested = $state(false); + discoverModelsOpen = $state(false); /** Whether the desktop sidebar is expanded (open). */ isSidebarExpanded = $state(false); /** Model the manager reveals when it opens, a qualified id or a raw model name. */ manageModelFocus = $state(null); /** Open state of the models manager, driven from the sidebar and from model rows. */ manageModelsOpen = $state(false); + /** Read and clear the request, so it fires once. */ + consumeComposerFocus(): boolean { + const requested = this.composerFocusRequested; + + this.composerFocusRequested = false; + + return requested; + } + openDiscoverModels(): void { this.discoverModelsOpen = true; } @@ -24,6 +35,11 @@ class UiStore { this.manageModelFocus = focus ?? null; this.manageModelsOpen = true; } + + /** Ask the composer to take focus, e.g. after a dialog closes onto the chat. */ + requestComposerFocus(): void { + this.composerFocusRequested = true; + } } export const uiStore = new UiStore();