Skip to content

Commit fb04ea4

Browse files
committed
Review fixes
1 parent bb93a5c commit fb04ea4

12 files changed

Lines changed: 156 additions & 152 deletions

File tree

apps/code/src/renderer/features/command-center/components/CommandCenterGrid.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,19 @@ function useTaskDragActive() {
2323
};
2424
const onDragEnd = () => setActive(false);
2525
const onDrop = () => setActive(false);
26+
const onDragLeave = (e: DragEvent) => {
27+
if (!e.relatedTarget) setActive(false);
28+
};
2629

2730
document.addEventListener("dragstart", onDragStart);
2831
document.addEventListener("dragend", onDragEnd);
2932
document.addEventListener("drop", onDrop);
33+
document.addEventListener("dragleave", onDragLeave);
3034
return () => {
3135
document.removeEventListener("dragstart", onDragStart);
3236
document.removeEventListener("dragend", onDragEnd);
3337
document.removeEventListener("drop", onDrop);
38+
document.removeEventListener("dragleave", onDragLeave);
3439
};
3540
}, []);
3641

apps/code/src/renderer/features/command-center/components/CommandCenterSessionView.tsx

Lines changed: 19 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@ import { useDraftStore } from "@features/message-editor/stores/draftStore";
22
import { SessionView } from "@features/sessions/components/SessionView";
33
import { useSessionCallbacks } from "@features/sessions/hooks/useSessionCallbacks";
44
import { useSessionConnection } from "@features/sessions/hooks/useSessionConnection";
5-
import { useSessionForTask } from "@features/sessions/stores/sessionStore";
6-
import { useCwd } from "@features/sidebar/hooks/useCwd";
7-
import { useWorkspace } from "@features/workspace/hooks/useWorkspace";
5+
import { useSessionViewState } from "@features/sessions/hooks/useSessionViewState";
86
import { Flex } from "@radix-ui/themes";
97
import type { Task } from "@shared/types";
10-
import { useEffect, useMemo } from "react";
8+
import { useEffect } from "react";
119

