From 20aa02269322391185394ed0321ed9b50508f033 Mon Sep 17 00:00:00 2001 From: Caleb Owens Date: Tue, 18 Nov 2025 14:38:12 +0100 Subject: [PATCH] Use repeated query selector --- apps/desktop/src/components/BranchList.svelte | 4 +-- .../src/lib/codegen/focusClaudeInput.ts | 34 ++++++++++++++++--- .../src/lib/stacks/createAiStack.svelte.ts | 4 --- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/apps/desktop/src/components/BranchList.svelte b/apps/desktop/src/components/BranchList.svelte index 87c52a9ff1..0c2abec26c 100644 --- a/apps/desktop/src/components/BranchList.svelte +++ b/apps/desktop/src/components/BranchList.svelte @@ -331,9 +331,7 @@ onclick={async () => { if (!stackId) return; laneState?.selection.set({ branchName, codegen: true, previewOpen: true }); - setTimeout(() => { - focusClaudeInput(stackId); - }, 100); + focusClaudeInput(stackId); }} /> {/if} diff --git a/apps/desktop/src/lib/codegen/focusClaudeInput.ts b/apps/desktop/src/lib/codegen/focusClaudeInput.ts index ada1ea4bac..541d76045e 100644 --- a/apps/desktop/src/lib/codegen/focusClaudeInput.ts +++ b/apps/desktop/src/lib/codegen/focusClaudeInput.ts @@ -1,9 +1,33 @@ -export function focusClaudeInput(stackId: string) { - // This is a hacky way, but we need the job done until we - // can figure out a good way of autofocusing text inputs, - // without too many of them firing at the wrong times. - const element = document.querySelector(`[data-id="${stackId}"] .ContentEditable__root`); +import { sleep } from '$lib/utils/sleep'; + +/** + * Tries to focus the claude input. + * + * This currently operates via a retried selector. + */ +export async function focusClaudeInput(stackId: string) { + const element = await hackyRetriedSelector(`[data-id="${stackId}"] .ContentEditable__root`); if (element instanceof HTMLElement) { element?.focus(); } } + +/** + * Tries to find the element at a given selector with a time limit and a + * refreshInterval. + * + * Retried selectors should be the _last_ resort. Prefer anything else. + */ +async function hackyRetriedSelector( + selector: string, + timeLimit = 50, + refreshInterval = 1 +): Promise { + let tries = 0; + while (refreshInterval * tries < timeLimit) { + const element = document.querySelector(selector); + if (element) return element; + await sleep(refreshInterval); + tries += 1; + } +} diff --git a/apps/desktop/src/lib/stacks/createAiStack.svelte.ts b/apps/desktop/src/lib/stacks/createAiStack.svelte.ts index f4933ae834..b5b19b995d 100644 --- a/apps/desktop/src/lib/stacks/createAiStack.svelte.ts +++ b/apps/desktop/src/lib/stacks/createAiStack.svelte.ts @@ -1,7 +1,6 @@ import { focusClaudeInput } from '$lib/codegen/focusClaudeInput'; import { STACK_SERVICE } from '$lib/stacks/stackService.svelte'; import { UI_STATE } from '$lib/state/uiState.svelte'; -import { sleep } from '$lib/utils/sleep'; import { inject } from '@gitbutler/core/context'; import { untrack } from 'svelte'; import type { Reactive } from '@gitbutler/shared/storeUtils'; @@ -23,9 +22,6 @@ export function useCreateAiStack(projectId: Reactive) { const lane = uiState.lane(stack.id); lane.selection.set({ codegen: true, branchName: stack.heads[0]?.name, previewOpen: true }); - // Because the ui state is updated asyncly, we need to let some time - // pass. This is far from a good solution to this problem. - await sleep(50); focusClaudeInput(stack.id); }