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();