1210
interface CommandCenterSessionViewProps {
1311
taskId: string;
@@ -18,14 +16,8 @@ export function CommandCenterSessionView({
1816
taskId,
1917
task,
2018
}: CommandCenterSessionViewProps) {
21-
const session = useSessionForTask(taskId);
22-
const repoPath = useCwd(taskId);
23-
const workspace = useWorkspace(taskId);
2419
const { requestFocus } = useDraftStore((s) => s.actions);
2520

26-
const isCloud =
27-
workspace?.mode === "cloud" || task.latest_run?.environment === "cloud";
28-
2921
useSessionConnection({ taskId, task });
3022

3123
const {
@@ -36,50 +28,20 @@ export function CommandCenterSessionView({
3628
handleBashCommand,
3729
} = useSessionCallbacks({ taskId, task });
3830

39-
const isCloudRunNotTerminal =
40-
isCloud &&
41-
(!session?.cloudStatus ||
42-
session.cloudStatus === "started" ||
43-
session.cloudStatus === "in_progress");
44-
45-
const isRunning = isCloud
46-
? isCloudRunNotTerminal
47-
: session?.status === "connected";
48-
const hasError = isCloud ? false : session?.status === "error";
49-
50-
const events = session?.events ?? [];
51-
const isPromptPending = session?.isPromptPending ?? false;
52-
const promptStartedAt = session?.promptStartedAt;
53-
54-
const isNewSessionWithInitialPrompt =
55-
!task.latest_run?.id && !!task.description;
56-
const isResumingExistingSession = !!task.latest_run?.id;
57-
const isInitializing = isCloud
58-
? !session || (events.length === 0 && isCloudRunNotTerminal)
59-
: !session ||
60-
(session.status === "connecting" && events.length === 0) ||
61-
(session.status === "connected" &&
62-
events.length === 0 &&
63-
(isPromptPending ||
64-
isNewSessionWithInitialPrompt ||
65-
isResumingExistingSession));
66-
67-
const cloudBranch = isCloud
68-
? (workspace?.baseBranch ?? task.latest_run?.branch ?? null)
69-
: null;
70-
71-
const readOnlyMessage = useMemo(() => {
72-
if (!isCloud) return undefined;
73-
const status = session?.cloudStatus;
74-
if (
75-
status === "completed" ||
76-
status === "failed" ||
77-
status === "cancelled"
78-
) {
79-
return "This cloud run has finished";
80-
}
81-
return undefined;
82-
}, [isCloud, session?.cloudStatus]);
31+
const {
32+
repoPath,
33+
isCloud,
34+
isRunning,
35+
hasError,
36+
events,
37+
isPromptPending,
38+
promptStartedAt,
39+
isInitializing,
40+
cloudBranch,
41+
readOnlyMessage,
42+
errorTitle,
43+
errorMessage,
44+
} = useSessionViewState(taskId, task);
8345

8446
useEffect(() => {
8547
requestFocus(taskId);
@@ -90,7 +52,7 @@ export function CommandCenterSessionView({
9052
<SessionView
9153
events={events}
9254
taskId={taskId}
93-
isRunning={!!isRunning}
55+
isRunning={isRunning}
9456
isPromptPending={isCloud ? null : isPromptPending}
9557
promptStartedAt={isCloud ? undefined : promptStartedAt}
9658
onSendPrompt={handleSendPrompt}
@@ -99,8 +61,8 @@ export function CommandCenterSessionView({
9961
repoPath={repoPath}
10062
cloudBranch={cloudBranch}
10163
hasError={hasError}
102-
errorTitle={session?.errorTitle}
103-
errorMessage={session?.errorMessage}
64+
errorTitle={errorTitle}
65+
errorMessage={errorMessage}
10466
onRetry={isCloud ? undefined : handleRetry}
10567
onNewSession={isCloud ? undefined : handleNewSession}
10668
isInitializing={isInitializing}

apps/code/src/renderer/features/command-center/stores/commandCenterStore.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export const useCommandCenterStore = create<CommandCenterStore>()(
6969

7070
assignTask: (cellIndex, taskId) =>
7171
set((state) => {
72+
if (cellIndex < 0 || cellIndex >= state.cells.length) return state;
7273
const cells = [...state.cells];
7374
const existingIndex = cells.indexOf(taskId);
7475
if (existingIndex !== -1) {

apps/code/src/renderer/features/sessions/hooks/useSessionCallbacks.ts

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type { Task } from "@shared/types";
77
import { useNavigationStore } from "@stores/navigationStore";
88
import { logger } from "@utils/logger";
99
import { toast } from "@utils/toast";
10-
import { useCallback } from "react";
10+
import { useCallback, useRef } from "react";
1111
import { getSessionService } from "../service/service";
1212
import { sessionStoreSetters, useSessionForTask } from "../stores/sessionStore";
1313

@@ -27,18 +27,21 @@ export function useSessionCallbacks({
2727
const { markActivity, markAsViewed } = useTaskViewed();
2828
const { requestFocus, setPendingContent } = useDraftStore((s) => s.actions);
2929

30-
const events = session?.events ?? [];
30+
const sessionRef = useRef(session);
31+
sessionRef.current = session;
3132

3233
const handleSendPrompt = useCallback(
3334
async (text: string) => {
35+
const currentSession = sessionRef.current;
36+
const currentEvents = currentSession?.events ?? [];
3437
const handled = await tryExecuteCodeCommand(text, {
3538
taskId,
3639
repoPath,
37-
session: session
40+
session: currentSession
3841
? {
39-
taskRunId: session.taskRunId,
40-
logUrl: session.logUrl,
41-
events,
42+
taskRunId: currentSession.taskRunId,
43+
logUrl: currentSession.logUrl,
44+
events: currentEvents,
4245
}
4346
: null,
4447
taskRun: task.latest_run ?? null,
@@ -64,15 +67,7 @@ export function useSessionCallbacks({
6467
log.error("Failed to send prompt", error);
6568
}
6669
},
67-
[
68-
taskId,
69-
repoPath,
70-
markActivity,
71-
markAsViewed,
72-
events,
73-
session,
74-
task.latest_run,
75-
],
70+
[taskId, repoPath, markActivity, markAsViewed, task.latest_run],
7671
);
7772

7873
const handleCancelPrompt = useCallback(async () => {

apps/code/src/renderer/features/sessions/hooks/useSessionConnection.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import { trpcClient } from "@renderer/trpc/client";
66
import type { Task } from "@shared/types";
77
import { useQueryClient } from "@tanstack/react-query";
88
import { logger } from "@utils/logger";
9-
import { useEffect, useRef } from "react";
9+
import { useEffect } from "react";
1010
import { getSessionService } from "../service/service";
1111
import { useSessionForTask } from "../stores/sessionStore";
1212
import { useChatTitleGenerator } from "./useChatTitleGenerator";
1313

1414
const log = logger.scope("session-connection");
1515

16+
const connectingTasks = new Set<string>();
17+
1618
interface UseSessionConnectionOptions {
1719
taskId: string;
1820
task: Task;
@@ -34,8 +36,6 @@ export function useSessionConnection({
3436

3537
useChatTitleGenerator(taskId);
3638

37-
const isConnecting = useRef(false);
38-
3939
useEffect(() => {
4040
const taskRunId = session?.taskRunId;
4141
if (!taskRunId) return;
@@ -73,7 +73,7 @@ export function useSessionConnection({
7373

7474
useEffect(() => {
7575
if (!repoPath) return;
76-
if (isConnecting.current) return;
76+
if (connectingTasks.has(taskId)) return;
7777
if (!isOnline) return;
7878
if (isCloud) return;
7979

@@ -85,7 +85,7 @@ export function useSessionConnection({
8585
return;
8686
}
8787

88-
isConnecting.current = true;
88+
connectingTasks.add(taskId);
8989

9090
const isNewSession = !task.latest_run?.id;
9191
const hasInitialPrompt = isNewSession && task.description;
@@ -109,7 +109,7 @@ export function useSessionConnection({
109109
: undefined,
110110
})
111111
.finally(() => {
112-
isConnecting.current = false;
112+
connectingTasks.delete(taskId);
113113
});
114-
}, [task, repoPath, session, markActivity, isOnline, isCloud]);
114+
}, [task, taskId, repoPath, session, markActivity, isOnline, isCloud]);
115115
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { useCwd } from "@features/sidebar/hooks/useCwd";
2+
import { useWorkspace } from "@features/workspace/hooks/useWorkspace";
3+
import type { Task } from "@shared/types";
4+
import { useMemo } from "react";
5+
import { useSessionForTask } from "../stores/sessionStore";
6+
7+
export function useSessionViewState(taskId: string, task: Task) {
8+
const session = useSessionForTask(taskId);
9+
const repoPath = useCwd(taskId);
10+
const workspace = useWorkspace(taskId);
11+
12+
const isCloud =
13+
workspace?.mode === "cloud" || task.latest_run?.environment === "cloud";
14+
15+
const cloudStatus = session?.cloudStatus ?? null;
16+
const isCloudRunNotTerminal =
17+
isCloud &&
18+
(!cloudStatus ||
19+
cloudStatus === "started" ||
20+
cloudStatus === "in_progress");
21+
const isCloudRunTerminal = isCloud && !isCloudRunNotTerminal;
22+
23+
const isRunning = isCloud
24+
? isCloudRunNotTerminal
25+
: session?.status === "connected";
26+
const hasError = isCloud ? false : session?.status === "error";
27+
28+
const events = session?.events ?? [];
29+
const isPromptPending = session?.isPromptPending ?? false;
30+
const promptStartedAt = session?.promptStartedAt;
31+
32+
const isNewSessionWithInitialPrompt =
33+
!task.latest_run?.id && !!task.description;
34+
const isResumingExistingSession = !!task.latest_run?.id;
35+
const isInitializing = isCloud
36+
? !session || (events.length === 0 && isCloudRunNotTerminal)
37+
: !session ||
38+
(session.status === "connecting" && events.length === 0) ||
39+
(session.status === "connected" &&
40+
events.length === 0 &&
41+
(isPromptPending ||
42+
isNewSessionWithInitialPrompt ||
43+
isResumingExistingSession));
44+
45+
const cloudBranch = isCloud
46+
? (workspace?.baseBranch ?? task.latest_run?.branch ?? null)
47+
: null;
48+
49+
const readOnlyMessage = useMemo(() => {
50+
if (!isCloud) return undefined;
51+
if (isCloudRunTerminal) return "This cloud run has finished";
52+
return undefined;
53+
}, [isCloud, isCloudRunTerminal]);
54+
55+
return {
56+
session,
57+
repoPath,
58+
isCloud,
59+
isCloudRunNotTerminal,
60+
isCloudRunTerminal,
61+
cloudStatus,
62+
isRunning: !!isRunning,
63+
hasError,
64+
events,
65+
isPromptPending,
66+
promptStartedAt,
67+
isInitializing,
68+
cloudBranch,
69+
readOnlyMessage,
70+
errorTitle: isCloud ? undefined : session?.errorTitle,
71+
errorMessage: isCloud ? undefined : session?.errorMessage,
72+
};
73+
}

apps/code/src/renderer/features/sidebar/components/DraggableFolder.tsx

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
import { useSortable } from "@dnd-kit/react/sortable";
2-
import {
3-
cloneElement,
4-
isValidElement,
5-
type ReactElement,
6-
type ReactNode,
7-
} from "react";
2+
import { cloneElement, isValidElement, type ReactNode } from "react";
83

94
interface DraggableFolderProps {
105
id: string;
@@ -31,15 +26,12 @@ export function DraggableFolder({ id, index, children }: DraggableFolderProps) {
3126
cursor: isDragging ? "grabbing" : undefined,
3227
}}
3328
>
34-
{isValidElement(children)
35-
? cloneElement(
36-
children as ReactElement<{
37-
dragHandleRef?: (el: Element | null) => void;
38-
}>,
39-
{
40-
dragHandleRef: handleRef,
41-
},
42-
)
29+
{isValidElement<{ dragHandleRef?: React.RefCallback<HTMLButtonElement> }>(
30+
children,
31+
)
32+
? cloneElement(children, {
33+
dragHandleRef: handleRef as React.RefCallback<HTMLButtonElement>,
34+
})
4335
: children}
4436
</div>
4537
);

apps/code/src/renderer/features/sidebar/components/SidebarMenu.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,13 @@ function SidebarMenuComponent() {
188188
/>
189189
</Box>
190190

191-
<CommandCenterItem
192-
isActive={sidebarData.isCommandCenterActive}
193-
onClick={handleCommandCenterClick}
194-
activeCount={commandCenterActiveCount}
195-
/>
191+
<Box mb="2">
192+
<CommandCenterItem
193+
isActive={sidebarData.isCommandCenterActive}
194+
onClick={handleCommandCenterClick}
195+
activeCount={commandCenterActiveCount}
196+
/>
197+
</Box>
196198

197199
{sidebarData.isLoading ? (
198200
<SidebarItem

apps/code/src/renderer/features/sidebar/components/SidebarSection.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ interface SidebarSectionProps {
1515
tooltipContent?: string;
1616
onNewTask?: () => void;
1717
newTaskTooltip?: string;
18-
dragHandleRef?: (el: Element | null) => void;
18+
dragHandleRef?: React.RefCallback<HTMLButtonElement>;
1919
}
2020

2121
export function SidebarSection({
@@ -37,7 +37,7 @@ export function SidebarSection({
3737
<Collapsible.Root open={isExpanded} onOpenChange={onToggle}>
3838
<Collapsible.Trigger asChild>
3939
<button
40-
ref={dragHandleRef as React.Ref<HTMLButtonElement>}
40+
ref={dragHandleRef}
4141
type="button"
4242
className="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-2 py-1 text-left font-mono text-[12px] text-gray-11 transition-colors hover:bg-gray-3"
4343
style={{

0 commit comments

Comments
 (0)