authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-15 01:29:27-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-02-15 16:27:25-08:00
logdd23bd73d94dd711df16fea5d380ddd7a194cf0e
treeef73878d51462452b3ff07e7e2292490e727fe6d
parent8dd107429e92dfcb52d8afb46e2a01517840fabc

Keep composer focused after toolbar and mode changes

- Add a shared composer-focus scheduler in `ChatView` - Refocus composer after runtime mode, model, effort, and env mode selections - Let `BranchToolbar` request composer focus after branch/worktree actions

2 files changed, 36 insertions(+), 3 deletions(-)

apps/web/src/components/BranchToolbar.tsx+11-1
......@@ -25,9 +25,15 @@ interface BranchToolbarProps {
2525 envMode: "local" | "worktree";
2626 onEnvModeChange: (mode: "local" | "worktree") => void;
2727 envLocked: boolean;
28 onComposerFocusRequest?: () => void;
2829}
2930
30export default function BranchToolbar({ envMode, onEnvModeChange, envLocked }: BranchToolbarProps) {
31export default function BranchToolbar({
32 envMode,
33 onEnvModeChange,
34 envLocked,
35 onComposerFocusRequest,
36}: BranchToolbarProps) {
3137 const { state, dispatch } = useStore();
3238 const api = useNativeApi();
3339 const queryClient = useQueryClient();
......@@ -123,6 +129,7 @@ export default function BranchToolbar({ envMode, onEnvModeChange, envLocked }: B
123129 setThreadError(null);
124130 setThreadBranch(branch.name, null);
125131 setIsBranchMenuOpen(false);
132 onComposerFocusRequest?.();
126133 return;
127134 }
128135
......@@ -135,6 +142,7 @@ export default function BranchToolbar({ envMode, onEnvModeChange, envLocked }: B
135142 // Secondary worktree → point the thread at that worktree path.
136143 setThreadBranch(branch.name, isMainWorktree ? null : branch.worktreePath);
137144 setIsBranchMenuOpen(false);
145 onComposerFocusRequest?.();
138146 return;
139147 }
140148
......@@ -143,6 +151,7 @@ export default function BranchToolbar({ envMode, onEnvModeChange, envLocked }: B
143151 setThreadError(null);
144152 setThreadBranch(branch.name, activeWorktreePath);
145153 setIsBranchMenuOpen(false);
154 onComposerFocusRequest?.();
146155 },
147156 });
148157 };
......@@ -156,6 +165,7 @@ export default function BranchToolbar({ envMode, onEnvModeChange, envLocked }: B
156165 setThreadBranch(name, activeWorktreePath);
157166 setBranchQuery("");
158167 setIsBranchMenuOpen(false);
168 onComposerFocusRequest?.();
159169 },
160170 });
161171 };
apps/web/src/components/ChatView.tsx+25-2
......@@ -298,6 +298,11 @@ export default function ChatView() {
298298 const cursor = textarea.value.length;
299299 textarea.setSelectionRange(cursor, cursor);
300300 }, []);
301 const scheduleComposerFocus = useCallback(() => {
302 window.requestAnimationFrame(() => {
303 focusComposer();
304 });
305 }, [focusComposer]);
301306 const toggleTerminalVisibility = useCallback(() => {
302307 if (!activeThreadId) return;
303308 dispatch({
......@@ -369,6 +374,7 @@ export default function ChatView() {
369374 const handleRuntimeModeChange = async (mode: "approval-required" | "full-access") => {
370375 if (mode === state.runtimeMode) return;
371376 dispatch({ type: "SET_RUNTIME_MODE", mode });
377 scheduleComposerFocus();
372378 if (!api) return;
373379
374380 const sessionIds = state.threads
......@@ -898,7 +904,19 @@ export default function ChatView() {
898904 threadId: activeThread.id,
899905 model: resolveModelSlug(model),
900906 });
907 scheduleComposerFocus();
908 };
909 const onEffortSelect = (effort: ReasoningEffort) => {
910 setSelectedEffort(effort);
911 scheduleComposerFocus();
901912 };
913 const onEnvModeChange = useCallback(
914 (mode: "local" | "worktree") => {
915 setEnvMode(mode);
916 scheduleComposerFocus();
917 },
918 [scheduleComposerFocus],
919 );
902920
903921 const onKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
904922 if (e.key === "Enter" && !e.shiftKey) {
......@@ -1302,7 +1320,7 @@ export default function ChatView() {
13021320 <Separator orientation="vertical" className="mx-0.5 h-4" />
13031321
13041322 {/* Reasoning effort */}
1305 <ReasoningEffortPicker effort={selectedEffort} onEffortChange={setSelectedEffort} />
1323 <ReasoningEffortPicker effort={selectedEffort} onEffortChange={onEffortSelect} />
13061324
13071325 {/* Divider */}
13081326 <Separator orientation="vertical" className="mx-0.5 h-4" />
......@@ -1405,7 +1423,12 @@ export default function ChatView() {
14051423 </div>
14061424
14071425 {isGitRepo && (
1408 <BranchToolbar envMode={envMode} onEnvModeChange={setEnvMode} envLocked={envLocked} />
1426 <BranchToolbar
1427 envMode={envMode}
1428 onEnvModeChange={onEnvModeChange}
1429 envLocked={envLocked}
1430 onComposerFocusRequest={scheduleComposerFocus}
1431 />
14091432 )}
14101433
14111434 {activeThread.terminalOpen && api && activeProject && (