Skip to content

Commit 0ddb010

Browse files
committed
feat: add directory source indicators and cloud mode gating
UI Enhancements: - Show badge indicating directory source (Custom/Shared/Default) - Add tooltips explaining each directory source type - Disable Cloud mode for tasks without repository_config - Show helpful tooltip when Cloud mode is unavailable Technical changes: - Added directorySource computation in useTaskData hook - Updated TaskMetadata to display source badge next to "Working directory" - Updated TaskActions to gate Cloud mode based on hasRepositoryConfig - Improved UX for tasks without repository configuration This helps users understand where their directory mappings come from and why certain features may be unavailable.
1 parent e93427d commit 0ddb010

File tree

4 files changed

+92
-6
lines changed

4 files changed

+92
-6
lines changed

src/renderer/features/task-detail/components/TaskActions.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ interface TaskActionsProps {
77
isCloningRepo: boolean;
88
cloneProgress: { message: string; percent: number } | null;
99
runMode: "local" | "cloud";
10+
hasRepositoryConfig: boolean;
1011
onRunTask: () => void;
1112
onCancel: () => void;
1213
onRunModeChange: (mode: "local" | "cloud") => void;
@@ -17,6 +18,7 @@ export const TaskActions: React.FC<TaskActionsProps> = ({
1718
isCloningRepo,
1819
cloneProgress,
1920
runMode,
21+
hasRepositoryConfig,
2022
onRunTask,
2123
onCancel,
2224
onRunModeChange,
@@ -58,12 +60,18 @@ export const TaskActions: React.FC<TaskActionsProps> = ({
5860
>
5961
<span className="truncate">{getRunButtonLabel()}</span>
6062
</Button>
61-
<Tooltip content="Toggle between Local or Cloud Agent">
63+
<Tooltip
64+
content={
65+
!hasRepositoryConfig
66+
? "Cloud mode requires a connected repository"
67+
: "Toggle between Local or Cloud Agent"
68+
}
69+
>
6270
<IconButton
6371
size="2"
6472
variant="classic"
6573
color={runMode === "cloud" ? "blue" : "gray"}
66-
disabled={isRunning || isCloningRepo}
74+
disabled={isRunning || isCloningRepo || !hasRepositoryConfig}
6775
onClick={() =>
6876
onRunModeChange(runMode === "local" ? "cloud" : "local")
6977
}

src/renderer/features/task-detail/components/TaskDetailPanel.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ export function TaskDetailPanel({ taskId, task }: TaskDetailPanelProps) {
9191
: undefined
9292
}
9393
derivedPath={taskData.derivedPath}
94+
directorySource={taskData.directorySource}
9495
defaultWorkspace={taskData.defaultWorkspace}
9596
/>
9697
</Flex>
@@ -101,6 +102,7 @@ export function TaskDetailPanel({ taskId, task }: TaskDetailPanelProps) {
101102
isCloningRepo={repository.isCloning}
102103
cloneProgress={taskData.cloneProgress}
103104
runMode={execution.state.runMode}
105+
hasRepositoryConfig={!!taskData.task.repository_config}
104106
onRunTask={execution.actions.run}
105107
onCancel={execution.actions.cancel}
106108
onRunModeChange={execution.actions.onRunModeChange}

src/renderer/features/task-detail/components/TaskMetadata.tsx

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,34 @@
1-
import { Button, Code, DataList, Link, Text, Tooltip } from "@radix-ui/themes";
1+
import { FolderPicker } from "@features/folder-picker/components/FolderPicker";
2+
import { useTaskExecutionStore } from "@features/task-detail/stores/taskExecutionStore";
3+
import {
4+
Badge,
5+
Button,
6+
Code,
7+
DataList,
8+
Flex,
9+
Link,
10+
Text,
11+
Tooltip,
12+
} from "@radix-ui/themes";
213
import type { Task } from "@shared/types";
314
import { format, formatDistanceToNow } from "date-fns";
415
import type React from "react";
5-
import { FolderPicker } from "@features/folder-picker/components/FolderPicker";
6-
import { useTaskExecutionStore } from "@features/task-detail/stores/taskExecutionStore";
16+
17+
type DirectorySource = "task" | "repo" | "workspace" | null;
718

819
interface TaskMetadataProps {
920
task: Task;
1021
progress?: { status: string };
1122
derivedPath: string | null;
23+
directorySource: DirectorySource;
1224
defaultWorkspace: string | null;
1325
}
1426

1527
export const TaskMetadata: React.FC<TaskMetadataProps> = ({
1628
task,
1729
progress,
1830
derivedPath,
31+
directorySource,
1932
defaultWorkspace,
2033
}) => {
2134
const { setRepoPath, revalidateRepo } = useTaskExecutionStore();
@@ -25,6 +38,38 @@ export const TaskMetadata: React.FC<TaskMetadataProps> = ({
2538
await revalidateRepo(task.id);
2639
};
2740

41+
const getDirectorySourceBadge = () => {
42+
if (!directorySource) return null;
43+
44+
const badgeConfig = {
45+
task: {
46+
label: "Custom",
47+
color: "blue" as const,
48+
tooltip: "Directory set specifically for this task",
49+
},
50+
repo: {
51+
label: "Shared",
52+
color: "green" as const,
53+
tooltip: "Directory shared with other tasks using this repository",
54+
},
55+
workspace: {
56+
label: "Default",
57+
color: "gray" as const,
58+
tooltip: "Directory derived from workspace settings",
59+
},
60+
};
61+
62+
const config = badgeConfig[directorySource];
63+
64+
return (
65+
<Tooltip content={config.tooltip}>
66+
<Badge size="1" color={config.color} variant="soft">
67+
{config.label}
68+
</Badge>
69+
</Tooltip>
70+
);
71+
};
72+
2873
return (
2974
<>
3075
<DataList.Root>
@@ -71,7 +116,12 @@ export const TaskMetadata: React.FC<TaskMetadataProps> = ({
71116
</DataList.Item>
72117

73118
<DataList.Item>
74-
<DataList.Label>Working directory</DataList.Label>
119+
<DataList.Label>
120+
<Flex align="center" gap="2">
121+
<Text>Working directory</Text>
122+
{getDirectorySourceBadge()}
123+
</Flex>
124+
</DataList.Label>
75125
<DataList.Value>
76126
<FolderPicker
77127
value={derivedPath || ""}

src/renderer/features/task-detail/hooks/useTaskData.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useTaskExecutionStore } from "@features/task-detail/stores/taskExecutio
33
import { useTasks } from "@features/tasks/hooks/useTasks";
44
import type { Task } from "@shared/types";
55
import { cloneStore } from "@stores/cloneStore";
6+
import { useTaskDirectoryStore } from "@stores/taskDirectoryStore";
67
import { expandTildePath } from "@utils/path";
78
import { useEffect, useMemo } from "react";
89

@@ -70,11 +71,36 @@ export function useTaskData({ taskId, initialTask }: UseTaskDataParams) {
7071
(a, b) => a?.message === b?.message && a?.percent === b?.percent,
7172
);
7273

74+
// Determine directory source for UI indicators
75+
const directorySource = useMemo(() => {
76+
if (!taskState.repoPath) return null;
77+
78+
const { taskDirectories, repoDirectories } =
79+
useTaskDirectoryStore.getState();
80+
81+
// Check if it's a task-specific mapping
82+
if (taskDirectories[taskId]) {
83+
return "task" as const;
84+
}
85+
86+
// Check if it's from a repo mapping
87+
if (task.repository_config) {
88+
const repoKey = `${task.repository_config.organization}/${task.repository_config.repository}`;
89+
if (repoDirectories[repoKey]) {
90+
return "repo" as const;
91+
}
92+
}
93+
94+
// Otherwise it was derived from workspace
95+
return "workspace" as const;
96+
}, [taskState.repoPath, taskId, task.repository_config]);
97+
7398
return {
7499
task,
75100
repoPath: taskState.repoPath,
76101
repoExists: taskState.repoExists,
77102
derivedPath,
103+
directorySource,
78104
isCloning,
79105
cloneProgress,
80106
defaultWorkspace,

0 commit comments

Comments
 (0)