Skip to content

Commit 4133fd3

Browse files
adboiojonathanlab
andauthored
feat(code): allow new task creation in command center (#1264)
Co-authored-by: JonathanLab <jonathanmieloo@gmail.com>
1 parent bb1fe27 commit 4133fd3

File tree

4 files changed

+92
-28
lines changed

4 files changed

+92
-28
lines changed

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

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { TaskInput } from "@features/task-detail/components/TaskInput";
12
import { ArrowsOut, Plus, X } from "@phosphor-icons/react";
23
import { Flex, Text } from "@radix-ui/themes";
34
import type { Task } from "@shared/types";
@@ -16,6 +17,48 @@ interface CommandCenterPanelProps {
1617

1718
function EmptyCell({ cellIndex }: { cellIndex: number }) {
1819
const [selectorOpen, setSelectorOpen] = useState(false);
20+
const [isCreating, setIsCreating] = useState(false);
21+
const assignTask = useCommandCenterStore((s) => s.assignTask);
22+
23+
const handleTaskCreated = useCallback(
24+
(task: Task) => {
25+
assignTask(cellIndex, task.id);
26+
},
27+
[assignTask, cellIndex],
28+
);
29+
30+
if (isCreating) {
31+
return (
32+
<Flex direction="column" height="100%">
33+
<Flex
34+
align="center"
35+
justify="between"
36+
px="2"
37+
py="1"
38+
className="shrink-0 border-gray-6 border-b"
39+
>
40+
<Text
41+
size="1"
42+
weight="medium"
43+
className="font-mono text-[11px] text-gray-11"
44+
>
45+
New task
46+
</Text>
47+
<button
48+
type="button"
49+
onClick={() => setIsCreating(false)}
50+
className="flex h-5 w-5 items-center justify-center rounded text-gray-10 transition-colors hover:bg-gray-4 hover:text-gray-12"
51+
title="Cancel"
52+
>
53+
<X size={12} />
54+
</button>
55+
</Flex>
56+
<Flex direction="column" className="min-h-0 flex-1">
57+
<TaskInput onTaskCreated={handleTaskCreated} />
58+
</Flex>
59+
</Flex>
60+
);
61+
}
1962

2063
return (
2164
<Flex align="center" justify="center" height="100%">
@@ -24,6 +67,7 @@ function EmptyCell({ cellIndex }: { cellIndex: number }) {
2467
cellIndex={cellIndex}
2568
open={selectorOpen}
2669
onOpenChange={setSelectorOpen}
70+
onNewTask={() => setIsCreating(true)}
2771
>
2872
<button
2973
type="button"

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

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ interface TaskSelectorProps {
99
cellIndex: number;
1010
open: boolean;
1111
onOpenChange: (open: boolean) => void;
12+
onNewTask?: () => void;
1213
children: ReactNode;
1314
}
1415

1516
export function TaskSelector({
1617
cellIndex,
1718
open,
1819
onOpenChange,
20+
onNewTask,
1921
children,
2022
}: TaskSelectorProps) {
2123
const availableTasks = useAvailableTasks();
@@ -32,8 +34,12 @@ export function TaskSelector({
3234

3335
const handleNewTask = useCallback(() => {
3436
onOpenChange(false);
35-
navigateToTaskInput();
36-
}, [onOpenChange, navigateToTaskInput]);
37+
if (onNewTask) {
38+
onNewTask();
39+
} else {
40+
navigateToTaskInput();
41+
}
42+
}, [onOpenChange, onNewTask, navigateToTaskInput]);
3743

3844
return (
3945
<Popover.Root open={open} onOpenChange={onOpenChange}>
@@ -44,29 +50,31 @@ export function TaskSelector({
4450
sideOffset={4}
4551
style={{ padding: 4, minWidth: 240, maxHeight: 300 }}
4652
>
47-
<div className="overflow-y-auto" style={{ maxHeight: 280 }}>
48-
<button
49-
type="button"
50-
onClick={handleNewTask}
51-
className="flex w-full items-center gap-1.5 rounded-sm px-2 py-1.5 text-left text-[12px] text-gray-12 transition-colors hover:bg-gray-3"
52-
>
53-
<Plus size={12} className="shrink-0" />
54-
<span>New task</span>
55-
</button>
56-
{availableTasks.length > 0 && (
57-
<>
58-
<Separator size="4" my="1" />
59-
{availableTasks.map((task) => (
60-
<button
61-
key={task.id}
62-
type="button"
63-
onClick={() => handleSelect(task.id)}
64-
className="flex w-full items-center rounded-sm px-2 py-1.5 text-left text-[12px] text-gray-12 transition-colors hover:bg-gray-3"
65-
>
66-
<span className="min-w-0 flex-1 truncate">{task.title}</span>
67-
</button>
68-
))}
69-
</>
53+
<button
54+
type="button"
55+
onClick={handleNewTask}
56+
className="flex w-full items-center gap-1.5 rounded-sm px-2 py-1.5 text-left text-[12px] text-gray-12 transition-colors hover:bg-gray-3"
57+
>
58+
<Plus size={12} />
59+
New task
60+
</button>
61+
<Separator size="4" className="my-1" />
62+
<div className="overflow-y-auto" style={{ maxHeight: 240 }}>
63+
{availableTasks.length === 0 ? (
64+
<div className="px-2 py-3 text-center font-mono text-[11px] text-gray-10">
65+
No available tasks
66+
</div>
67+
) : (
68+
availableTasks.map((task) => (
69+
<button
70+
key={task.id}
71+
type="button"
72+
onClick={() => handleSelect(task.id)}
73+
className="flex w-full items-center rounded-sm px-2 py-1.5 text-left text-[12px] text-gray-12 transition-colors hover:bg-gray-3"
74+
>
75+
<span className="min-w-0 flex-1 truncate">{task.title}</span>
76+
</button>
77+
))
7078
)}
7179
</div>
7280
</Popover.Content>

apps/code/src/renderer/features/task-detail/components/TaskInput.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ import { type WorkspaceMode, WorkspaceModeSelect } from "./WorkspaceModeSelect";
3333

3434
const DOT_FILL = "var(--gray-6)";
3535

36-
export function TaskInput() {
36+
interface TaskInputProps {
37+
onTaskCreated?: (task: import("@shared/types").Task) => void;
38+
}
39+
40+
export function TaskInput({ onTaskCreated }: TaskInputProps = {}) {
3741
const { cloudRegion } = useAuthStore();
3842
const trpcReact = useTRPC();
3943
const { view } = useNavigationStore();
@@ -172,6 +176,7 @@ export function TaskInput() {
172176
executionMode: currentExecutionMode,
173177
model: currentModel,
174178
reasoningLevel: currentReasoningLevel,
179+
onTaskCreated,
175180
environmentId: selectedEnvironment,
176181
sandboxEnvironmentId:
177182
effectiveWorkspaceMode === "cloud" && selectedCloudEnvId

apps/code/src/renderer/features/task-detail/hooks/useTaskCreation.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import type { WorkspaceMode } from "@main/services/workspace/schemas";
1212
import { get } from "@renderer/di/container";
1313
import { RENDERER_TOKENS } from "@renderer/di/tokens";
1414
import { toast } from "@renderer/utils/toast";
15-
import type { ExecutionMode } from "@shared/types";
15+
import type { ExecutionMode, Task } from "@shared/types";
1616
import { useNavigationStore } from "@stores/navigationStore";
1717
import { logger } from "@utils/logger";
1818
import { useCallback, useState } from "react";
@@ -34,6 +34,7 @@ interface UseTaskCreationOptions {
3434
reasoningLevel?: string;
3535
environmentId?: string | null;
3636
sandboxEnvironmentId?: string;
37+
onTaskCreated?: (task: Task) => void;
3738
}
3839

3940
interface UseTaskCreationReturn {
@@ -100,6 +101,7 @@ export function useTaskCreation({
100101
reasoningLevel,
101102
environmentId,
102103
sandboxEnvironmentId,
104+
onTaskCreated,
103105
}: UseTaskCreationOptions): UseTaskCreationReturn {
104106
const [isCreatingTask, setIsCreatingTask] = useState(false);
105107
const { navigateToTask } = useNavigationStore();
@@ -154,7 +156,11 @@ export function useTaskCreation({
154156
const taskService = get<TaskService>(RENDERER_TOKENS.TaskService);
155157
const result = await taskService.createTask(input, (output) => {
156158
invalidateTasks(output.task);
157-
navigateToTask(output.task);
159+
if (onTaskCreated) {
160+
onTaskCreated(output.task);
161+
} else {
162+
navigateToTask(output.task);
163+
}
158164
editor.clear();
159165
log.info("Task ready, navigated early", { taskId: output.task.id });
160166
});
@@ -191,6 +197,7 @@ export function useTaskCreation({
191197
sandboxEnvironmentId,
192198
invalidateTasks,
193199
navigateToTask,
200+
onTaskCreated,
194201
]);
195202

196203
return {

0 commit comments

Comments
 (0)