import type { ThreadId } from "@t3tools/contracts"; import { useCallback } from "react"; import { newCommandId } from "../lib/utils"; import { readNativeApi } from "../nativeApi"; import { useComposerDraftStore } from "../composerDraftStore"; import { useStore } from "../store"; import { EnvMode, resolveDraftEnvModeAfterBranchChange, resolveEffectiveEnvMode, } from "./BranchToolbar.logic"; import { BranchToolbarBranchSelector } from "./BranchToolbarBranchSelector"; import { Button } from "./ui/button"; interface BranchToolbarProps { threadId: ThreadId; onEnvModeChange: (mode: EnvMode) => void; envLocked: boolean; onComposerFocusRequest?: () => void; } export default function BranchToolbar({ threadId, onEnvModeChange, envLocked, onComposerFocusRequest, }: BranchToolbarProps) { const threads = useStore((store) => store.threads); const projects = useStore((store) => store.projects); const setThreadBranchAction = useStore((store) => store.setThreadBranch); const draftThread = useComposerDraftStore((store) => store.getDraftThread(threadId)); const setDraftThreadContext = useComposerDraftStore((store) => store.setDraftThreadContext); const serverThread = threads.find((thread) => thread.id === threadId); const activeProjectId = serverThread?.projectId ?? draftThread?.projectId ?? null; const activeProject = projects.find((project) => project.id === activeProjectId); const activeThreadId = serverThread?.id ?? (draftThread ? threadId : undefined); const activeThreadBranch = serverThread?.branch ?? draftThread?.branch ?? null; const activeWorktreePath = serverThread?.worktreePath ?? draftThread?.worktreePath ?? null; const branchCwd = activeWorktreePath ?? activeProject?.cwd ?? null; const hasServerThread = serverThread !== undefined; const effectiveEnvMode = resolveEffectiveEnvMode({ activeWorktreePath, hasServerThread, draftThreadEnvMode: draftThread?.envMode, }); const setThreadBranch = useCallback( (branch: string | null, worktreePath: string | null) => { if (!activeThreadId) return; const api = readNativeApi(); // If the effective cwd is about to change, stop the running session so the // next message creates a new one with the correct cwd. if (serverThread?.session && worktreePath !== activeWorktreePath && api) { void api.orchestration .dispatchCommand({ type: "thread.session.stop", commandId: newCommandId(), threadId: activeThreadId, createdAt: new Date().toISOString(), }) .catch(() => undefined); } if (api && hasServerThread) { void api.orchestration.dispatchCommand({ type: "thread.meta.update", commandId: newCommandId(), threadId: activeThreadId, branch, worktreePath, }); } if (hasServerThread) { setThreadBranchAction(activeThreadId, branch, worktreePath); return; } const nextDraftEnvMode = resolveDraftEnvModeAfterBranchChange({ nextWorktreePath: worktreePath, currentWorktreePath: activeWorktreePath, effectiveEnvMode, }); setDraftThreadContext(threadId, { branch, worktreePath, envMode: nextDraftEnvMode, }); }, [ activeThreadId, serverThread?.session, activeWorktreePath, hasServerThread, setThreadBranchAction, setDraftThreadContext, threadId, effectiveEnvMode, ], ); if (!activeThreadId || !activeProject) return null; return (