| ... | ... | @@ -9,6 +9,7 @@ import { |
| 9 | 9 | gitCreateBranchAndCheckoutMutationOptions, |
| 10 | 10 | } from "../lib/gitReactQuery"; |
| 11 | 11 | import { readNativeApi } from "../nativeApi"; |
| 12 | import { useComposerDraftStore } from "../composerDraftStore"; |
| 12 | 13 | import { useStore } from "../store"; |
| 13 | 14 | import { |
| 14 | 15 | dedupeRemoteBranchesWithLocalMatches, |
| ... | ... | @@ -43,17 +44,21 @@ export default function BranchToolbar({ |
| 43 | 44 | onComposerFocusRequest, |
| 44 | 45 | }: BranchToolbarProps) { |
| 45 | 46 | const { state, dispatch } = useStore(); |
| 47 | const draftThread = useComposerDraftStore((store) => store.getDraftThread(threadId)); |
| 48 | const setDraftThreadContext = useComposerDraftStore((store) => store.setDraftThreadContext); |
| 46 | 49 | const queryClient = useQueryClient(); |
| 47 | 50 | |
| 48 | 51 | const [isBranchMenuOpen, setIsBranchMenuOpen] = useState(false); |
| 49 | 52 | const [branchQuery, setBranchQuery] = useState(""); |
| 50 | 53 | |
| 51 | | const activeThread = state.threads.find((thread) => thread.id === threadId); |
| 52 | | const activeProject = state.projects.find((project) => project.id === activeThread?.projectId); |
| 53 | | const activeThreadId = activeThread?.id; |
| 54 | | const activeThreadBranch = activeThread?.branch ?? null; |
| 55 | | const activeWorktreePath = activeThread?.worktreePath ?? null; |
| 54 | const serverThread = state.threads.find((thread) => thread.id === threadId); |
| 55 | const activeProjectId = serverThread?.projectId ?? draftThread?.projectId ?? null; |
| 56 | const activeProject = state.projects.find((project) => project.id === activeProjectId); |
| 57 | const activeThreadId = serverThread?.id ?? (draftThread ? threadId : undefined); |
| 58 | const activeThreadBranch = serverThread?.branch ?? draftThread?.branch ?? null; |
| 59 | const activeWorktreePath = serverThread?.worktreePath ?? draftThread?.worktreePath ?? null; |
| 56 | 60 | const branchCwd = activeWorktreePath ?? activeProject?.cwd ?? null; |
| 61 | const hasServerThread = serverThread !== undefined; |
| 57 | 62 | |
| 58 | 63 | // ── Queries ─────────────────────────────────────────────────────────── |
| 59 | 64 | |
| ... | ... | @@ -95,7 +100,7 @@ export default function BranchToolbar({ |
| 95 | 100 | if (!activeThreadId || !syncedBranch) return; |
| 96 | 101 | const api = readNativeApi(); |
| 97 | 102 | |
| 98 | | if (api) { |
| 103 | if (api && hasServerThread) { |
| 99 | 104 | void api.orchestration.dispatchCommand({ |
| 100 | 105 | type: "thread.meta.update", |
| 101 | 106 | commandId: newCommandId(), |
| ... | ... | @@ -104,13 +109,30 @@ export default function BranchToolbar({ |
| 104 | 109 | worktreePath: null, |
| 105 | 110 | }); |
| 106 | 111 | } |
| 107 | | dispatch({ |
| 108 | | type: "SET_THREAD_BRANCH", |
| 109 | | threadId: activeThreadId, |
| 112 | if (hasServerThread) { |
| 113 | dispatch({ |
| 114 | type: "SET_THREAD_BRANCH", |
| 115 | threadId: activeThreadId, |
| 116 | branch: syncedBranch, |
| 117 | worktreePath: null, |
| 118 | }); |
| 119 | return; |
| 120 | } |
| 121 | setDraftThreadContext(threadId, { |
| 110 | 122 | branch: syncedBranch, |
| 111 | 123 | worktreePath: null, |
| 112 | 124 | }); |
| 113 | | }, [activeThreadId, activeWorktreePath, activeThreadBranch, queryBranches, envMode, dispatch]); |
| 125 | }, [ |
| 126 | activeThreadId, |
| 127 | activeWorktreePath, |
| 128 | activeThreadBranch, |
| 129 | queryBranches, |
| 130 | envMode, |
| 131 | dispatch, |
| 132 | hasServerThread, |
| 133 | setDraftThreadContext, |
| 134 | threadId, |
| 135 | ]); |
| 114 | 136 | |
| 115 | 137 | useEffect(() => { |
| 116 | 138 | if (isBranchMenuOpen) return; |
| ... | ... | @@ -129,7 +151,7 @@ export default function BranchToolbar({ |
| 129 | 151 | const api = readNativeApi(); |
| 130 | 152 | // If the effective cwd is about to change, stop the running session so the |
| 131 | 153 | // next message creates a new one with the correct cwd. |
| 132 | | const sessionId = activeThread?.session?.sessionId; |
| 154 | const sessionId = serverThread?.session?.sessionId; |
| 133 | 155 | if (sessionId && worktreePath !== activeWorktreePath && api) { |
| 134 | 156 | void api.orchestration |
| 135 | 157 | .dispatchCommand({ |
| ... | ... | @@ -140,7 +162,7 @@ export default function BranchToolbar({ |
| 140 | 162 | }) |
| 141 | 163 | .catch(() => undefined); |
| 142 | 164 | } |
| 143 | | if (api) { |
| 165 | if (api && hasServerThread) { |
| 144 | 166 | void api.orchestration.dispatchCommand({ |
| 145 | 167 | type: "thread.meta.update", |
| 146 | 168 | commandId: newCommandId(), |
| ... | ... | @@ -149,7 +171,14 @@ export default function BranchToolbar({ |
| 149 | 171 | worktreePath, |
| 150 | 172 | }); |
| 151 | 173 | } |
| 152 | | dispatch({ type: "SET_THREAD_BRANCH", threadId: activeThreadId, branch, worktreePath }); |
| 174 | if (hasServerThread) { |
| 175 | dispatch({ type: "SET_THREAD_BRANCH", threadId: activeThreadId, branch, worktreePath }); |
| 176 | return; |
| 177 | } |
| 178 | setDraftThreadContext(threadId, { |
| 179 | branch, |
| 180 | worktreePath, |
| 181 | }); |
| 153 | 182 | }; |
| 154 | 183 | |
| 155 | 184 | const selectBranch = (branch: GitBranch) => { |
| ... | ... | @@ -221,7 +250,7 @@ export default function BranchToolbar({ |
| 221 | 250 | }); |
| 222 | 251 | }; |
| 223 | 252 | |
| 224 | | if (!activeThread || !activeProject) return null; |
| 253 | if (!activeThreadId || !activeProject) return null; |
| 225 | 254 | |
| 226 | 255 | return ( |
| 227 | 256 | <div className="mx-auto flex w-full max-w-3xl items-center justify-between px-5 pb-3 pt-1"> |
| ... | ... | @@ -248,7 +277,7 @@ export default function BranchToolbar({ |
| 248 | 277 | autoHighlight |
| 249 | 278 | onOpenChange={(open) => setIsBranchMenuOpen(open)} |
| 250 | 279 | open={isBranchMenuOpen} |
| 251 | | value={activeThread.branch} |
| 280 | value={activeThreadBranch} |
| 252 | 281 | > |
| 253 | 282 | <ComboboxTrigger |
| 254 | 283 | render={<Button variant="ghost" size="xs" />} |
| ... | ... | @@ -256,10 +285,10 @@ export default function BranchToolbar({ |
| 256 | 285 | disabled={branchesQuery.isLoading} |
| 257 | 286 | > |
| 258 | 287 | <span className="max-w-[240px] truncate"> |
| 259 | | {activeThread.branch |
| 288 | {activeThreadBranch |
| 260 | 289 | ? envMode === "worktree" && !activeWorktreePath |
| 261 | | ? `From ${activeThread.branch}` |
| 262 | | : activeThread.branch |
| 290 | ? `From ${activeThreadBranch}` |
| 291 | : activeThreadBranch |
| 263 | 292 | : "Select branch"} |
| 264 | 293 | </span> |
| 265 | 294 | <ChevronDownIcon /> |
| ... | ... | @@ -313,7 +342,7 @@ export default function BranchToolbar({ |
| 313 | 342 | key={itemValue} |
| 314 | 343 | value={itemValue} |
| 315 | 344 | className={ |
| 316 | | itemValue === activeThread.branch ? "bg-accent text-foreground" : undefined |
| 345 | itemValue === activeThreadBranch ? "bg-accent text-foreground" : undefined |
| 317 | 346 | } |
| 318 | 347 | onClick={() => selectBranch(branch)} |
| 319 | 348 | > |