| author | |
| committer | |
| log | dd23bd73d94dd711df16fea5d380ddd7a194cf0e |
| tree | ef73878d51462452b3ff07e7e2292490e727fe6d |
| parent | 8dd107429e92dfcb52d8afb46e2a01517840fabc |
- 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 actions2 files changed, 36 insertions(+), 3 deletions(-)
apps/web/src/components/BranchToolbar.tsx+11-1| ... | ... | @@ -25,9 +25,15 @@ interface BranchToolbarProps { |
| 25 | 25 | envMode: "local" | "worktree"; |
| 26 | 26 | onEnvModeChange: (mode: "local" | "worktree") => void; |
| 27 | 27 | envLocked: boolean; |
| 28 | onComposerFocusRequest?: () => void; | |
| 28 | 29 | } |
| 29 | 30 | |
| 30 | export default function BranchToolbar({ envMode, onEnvModeChange, envLocked }: BranchToolbarProps) { | |
| 31 | export default function BranchToolbar({ | |
| 32 | envMode, | |
| 33 | onEnvModeChange, | |
| 34 | envLocked, | |
| 35 | onComposerFocusRequest, | |
| 36 | }: BranchToolbarProps) { | |
| 31 | 37 | const { state, dispatch } = useStore(); |
| 32 | 38 | const api = useNativeApi(); |
| 33 | 39 | const queryClient = useQueryClient(); |
| ... | ... | @@ -123,6 +129,7 @@ export default function BranchToolbar({ envMode, onEnvModeChange, envLocked }: B |
| 123 | 129 | setThreadError(null); |
| 124 | 130 | setThreadBranch(branch.name, null); |
| 125 | 131 | setIsBranchMenuOpen(false); |
| 132 | onComposerFocusRequest?.(); | |
| 126 | 133 | return; |
| 127 | 134 | } |
| 128 | 135 | |
| ... | ... | @@ -135,6 +142,7 @@ export default function BranchToolbar({ envMode, onEnvModeChange, envLocked }: B |
| 135 | 142 | // Secondary worktree → point the thread at that worktree path. |
| 136 | 143 | setThreadBranch(branch.name, isMainWorktree ? null : branch.worktreePath); |
| 137 | 144 | setIsBranchMenuOpen(false); |
| 145 | onComposerFocusRequest?.(); | |
| 138 | 146 | return; |
| 139 | 147 | } |
| 140 | 148 | |
| ... | ... | @@ -143,6 +151,7 @@ export default function BranchToolbar({ envMode, onEnvModeChange, envLocked }: B |
| 143 | 151 | setThreadError(null); |
| 144 | 152 | setThreadBranch(branch.name, activeWorktreePath); |
| 145 | 153 | setIsBranchMenuOpen(false); |
| 154 | onComposerFocusRequest?.(); | |
| 146 | 155 | }, |
| 147 | 156 | }); |
| 148 | 157 | }; |
| ... | ... | @@ -156,6 +165,7 @@ export default function BranchToolbar({ envMode, onEnvModeChange, envLocked }: B |
| 156 | 165 | setThreadBranch(name, activeWorktreePath); |
| 157 | 166 | setBranchQuery(""); |
| 158 | 167 | setIsBranchMenuOpen(false); |
| 168 | onComposerFocusRequest?.(); | |
| 159 | 169 | }, |
| 160 | 170 | }); |
| 161 | 171 | }; |
apps/web/src/components/ChatView.tsx+25-2| ... | ... | @@ -298,6 +298,11 @@ export default function ChatView() { |
| 298 | 298 | const cursor = textarea.value.length; |
| 299 | 299 | textarea.setSelectionRange(cursor, cursor); |
| 300 | 300 | }, []); |
| 301 | const scheduleComposerFocus = useCallback(() => { | |
| 302 | window.requestAnimationFrame(() => { | |
| 303 | focusComposer(); | |
| 304 | }); | |
| 305 | }, [focusComposer]); | |
| 301 | 306 | const toggleTerminalVisibility = useCallback(() => { |
| 302 | 307 | if (!activeThreadId) return; |
| 303 | 308 | dispatch({ |
| ... | ... | @@ -369,6 +374,7 @@ export default function ChatView() { |
| 369 | 374 | const handleRuntimeModeChange = async (mode: "approval-required" | "full-access") => { |
| 370 | 375 | if (mode === state.runtimeMode) return; |
| 371 | 376 | dispatch({ type: "SET_RUNTIME_MODE", mode }); |
| 377 | scheduleComposerFocus(); | |
| 372 | 378 | if (!api) return; |
| 373 | 379 | |
| 374 | 380 | const sessionIds = state.threads |
| ... | ... | @@ -898,7 +904,19 @@ export default function ChatView() { |
| 898 | 904 | threadId: activeThread.id, |
| 899 | 905 | model: resolveModelSlug(model), |
| 900 | 906 | }); |
| 907 | scheduleComposerFocus(); | |
| 908 | }; | |
| 909 | const onEffortSelect = (effort: ReasoningEffort) => { | |
| 910 | setSelectedEffort(effort); | |
| 911 | scheduleComposerFocus(); | |
| 901 | 912 | }; |
| 913 | const onEnvModeChange = useCallback( | |
| 914 | (mode: "local" | "worktree") => { | |
| 915 | setEnvMode(mode); | |
| 916 | scheduleComposerFocus(); | |
| 917 | }, | |
| 918 | [scheduleComposerFocus], | |
| 919 | ); | |
| 902 | 920 | |
| 903 | 921 | const onKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => { |
| 904 | 922 | if (e.key === "Enter" && !e.shiftKey) { |
| ... | ... | @@ -1302,7 +1320,7 @@ export default function ChatView() { |
| 1302 | 1320 | <Separator orientation="vertical" className="mx-0.5 h-4" /> |
| 1303 | 1321 | |
| 1304 | 1322 | {/* Reasoning effort */} |
| 1305 | <ReasoningEffortPicker effort={selectedEffort} onEffortChange={setSelectedEffort} /> | |
| 1323 | <ReasoningEffortPicker effort={selectedEffort} onEffortChange={onEffortSelect} /> | |
| 1306 | 1324 | |
| 1307 | 1325 | {/* Divider */} |
| 1308 | 1326 | <Separator orientation="vertical" className="mx-0.5 h-4" /> |
| ... | ... | @@ -1405,7 +1423,12 @@ export default function ChatView() { |
| 1405 | 1423 | </div> |
| 1406 | 1424 | |
| 1407 | 1425 | {isGitRepo && ( |
| 1408 | <BranchToolbar envMode={envMode} onEnvModeChange={setEnvMode} envLocked={envLocked} /> | |
| 1426 | <BranchToolbar | |
| 1427 | envMode={envMode} | |
| 1428 | onEnvModeChange={onEnvModeChange} | |
| 1429 | envLocked={envLocked} | |
| 1430 | onComposerFocusRequest={scheduleComposerFocus} | |
| 1431 | /> | |
| 1409 | 1432 | )} |
| 1410 | 1433 | |
| 1411 | 1434 | {activeThread.terminalOpen && api && activeProject && ( |