| author | |
| committer | |
| log | e2316814da7463b5332054f463f71020f1c50c08 |
| tree | b3d64c3404c56470c28a0563807978376ba7d0d2 |
| parent | 678f827f0b0713ab5ea9512ce12e034c2c923be8 |
| signature | Signed by PGP key B5690EEEBB952194 |
7 files changed, 181 insertions(+), 9 deletions(-)
apps/web/src/components/BranchToolbar.tsx+3-1| ... | @@ -44,7 +44,9 @@ export const BranchToolbar = memo(function BranchToolbar({ | ... | @@ -44,7 +44,9 @@ export const BranchToolbar = memo(function BranchToolbar({ |
| 44 | ); | 44 | ); |
| 45 | const serverThreadSelector = useMemo(() => createThreadSelectorByRef(threadRef), [threadRef]); | 45 | const serverThreadSelector = useMemo(() => createThreadSelectorByRef(threadRef), [threadRef]); |
| 46 | const serverThread = useStore(serverThreadSelector); | 46 | const serverThread = useStore(serverThreadSelector); |
| 47 | const draftThread = useComposerDraftStore((store) => store.getDraftThreadByRef(threadRef)); | 47 | const draftThread = useComposerDraftStore((store) => |
| 48 | draftId ? store.getDraftSession(draftId) : store.getDraftThreadByRef(threadRef), | ||
| 49 | ); | ||
| 48 | const activeProjectRef = serverThread | 50 | const activeProjectRef = serverThread |
| 49 | ? scopeProjectRef(serverThread.environmentId, serverThread.projectId) | 51 | ? scopeProjectRef(serverThread.environmentId, serverThread.projectId) |
| 50 | : draftThread | 52 | : draftThread |
apps/web/src/components/BranchToolbarBranchSelector.tsx+3-1| ... | @@ -91,7 +91,9 @@ export function BranchToolbarBranchSelector({ | ... | @@ -91,7 +91,9 @@ export function BranchToolbarBranchSelector({ |
| 91 | const serverThread = useStore(serverThreadSelector); | 91 | const serverThread = useStore(serverThreadSelector); |
| 92 | const serverSession = serverThread?.session ?? null; | 92 | const serverSession = serverThread?.session ?? null; |
| 93 | const setThreadBranchAction = useStore((store) => store.setThreadBranch); | 93 | const setThreadBranchAction = useStore((store) => store.setThreadBranch); |
| 94 | const draftThread = useComposerDraftStore((store) => store.getDraftThreadByRef(threadRef)); | 94 | const draftThread = useComposerDraftStore((store) => |
| 95 | draftId ? store.getDraftSession(draftId) : store.getDraftThreadByRef(threadRef), | ||
| 96 | ); | ||
| 95 | const setDraftThreadContext = useComposerDraftStore((store) => store.setDraftThreadContext); | 97 | const setDraftThreadContext = useComposerDraftStore((store) => store.setDraftThreadContext); |
| 96 | 98 | ||
| 97 | const activeProjectRef = serverThread | 99 | const activeProjectRef = serverThread |
apps/web/src/components/ChatView.browser.tsx+115-1| ... | @@ -1327,6 +1327,7 @@ async function mountChatView(options: { | ... | @@ -1327,6 +1327,7 @@ async function mountChatView(options: { |
| 1327 | snapshot: OrchestrationReadModel; | 1327 | snapshot: OrchestrationReadModel; |
| 1328 | configureFixture?: (fixture: TestFixture) => void; | 1328 | configureFixture?: (fixture: TestFixture) => void; |
| 1329 | resolveRpc?: (body: NormalizedWsRpcRequestBody) => unknown | undefined; | 1329 | resolveRpc?: (body: NormalizedWsRpcRequestBody) => unknown | undefined; |
| 1330 | initialPath?: string; | ||
| 1330 | }): Promise<MountedChatView> { | 1331 | }): Promise<MountedChatView> { |
| 1331 | fixture = buildFixture(options.snapshot); | 1332 | fixture = buildFixture(options.snapshot); |
| 1332 | options.configureFixture?.(fixture); | 1333 | options.configureFixture?.(fixture); |
| ... | @@ -1346,7 +1347,7 @@ async function mountChatView(options: { | ... | @@ -1346,7 +1347,7 @@ async function mountChatView(options: { |
| 1346 | 1347 | ||
| 1347 | const router = getRouter( | 1348 | const router = getRouter( |
| 1348 | createMemoryHistory({ | 1349 | createMemoryHistory({ |
| 1349 | initialEntries: [`/${LOCAL_ENVIRONMENT_ID}/${THREAD_ID}`], | 1350 | initialEntries: [options.initialPath ?? `/${LOCAL_ENVIRONMENT_ID}/${THREAD_ID}`], |
| 1350 | }), | 1351 | }), |
| 1351 | ); | 1352 | ); |
| 1352 | 1353 | ||
| ... | @@ -2512,6 +2513,119 @@ describe("ChatView timeline estimator parity (full app)", () => { | ... | @@ -2512,6 +2513,119 @@ describe("ChatView timeline estimator parity (full app)", () => { |
| 2512 | } | 2513 | } |
| 2513 | }); | 2514 | }); |
| 2514 | 2515 | ||
| 2516 | it("uses the active draft route session when changing the base branch", async () => { | ||
| 2517 | const staleDraftId = draftIdFromPath("/draft/draft-stale-branch-session"); | ||
| 2518 | const activeDraftId = draftIdFromPath("/draft/draft-active-branch-session"); | ||
| 2519 | |||
| 2520 | useComposerDraftStore.setState({ | ||
| 2521 | draftThreadsByThreadKey: { | ||
| 2522 | [staleDraftId]: { | ||
| 2523 | threadId: THREAD_ID, | ||
| 2524 | environmentId: LOCAL_ENVIRONMENT_ID, | ||
| 2525 | projectId: PROJECT_ID, | ||
| 2526 | logicalProjectKey: `${PROJECT_DRAFT_KEY}:stale`, | ||
| 2527 | createdAt: NOW_ISO, | ||
| 2528 | runtimeMode: "full-access", | ||
| 2529 | interactionMode: "default", | ||
| 2530 | branch: "main", | ||
| 2531 | worktreePath: null, | ||
| 2532 | envMode: "worktree", | ||
| 2533 | }, | ||
| 2534 | [activeDraftId]: { | ||
| 2535 | threadId: THREAD_ID, | ||
| 2536 | environmentId: LOCAL_ENVIRONMENT_ID, | ||
| 2537 | projectId: PROJECT_ID, | ||
| 2538 | logicalProjectKey: PROJECT_DRAFT_KEY, | ||
| 2539 | createdAt: NOW_ISO, | ||
| 2540 | runtimeMode: "full-access", | ||
| 2541 | interactionMode: "default", | ||
| 2542 | branch: "main", | ||
| 2543 | worktreePath: null, | ||
| 2544 | envMode: "worktree", | ||
| 2545 | }, | ||
| 2546 | }, | ||
| 2547 | logicalProjectDraftThreadKeyByLogicalProjectKey: { | ||
| 2548 | [`${PROJECT_DRAFT_KEY}:stale`]: staleDraftId, | ||
| 2549 | [PROJECT_DRAFT_KEY]: activeDraftId, | ||
| 2550 | }, | ||
| 2551 | }); | ||
| 2552 | |||
| 2553 | const mounted = await mountChatView({ | ||
| 2554 | viewport: DEFAULT_VIEWPORT, | ||
| 2555 | snapshot: createDraftOnlySnapshot(), | ||
| 2556 | initialPath: `/draft/${activeDraftId}`, | ||
| 2557 | resolveRpc: (body) => { | ||
| 2558 | if (body._tag === WS_METHODS.gitListBranches) { | ||
| 2559 | return { | ||
| 2560 | isRepo: true, | ||
| 2561 | hasOriginRemote: true, | ||
| 2562 | nextCursor: null, | ||
| 2563 | totalCount: 2, | ||
| 2564 | branches: [ | ||
| 2565 | { | ||
| 2566 | name: "main", | ||
| 2567 | current: true, | ||
| 2568 | isDefault: true, | ||
| 2569 | worktreePath: null, | ||
| 2570 | }, | ||
| 2571 | { | ||
| 2572 | name: "release/next", | ||
| 2573 | current: false, | ||
| 2574 | isDefault: false, | ||
| 2575 | worktreePath: null, | ||
| 2576 | }, | ||
| 2577 | ], | ||
| 2578 | }; | ||
| 2579 | } | ||
| 2580 | return undefined; | ||
| 2581 | }, | ||
| 2582 | }); | ||
| 2583 | |||
| 2584 | try { | ||
| 2585 | const branchButton = await waitForElement( | ||
| 2586 | () => | ||
| 2587 | Array.from(document.querySelectorAll("button")).find( | ||
| 2588 | (button) => button.textContent?.trim() === "From main", | ||
| 2589 | ) as HTMLButtonElement | null, | ||
| 2590 | 'Unable to find branch selector button with "From main".', | ||
| 2591 | ); | ||
| 2592 | branchButton.click(); | ||
| 2593 | |||
| 2594 | const branchOption = await waitForElement( | ||
| 2595 | () => | ||
| 2596 | Array.from(document.querySelectorAll("span")).find( | ||
| 2597 | (element) => element.textContent?.trim() === "release/next", | ||
| 2598 | ) as HTMLSpanElement | null, | ||
| 2599 | 'Unable to find the "release/next" branch option.', | ||
| 2600 | ); | ||
| 2601 | branchOption.click(); | ||
| 2602 | |||
| 2603 | await vi.waitFor( | ||
| 2604 | () => { | ||
| 2605 | expect(useComposerDraftStore.getState().getDraftSession(activeDraftId)?.branch).toBe( | ||
| 2606 | "release/next", | ||
| 2607 | ); | ||
| 2608 | expect(useComposerDraftStore.getState().getDraftSession(staleDraftId)?.branch).toBe( | ||
| 2609 | "main", | ||
| 2610 | ); | ||
| 2611 | }, | ||
| 2612 | { timeout: 8_000, interval: 16 }, | ||
| 2613 | ); | ||
| 2614 | |||
| 2615 | await vi.waitFor( | ||
| 2616 | () => { | ||
| 2617 | const updatedButton = Array.from(document.querySelectorAll("button")).find((button) => | ||
| 2618 | button.textContent?.trim().includes("From release/next"), | ||
| 2619 | ); | ||
| 2620 | expect(updatedButton).toBeTruthy(); | ||
| 2621 | }, | ||
| 2622 | { timeout: 8_000, interval: 16 }, | ||
| 2623 | ); | ||
| 2624 | } finally { | ||
| 2625 | await mounted.cleanup(); | ||
| 2626 | } | ||
| 2627 | }); | ||
| 2628 | |||
| 2515 | it("surrounds selected plain text and preserves the inner selection for repeated wrapping", async () => { | 2629 | it("surrounds selected plain text and preserves the inner selection for repeated wrapping", async () => { |
| 2516 | const mounted = await mountChatView({ | 2630 | const mounted = await mountChatView({ |
| 2517 | viewport: DEFAULT_VIEWPORT, | 2631 | viewport: DEFAULT_VIEWPORT, |
apps/web/src/components/ChatView.tsx+1| ... | @@ -3289,6 +3289,7 @@ export default function ChatView(props: ChatViewProps) { | ... | @@ -3289,6 +3289,7 @@ export default function ChatView(props: ChatViewProps) { |
| 3289 | <ChatHeader | 3289 | <ChatHeader |
| 3290 | activeThreadEnvironmentId={activeThread.environmentId} | 3290 | activeThreadEnvironmentId={activeThread.environmentId} |
| 3291 | activeThreadId={activeThread.id} | 3291 | activeThreadId={activeThread.id} |
| 3292 | {...(routeKind === "draft" && draftId ? { draftId } : {})} | ||
| 3292 | activeThreadTitle={activeThread.title} | 3293 | activeThreadTitle={activeThread.title} |
| 3293 | activeProjectName={activeProject?.name} | 3294 | activeProjectName={activeProject?.name} |
| 3294 | isGitRepo={isGitRepo} | 3295 | isGitRepo={isGitRepo} |
apps/web/src/components/GitActionsControl.browser.tsx+33| ... | @@ -426,4 +426,37 @@ describe("GitActionsControl thread-scoped progress toast", () => { | ... | @@ -426,4 +426,37 @@ describe("GitActionsControl thread-scoped progress toast", () => { |
| 426 | host.remove(); | 426 | host.remove(); |
| 427 | } | 427 | } |
| 428 | }); | 428 | }); |
| 429 | |||
| 430 | it("does not overwrite a selected base branch while a new worktree draft is being configured", async () => { | ||
| 431 | hasServerThreadRef.current = false; | ||
| 432 | activeDraftThreadRef.current = { | ||
| 433 | threadId: SHARED_THREAD_ID, | ||
| 434 | environmentId: ENVIRONMENT_A, | ||
| 435 | branch: "feature/base-branch", | ||
| 436 | worktreePath: null, | ||
| 437 | envMode: "worktree", | ||
| 438 | }; | ||
| 439 | |||
| 440 | const host = document.createElement("div"); | ||
| 441 | document.body.append(host); | ||
| 442 | const screen = await render( | ||
| 443 | <GitActionsControl | ||
| 444 | gitCwd={GIT_CWD} | ||
| 445 | activeThreadRef={scopeThreadRef(ENVIRONMENT_A, SHARED_THREAD_ID)} | ||
| 446 | />, | ||
| 447 | { | ||
| 448 | container: host, | ||
| 449 | }, | ||
| 450 | ); | ||
| 451 | |||
| 452 | try { | ||
| 453 | await Promise.resolve(); | ||
| 454 | |||
| 455 | expect(setDraftThreadContextSpy).not.toHaveBeenCalled(); | ||
| 456 | expect(setThreadBranchSpy).not.toHaveBeenCalled(); | ||
| 457 | } finally { | ||
| 458 | await screen.unmount(); | ||
| 459 | host.remove(); | ||
| 460 | } | ||
| 461 | }); | ||
| 429 | }); | 462 | }); |
apps/web/src/components/GitActionsControl.tsx+22-6| ... | @@ -49,7 +49,7 @@ import { | ... | @@ -49,7 +49,7 @@ import { |
| 49 | import { refreshGitStatus, useGitStatus } from "~/lib/gitStatusState"; | 49 | import { refreshGitStatus, useGitStatus } from "~/lib/gitStatusState"; |
| 50 | import { newCommandId, randomUUID } from "~/lib/utils"; | 50 | import { newCommandId, randomUUID } from "~/lib/utils"; |
| 51 | import { resolvePathLinkTarget } from "~/terminal-links"; | 51 | import { resolvePathLinkTarget } from "~/terminal-links"; |
| 52 | import { useComposerDraftStore } from "~/composerDraftStore"; | 52 | import { type DraftId, useComposerDraftStore } from "~/composerDraftStore"; |
| 53 | import { readEnvironmentApi } from "~/environmentApi"; | 53 | import { readEnvironmentApi } from "~/environmentApi"; |
| 54 | import { readLocalApi } from "~/localApi"; | 54 | import { readLocalApi } from "~/localApi"; |
| 55 | import { useStore } from "~/store"; | 55 | import { useStore } from "~/store"; |
| ... | @@ -58,6 +58,7 @@ import { createThreadSelectorByRef } from "~/storeSelectors"; | ... | @@ -58,6 +58,7 @@ import { createThreadSelectorByRef } from "~/storeSelectors"; |
| 58 | interface GitActionsControlProps { | 58 | interface GitActionsControlProps { |
| 59 | gitCwd: string | null; | 59 | gitCwd: string | null; |
| 60 | activeThreadRef: ScopedThreadRef | null; | 60 | activeThreadRef: ScopedThreadRef | null; |
| 61 | draftId?: DraftId; | ||
| 61 | } | 62 | } |
| 62 | 63 | ||
| 63 | interface PendingDefaultBranchAction { | 64 | interface PendingDefaultBranchAction { |
| ... | @@ -209,7 +210,11 @@ function GitQuickActionIcon({ quickAction }: { quickAction: GitQuickAction }) { | ... | @@ -209,7 +210,11 @@ function GitQuickActionIcon({ quickAction }: { quickAction: GitQuickAction }) { |
| 209 | return <InfoIcon className={iconClassName} />; | 210 | return <InfoIcon className={iconClassName} />; |
| 210 | } | 211 | } |
| 211 | 212 | ||
| 212 | export default function GitActionsControl({ gitCwd, activeThreadRef }: GitActionsControlProps) { | 213 | export default function GitActionsControl({ |
| 214 | gitCwd, | ||
| 215 | activeThreadRef, | ||
| 216 | draftId, | ||
| 217 | }: GitActionsControlProps) { | ||
| 213 | const activeEnvironmentId = activeThreadRef?.environmentId ?? null; | 218 | const activeEnvironmentId = activeThreadRef?.environmentId ?? null; |
| 214 | const threadToastData = useMemo( | 219 | const threadToastData = useMemo( |
| 215 | () => (activeThreadRef ? { threadRef: activeThreadRef } : undefined), | 220 | () => (activeThreadRef ? { threadRef: activeThreadRef } : undefined), |
| ... | @@ -221,7 +226,11 @@ export default function GitActionsControl({ gitCwd, activeThreadRef }: GitAction | ... | @@ -221,7 +226,11 @@ export default function GitActionsControl({ gitCwd, activeThreadRef }: GitAction |
| 221 | ); | 226 | ); |
| 222 | const activeServerThread = useStore(activeServerThreadSelector); | 227 | const activeServerThread = useStore(activeServerThreadSelector); |
| 223 | const activeDraftThread = useComposerDraftStore((store) => | 228 | const activeDraftThread = useComposerDraftStore((store) => |
| 224 | activeThreadRef ? store.getDraftThreadByRef(activeThreadRef) : null, | 229 | draftId |
| 230 | ? store.getDraftSession(draftId) | ||
| 231 | : activeThreadRef | ||
| 232 | ? store.getDraftThreadByRef(activeThreadRef) | ||
| 233 | : null, | ||
| 225 | ); | 234 | ); |
| 226 | const setDraftThreadContext = useComposerDraftStore((store) => store.setDraftThreadContext); | 235 | const setDraftThreadContext = useComposerDraftStore((store) => store.setDraftThreadContext); |
| 227 | const setThreadBranch = useStore((store) => store.setThreadBranch); | 236 | const setThreadBranch = useStore((store) => store.setThreadBranch); |
| ... | @@ -282,7 +291,7 @@ export default function GitActionsControl({ gitCwd, activeThreadRef }: GitAction | ... | @@ -282,7 +291,7 @@ export default function GitActionsControl({ gitCwd, activeThreadRef }: GitAction |
| 282 | return; | 291 | return; |
| 283 | } | 292 | } |
| 284 | 293 | ||
| 285 | setDraftThreadContext(activeThreadRef, { | 294 | setDraftThreadContext(draftId ?? activeThreadRef, { |
| 286 | branch, | 295 | branch, |
| 287 | worktreePath: activeDraftThread.worktreePath, | 296 | worktreePath: activeDraftThread.worktreePath, |
| 288 | }); | 297 | }); |
| ... | @@ -291,6 +300,7 @@ export default function GitActionsControl({ gitCwd, activeThreadRef }: GitAction | ... | @@ -291,6 +300,7 @@ export default function GitActionsControl({ gitCwd, activeThreadRef }: GitAction |
| 291 | activeDraftThread, | 300 | activeDraftThread, |
| 292 | activeServerThread, | 301 | activeServerThread, |
| 293 | activeThreadRef, | 302 | activeThreadRef, |
| 303 | draftId, | ||
| 294 | setDraftThreadContext, | 304 | setDraftThreadContext, |
| 295 | setThreadBranch, | 305 | setThreadBranch, |
| 296 | ], | 306 | ], |
| ... | @@ -344,14 +354,18 @@ export default function GitActionsControl({ gitCwd, activeThreadRef }: GitAction | ... | @@ -344,14 +354,18 @@ export default function GitActionsControl({ gitCwd, activeThreadRef }: GitAction |
| 344 | const isPullRunning = | 354 | const isPullRunning = |
| 345 | useIsMutating({ mutationKey: gitMutationKeys.pull(activeEnvironmentId, gitCwd) }) > 0; | 355 | useIsMutating({ mutationKey: gitMutationKeys.pull(activeEnvironmentId, gitCwd) }) > 0; |
| 346 | const isGitActionRunning = isRunStackedActionRunning || isPullRunning; | 356 | const isGitActionRunning = isRunStackedActionRunning || isPullRunning; |
| 357 | const isSelectingWorktreeBase = | ||
| 358 | !activeServerThread && | ||
| 359 | activeDraftThread?.envMode === "worktree" && | ||
| 360 | activeDraftThread.worktreePath === null; | ||
| 347 | 361 | ||
| 348 | useEffect(() => { | 362 | useEffect(() => { |
| 349 | if (isGitActionRunning) { | 363 | if (isGitActionRunning || isSelectingWorktreeBase) { |
| 350 | return; | 364 | return; |
| 351 | } | 365 | } |
| 352 | 366 | ||
| 353 | const branchUpdate = resolveLiveThreadBranchUpdate({ | 367 | const branchUpdate = resolveLiveThreadBranchUpdate({ |
| 354 | threadBranch: activeServerThread?.branch ?? null, | 368 | threadBranch: activeServerThread?.branch ?? activeDraftThread?.branch ?? null, |
| 355 | gitStatus: gitStatusForActions, | 369 | gitStatus: gitStatusForActions, |
| 356 | }); | 370 | }); |
| 357 | if (!branchUpdate) { | 371 | if (!branchUpdate) { |
| ... | @@ -361,8 +375,10 @@ export default function GitActionsControl({ gitCwd, activeThreadRef }: GitAction | ... | @@ -361,8 +375,10 @@ export default function GitActionsControl({ gitCwd, activeThreadRef }: GitAction |
| 361 | persistThreadBranchSync(branchUpdate.branch); | 375 | persistThreadBranchSync(branchUpdate.branch); |
| 362 | }, [ | 376 | }, [ |
| 363 | activeServerThread?.branch, | 377 | activeServerThread?.branch, |
| 378 | activeDraftThread?.branch, | ||
| 364 | gitStatusForActions, | 379 | gitStatusForActions, |
| 365 | isGitActionRunning, | 380 | isGitActionRunning, |
| 381 | isSelectingWorktreeBase, | ||
| 366 | persistThreadBranchSync, | 382 | persistThreadBranchSync, |
| 367 | ]); | 383 | ]); |
| 368 | 384 |
apps/web/src/components/chat/ChatHeader.tsx+4| ... | @@ -8,6 +8,7 @@ import { | ... | @@ -8,6 +8,7 @@ import { |
| 8 | import { scopeThreadRef } from "@t3tools/client-runtime"; | 8 | import { scopeThreadRef } from "@t3tools/client-runtime"; |
| 9 | import { memo } from "react"; | 9 | import { memo } from "react"; |
| 10 | import GitActionsControl from "../GitActionsControl"; | 10 | import GitActionsControl from "../GitActionsControl"; |
| 11 | import { type DraftId } from "~/composerDraftStore"; | ||
| 11 | import { DiffIcon, TerminalSquareIcon } from "lucide-react"; | 12 | import { DiffIcon, TerminalSquareIcon } from "lucide-react"; |
| 12 | import { Badge } from "../ui/badge"; | 13 | import { Badge } from "../ui/badge"; |
| 13 | import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; | 14 | import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; |
| ... | @@ -19,6 +20,7 @@ import { OpenInPicker } from "./OpenInPicker"; | ... | @@ -19,6 +20,7 @@ import { OpenInPicker } from "./OpenInPicker"; |
| 19 | interface ChatHeaderProps { | 20 | interface ChatHeaderProps { |
| 20 | activeThreadEnvironmentId: EnvironmentId; | 21 | activeThreadEnvironmentId: EnvironmentId; |
| 21 | activeThreadId: ThreadId; | 22 | activeThreadId: ThreadId; |
| 23 | draftId?: DraftId; | ||
| 22 | activeThreadTitle: string; | 24 | activeThreadTitle: string; |
| 23 | activeProjectName: string | undefined; | 25 | activeProjectName: string | undefined; |
| 24 | isGitRepo: boolean; | 26 | isGitRepo: boolean; |
| ... | @@ -44,6 +46,7 @@ interface ChatHeaderProps { | ... | @@ -44,6 +46,7 @@ interface ChatHeaderProps { |
| 44 | export const ChatHeader = memo(function ChatHeader({ | 46 | export const ChatHeader = memo(function ChatHeader({ |
| 45 | activeThreadEnvironmentId, | 47 | activeThreadEnvironmentId, |
| 46 | activeThreadId, | 48 | activeThreadId, |
| 49 | draftId, | ||
| 47 | activeThreadTitle, | 50 | activeThreadTitle, |
| 48 | activeProjectName, | 51 | activeProjectName, |
| 49 | isGitRepo, | 52 | isGitRepo, |
| ... | @@ -109,6 +112,7 @@ export const ChatHeader = memo(function ChatHeader({ | ... | @@ -109,6 +112,7 @@ export const ChatHeader = memo(function ChatHeader({ |
| 109 | <GitActionsControl | 112 | <GitActionsControl |
| 110 | gitCwd={gitCwd} | 113 | gitCwd={gitCwd} |
| 111 | activeThreadRef={scopeThreadRef(activeThreadEnvironmentId, activeThreadId)} | 114 | activeThreadRef={scopeThreadRef(activeThreadEnvironmentId, activeThreadId)} |
| 115 | {...(draftId ? { draftId } : {})} | ||
| 112 | /> | 116 | /> |
| 113 | )} | 117 | )} |
| 114 | <Tooltip> | 118 | <Tooltip> |