@parhelia/core 0.1.12272 → 0.1.12285
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents-view/AgentCard.js +26 -2
- package/dist/agents-view/AgentCard.js.map +1 -1
- package/dist/components/MarkdownDisplay.d.ts +1 -1
- package/dist/components/MarkdownDisplay.js +6 -0
- package/dist/components/MarkdownDisplay.js.map +1 -1
- package/dist/config/notificationRoutes.js +19 -0
- package/dist/config/notificationRoutes.js.map +1 -1
- package/dist/editor/ConcurrentUserLimitDialog.d.ts +1 -1
- package/dist/editor/ConcurrentUserLimitDialog.js +46 -40
- package/dist/editor/ConcurrentUserLimitDialog.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.d.ts +3 -1
- package/dist/editor/ai/AgentTerminal.js +92 -26
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/dialogs/AgentDialogHandler.js +32 -22
- package/dist/editor/ai/dialogs/AgentDialogHandler.js.map +1 -1
- package/dist/editor/ai/dialogs/QuestionnaireInline.js +4 -2
- package/dist/editor/ai/dialogs/QuestionnaireInline.js.map +1 -1
- package/dist/editor/client/EditorShell.js +6 -1
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/notifications/WatchButton.js +3 -3
- package/dist/editor/notifications/WatchButton.js.map +1 -1
- package/dist/editor/notifications/useNotifications.js +19 -1
- package/dist/editor/notifications/useNotifications.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +1 -0
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/settings/About.js +23 -8
- package/dist/editor/settings/About.js.map +1 -1
- package/dist/licensing/LicenseOverlay.js +1 -3
- package/dist/licensing/LicenseOverlay.js.map +1 -1
- package/dist/licensing/types.d.ts +2 -0
- package/dist/licensing/types.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/task-board/TaskBoardWorkspace.js +390 -443
- package/dist/task-board/TaskBoardWorkspace.js.map +1 -1
- package/dist/task-board/components/CreateProjectDialog.js +37 -17
- package/dist/task-board/components/CreateProjectDialog.js.map +1 -1
- package/dist/task-board/components/ProjectSelector.js +1 -1
- package/dist/task-board/components/ProjectSelector.js.map +1 -1
- package/dist/task-board/components/TaskAgentPanel.js +2 -6
- package/dist/task-board/components/TaskAgentPanel.js.map +1 -1
- package/dist/task-board/components/TaskBoardMyTasksSidebar.js +1 -1
- package/dist/task-board/components/TaskBoardMyTasksSidebar.js.map +1 -1
- package/dist/task-board/components/TaskCard.d.ts +1 -2
- package/dist/task-board/components/TaskCard.js +7 -3
- package/dist/task-board/components/TaskCard.js.map +1 -1
- package/dist/task-board/components/TaskDetailPanel.d.ts +0 -2
- package/dist/task-board/components/TaskDetailPanel.js +5 -12
- package/dist/task-board/components/TaskDetailPanel.js.map +1 -1
- package/dist/task-board/components/TaskReviewActions.d.ts +3 -0
- package/dist/task-board/components/TaskReviewActions.js +8 -7
- package/dist/task-board/components/TaskReviewActions.js.map +1 -1
- package/dist/task-board/components/TaskRow.d.ts +0 -2
- package/dist/task-board/components/TaskRow.js +7 -4
- package/dist/task-board/components/TaskRow.js.map +1 -1
- package/dist/task-board/components/WizardCommunicationCenter.d.ts +3 -0
- package/dist/task-board/components/WizardCommunicationCenter.js +181 -22
- package/dist/task-board/components/WizardCommunicationCenter.js.map +1 -1
- package/dist/task-board/index.d.ts +1 -1
- package/dist/task-board/services/taskService.d.ts +4 -1
- package/dist/task-board/services/taskService.js +3 -0
- package/dist/task-board/services/taskService.js.map +1 -1
- package/dist/task-board/taskAgentLink.js +5 -16
- package/dist/task-board/taskAgentLink.js.map +1 -1
- package/dist/task-board/taskExecutionRecords.d.ts +5 -0
- package/dist/task-board/taskExecutionRecords.js +49 -0
- package/dist/task-board/taskExecutionRecords.js.map +1 -0
- package/dist/task-board/taskExecutionStatus.d.ts +7 -3
- package/dist/task-board/taskExecutionStatus.js +75 -113
- package/dist/task-board/taskExecutionStatus.js.map +1 -1
- package/dist/task-board/taskStatus.d.ts +9 -2
- package/dist/task-board/taskStatus.js +53 -8
- package/dist/task-board/taskStatus.js.map +1 -1
- package/dist/task-board/types.d.ts +30 -5
- package/dist/task-board/views/KanbanView.d.ts +0 -2
- package/dist/task-board/views/KanbanView.js +14 -11
- package/dist/task-board/views/KanbanView.js.map +1 -1
- package/dist/task-board/views/ListView.d.ts +0 -2
- package/dist/task-board/views/ListView.js +3 -13
- package/dist/task-board/views/ListView.js.map +1 -1
- package/dist/task-board/views/WizardView.d.ts +0 -2
- package/dist/task-board/views/WizardView.js +54 -61
- package/dist/task-board/views/WizardView.js.map +1 -1
- package/package.json +1 -1
- package/styles.css +5 -0
|
@@ -2,8 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { toast } from "sonner";
|
|
4
4
|
import { Splitter } from "../editor/ui/Splitter";
|
|
5
|
-
import { deleteProject, getProjects, getTasks, getDependencies, runOrchestrator, triggerPlanning, updateProject, updateTask, } from "./services/taskService";
|
|
6
|
-
import {
|
|
5
|
+
import { deleteProject, getExecutionState, getProjects, getTasks, getDependencies, runOrchestrator, triggerPlanning, updateProject, updateTask, } from "./services/taskService";
|
|
6
|
+
import { getAgent, } from "../editor/services/agentService";
|
|
7
7
|
import { loadAiProfiles } from "../editor/services/aiService";
|
|
8
8
|
import { useEditContext } from "../editor/client/editContext";
|
|
9
9
|
import { KanbanView } from "./views/KanbanView";
|
|
@@ -19,14 +19,27 @@ import { CreateProjectDialog } from "./components/CreateProjectDialog";
|
|
|
19
19
|
import { ProjectSettingsDialog } from "./components/ProjectSettingsDialog";
|
|
20
20
|
import { EditorSlotContextProvider } from "../editor/views/editorSlotContext";
|
|
21
21
|
import { SingleEditView } from "../editor/views/SingleEditView";
|
|
22
|
+
import { useEditorSlotContext } from "../editor/views/editorSlotContext";
|
|
22
23
|
import { Select } from "../components/ui/select";
|
|
23
24
|
import { getLinkedAgentId } from "./taskAgentLink";
|
|
24
25
|
import { setTaskBoardNavState, resetTaskBoardNavState, } from "./taskBoardNavStore";
|
|
25
26
|
import { flattenProjectsHierarchy } from "./utils/projectHierarchy";
|
|
26
|
-
import { normalizeTaskStatus } from "./taskStatus";
|
|
27
|
+
import { normalizeTaskStatus, } from "./taskStatus";
|
|
27
28
|
import { getTaskExecutionDisplay, } from "./taskExecutionStatus";
|
|
29
|
+
import { indexTaskExecutionRecords, overlayTaskExecutionList, } from "./taskExecutionRecords";
|
|
28
30
|
import { WizardCommunicationCenter } from "./components/WizardCommunicationCenter";
|
|
29
31
|
import { Loader2 } from "lucide-react";
|
|
32
|
+
import { MainContentTree } from "../editor/sidebar/MainContentTree";
|
|
33
|
+
function TaskboardPreviewEditor({ itemDescriptor, }) {
|
|
34
|
+
const slotContext = useEditorSlotContext({
|
|
35
|
+
slotId: "taskboard-preview-slot",
|
|
36
|
+
itemDescriptor,
|
|
37
|
+
});
|
|
38
|
+
if (!slotContext) {
|
|
39
|
+
return (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col items-center justify-center gap-3 p-6 text-center", children: [_jsx("div", { className: "text-muted-foreground text-sm font-medium", children: "Editor preview unavailable" }), _jsx("div", { className: "text-muted-foreground text-xs", children: "Open the editor context item to preview and edit it here." })] }));
|
|
40
|
+
}
|
|
41
|
+
return (_jsx(EditorSlotContextProvider, { value: slotContext, children: _jsx(SingleEditView, { compareView: false, name: "taskboard-workspace-preview", view: "primary" }) }));
|
|
42
|
+
}
|
|
30
43
|
const TASKBOARD_PROJECT_QUERY_KEY = "tbProjectId";
|
|
31
44
|
const TASKBOARD_TASK_QUERY_KEY = "tbTaskId";
|
|
32
45
|
const TASKBOARD_VIEW_QUERY_KEY = "tbView";
|
|
@@ -54,84 +67,10 @@ function getTaskBoardStateFromUrl() {
|
|
|
54
67
|
isWizardMode: getInitialWizardMode(),
|
|
55
68
|
};
|
|
56
69
|
}
|
|
57
|
-
function
|
|
58
|
-
if (
|
|
59
|
-
return undefined;
|
|
60
|
-
if (typeof status === "number") {
|
|
61
|
-
return status;
|
|
62
|
-
}
|
|
63
|
-
const normalized = String(status)
|
|
64
|
-
.trim()
|
|
65
|
-
.replace(/[^a-z0-9]/gi, "")
|
|
66
|
-
.toLowerCase();
|
|
67
|
-
switch (normalized) {
|
|
68
|
-
case "running":
|
|
69
|
-
return "running";
|
|
70
|
-
case "waitingforapproval":
|
|
71
|
-
return "waitingForApproval";
|
|
72
|
-
case "waitingforinput":
|
|
73
|
-
return "waitingForInput";
|
|
74
|
-
case "costlimitreached":
|
|
75
|
-
return "costLimitReached";
|
|
76
|
-
case "error":
|
|
77
|
-
return "error";
|
|
78
|
-
case "idle":
|
|
79
|
-
return "idle";
|
|
80
|
-
case "completed":
|
|
81
|
-
return "completed";
|
|
82
|
-
case "closed":
|
|
83
|
-
return "closed";
|
|
84
|
-
case "cancelled":
|
|
85
|
-
return "cancelled";
|
|
86
|
-
case "new":
|
|
87
|
-
return "new";
|
|
88
|
-
}
|
|
89
|
-
switch (status) {
|
|
90
|
-
case "Running":
|
|
91
|
-
return "running";
|
|
92
|
-
case "WaitingForApproval":
|
|
93
|
-
return "waitingForApproval";
|
|
94
|
-
case "WaitingForInput":
|
|
95
|
-
return "waitingForInput";
|
|
96
|
-
case "CostLimitReached":
|
|
97
|
-
return "costLimitReached";
|
|
98
|
-
case "Error":
|
|
99
|
-
return "error";
|
|
100
|
-
case "Idle":
|
|
101
|
-
return "idle";
|
|
102
|
-
case "Completed":
|
|
103
|
-
return "completed";
|
|
104
|
-
case "Closed":
|
|
105
|
-
return "closed";
|
|
106
|
-
case "Cancelled":
|
|
107
|
-
return "cancelled";
|
|
108
|
-
case "New":
|
|
109
|
-
return "new";
|
|
110
|
-
default:
|
|
111
|
-
return status;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
function isClosedAgentStatus(status) {
|
|
115
|
-
if (status === undefined || status === null)
|
|
116
|
-
return false;
|
|
117
|
-
if (typeof status === "number")
|
|
118
|
-
return status === 5;
|
|
119
|
-
return (String(status)
|
|
120
|
-
.replace(/[^a-z0-9]/gi, "")
|
|
121
|
-
.toLowerCase() === "closed");
|
|
122
|
-
}
|
|
123
|
-
function getAgentStatusById(agentStatusesById, agentId) {
|
|
124
|
-
if (!agentId)
|
|
70
|
+
function getTaskExecutionRecordById(recordsByTaskId, taskId) {
|
|
71
|
+
if (!taskId)
|
|
125
72
|
return undefined;
|
|
126
|
-
return
|
|
127
|
-
}
|
|
128
|
-
function getTaskAgentStatus(task, agentStatusesById) {
|
|
129
|
-
const linkedAgentId = getLinkedAgentId(task ?? null);
|
|
130
|
-
const liveStatus = getAgentStatusById(agentStatusesById, linkedAgentId);
|
|
131
|
-
if (liveStatus !== undefined) {
|
|
132
|
-
return liveStatus;
|
|
133
|
-
}
|
|
134
|
-
return normalizeAgentStatus(task?.agentStatus);
|
|
73
|
+
return recordsByTaskId[taskId];
|
|
135
74
|
}
|
|
136
75
|
function addAgentDisplayNames(taskList, agentProfileTitlesById) {
|
|
137
76
|
return taskList.map((task) => {
|
|
@@ -158,7 +97,7 @@ function buildTaskCounts(taskList) {
|
|
|
158
97
|
done: 0,
|
|
159
98
|
};
|
|
160
99
|
for (const task of taskList) {
|
|
161
|
-
const normalizedStatus = normalizeTaskStatus(task.status, task.
|
|
100
|
+
const normalizedStatus = normalizeTaskStatus(task.status, task.executionState);
|
|
162
101
|
if (normalizedStatus === "Todo")
|
|
163
102
|
counts.todo += 1;
|
|
164
103
|
else if (normalizedStatus === "InProgress")
|
|
@@ -188,7 +127,9 @@ export function TaskBoardWorkspace() {
|
|
|
188
127
|
const [tasks, setTasks] = useState([]);
|
|
189
128
|
const [isTasksLoading, setIsTasksLoading] = useState(false);
|
|
190
129
|
const [dependencies, setDependencies] = useState([]);
|
|
130
|
+
const [executionRecordsByTaskId, setExecutionRecordsByTaskId] = useState({});
|
|
191
131
|
const [selectedTaskId, setSelectedTaskId] = useState(() => getInitialQueryValue(TASKBOARD_TASK_QUERY_KEY));
|
|
132
|
+
const [selectedTaskSource, setSelectedTaskSource] = useState(() => getInitialQueryValue(TASKBOARD_TASK_QUERY_KEY) ? "system" : null);
|
|
192
133
|
const [activeTab, setActiveTab] = useState(() => getInitialViewTabIndex());
|
|
193
134
|
const [isWizardMode, setIsWizardMode] = useState(() => getInitialWizardMode());
|
|
194
135
|
const [createDialogOpen, setCreateDialogOpen] = useState(false);
|
|
@@ -200,10 +141,10 @@ export function TaskBoardWorkspace() {
|
|
|
200
141
|
const [showAllProjects, setShowAllProjects] = useState(false);
|
|
201
142
|
const [isProjectsLoading, setIsProjectsLoading] = useState(false);
|
|
202
143
|
const [projectViewLoadingId, setProjectViewLoadingId] = useState(null);
|
|
203
|
-
const [agentStatusesById, setAgentStatusesById] = useState({});
|
|
204
144
|
const [agentProfileTitlesById, setAgentProfileTitlesById] = useState({});
|
|
205
145
|
const [subprojectTaskCounts, setSubprojectTaskCounts] = useState({});
|
|
206
146
|
const [subprojectTasksByProjectId, setSubprojectTasksByProjectId] = useState({});
|
|
147
|
+
const [subprojectExecutionRecordsByProjectId, setSubprojectExecutionRecordsByProjectId] = useState({});
|
|
207
148
|
const [subprojectCountsLoading, setSubprojectCountsLoading] = useState(false);
|
|
208
149
|
const [previewItemName, setPreviewItemName] = useState("");
|
|
209
150
|
const [previewItemPath, setPreviewItemPath] = useState("");
|
|
@@ -212,9 +153,15 @@ export function TaskBoardWorkspace() {
|
|
|
212
153
|
const [contextItemPathsByKey, setContextItemPathsByKey] = useState({});
|
|
213
154
|
const [wizardPinnedTaskId, setWizardPinnedTaskId] = useState(null);
|
|
214
155
|
const [wizardForceOverview, setWizardForceOverview] = useState(false);
|
|
156
|
+
const setTaskSelection = useCallback((taskId, options) => {
|
|
157
|
+
setSelectedTaskId(taskId);
|
|
158
|
+
setSelectedTaskSource(taskId ? (options?.source ?? "system") : null);
|
|
159
|
+
if (options?.pinInWizard !== undefined) {
|
|
160
|
+
setWizardPinnedTaskId(options.pinInWizard ? taskId : null);
|
|
161
|
+
}
|
|
162
|
+
}, []);
|
|
215
163
|
const wsRefreshTimeoutRef = useRef(null);
|
|
216
164
|
const wsSubprojectRefreshTimeoutRef = useRef(null);
|
|
217
|
-
const wsProjectRefreshTimeoutRef = useRef(null);
|
|
218
165
|
const wizardCloseTransitionTimeoutRef = useRef(null);
|
|
219
166
|
const projectsRequestCountRef = useRef(0);
|
|
220
167
|
const tasksRequestCountRef = useRef(0);
|
|
@@ -223,23 +170,23 @@ export function TaskBoardWorkspace() {
|
|
|
223
170
|
const latestSubprojectCountsProjectIdRef = useRef(null);
|
|
224
171
|
const refreshProjectsRef = useRef(null);
|
|
225
172
|
const refreshTasksRef = useRef(null);
|
|
173
|
+
const refreshExecutionStateRef = useRef(null);
|
|
226
174
|
const refreshDependenciesRef = useRef(null);
|
|
227
175
|
const refreshSubprojectTaskCountsRef = useRef(null);
|
|
228
176
|
const selectedProjectIdRef = useRef(null);
|
|
229
177
|
const directSubprojectIdsRef = useRef(new Set());
|
|
230
178
|
const tasksRef = useRef([]);
|
|
231
179
|
const previousTaskStatusesRef = useRef({});
|
|
180
|
+
const previousWizardAttentionSignatureRef = useRef("");
|
|
232
181
|
const autoSelectedProjectIdsRef = useRef(new Set());
|
|
233
|
-
const taskBoardSubscribedAgentIdsRef = useRef(new Set());
|
|
234
|
-
const taskBoardSocketVersionRef = useRef(null);
|
|
235
182
|
const syncTaskBoardStateFromUrl = useCallback(() => {
|
|
236
183
|
const nextState = getTaskBoardStateFromUrl();
|
|
237
184
|
setSelectedProjectId(nextState.projectId);
|
|
238
|
-
|
|
185
|
+
setTaskSelection(nextState.taskId, { source: "system" });
|
|
239
186
|
setActiveTab(nextState.activeTab);
|
|
240
187
|
setIsWizardMode(nextState.isWizardMode);
|
|
241
188
|
setWizardForceOverview(false);
|
|
242
|
-
}, []);
|
|
189
|
+
}, [setTaskSelection]);
|
|
243
190
|
useEffect(() => {
|
|
244
191
|
let cancelled = false;
|
|
245
192
|
loadAiProfiles()
|
|
@@ -262,10 +209,10 @@ export function TaskBoardWorkspace() {
|
|
|
262
209
|
cancelled = true;
|
|
263
210
|
};
|
|
264
211
|
}, []);
|
|
265
|
-
const tasksWithDisplayAssignees = useMemo(() => addAgentDisplayNames(tasks, agentProfileTitlesById), [tasks, agentProfileTitlesById]);
|
|
212
|
+
const tasksWithDisplayAssignees = useMemo(() => addAgentDisplayNames(overlayTaskExecutionList(tasks, executionRecordsByTaskId), agentProfileTitlesById), [tasks, executionRecordsByTaskId, agentProfileTitlesById]);
|
|
266
213
|
const selectedProject = useMemo(() => projects.find((p) => p.project.projectId === selectedProjectId) ?? null, [projects, selectedProjectId]);
|
|
267
214
|
const settingsProject = useMemo(() => projects.find((p) => p.project.projectId === settingsProjectId) ?? null, [projects, settingsProjectId]);
|
|
268
|
-
const selectedProjectTaskCounts = useMemo(() => buildTaskCounts(
|
|
215
|
+
const selectedProjectTaskCounts = useMemo(() => buildTaskCounts(tasksWithDisplayAssignees), [tasksWithDisplayAssignees]);
|
|
269
216
|
const directSubprojects = useMemo(() => selectedProjectId
|
|
270
217
|
? projects
|
|
271
218
|
.filter((project) => project.project.parentProjectId === selectedProjectId)
|
|
@@ -274,10 +221,14 @@ export function TaskBoardWorkspace() {
|
|
|
274
221
|
const subprojectTasksWithDisplayAssignees = useMemo(() => {
|
|
275
222
|
const nextTasks = {};
|
|
276
223
|
for (const [projectId, taskList] of Object.entries(subprojectTasksByProjectId)) {
|
|
277
|
-
nextTasks[projectId] = addAgentDisplayNames(taskList, agentProfileTitlesById);
|
|
224
|
+
nextTasks[projectId] = addAgentDisplayNames(overlayTaskExecutionList(taskList, subprojectExecutionRecordsByProjectId[projectId] || {}), agentProfileTitlesById);
|
|
278
225
|
}
|
|
279
226
|
return nextTasks;
|
|
280
|
-
}, [
|
|
227
|
+
}, [
|
|
228
|
+
subprojectTasksByProjectId,
|
|
229
|
+
subprojectExecutionRecordsByProjectId,
|
|
230
|
+
agentProfileTitlesById,
|
|
231
|
+
]);
|
|
281
232
|
const wizardSubprojectTaskLists = useMemo(() => directSubprojects.map((subproject) => ({
|
|
282
233
|
projectId: subproject.project.projectId,
|
|
283
234
|
title: subproject.project.title,
|
|
@@ -288,6 +239,15 @@ export function TaskBoardWorkspace() {
|
|
|
288
239
|
...tasksWithDisplayAssignees,
|
|
289
240
|
...wizardSubprojectTaskLists.flatMap((subproject) => subproject.tasks),
|
|
290
241
|
], [tasksWithDisplayAssignees, wizardSubprojectTaskLists]);
|
|
242
|
+
const wizardExecutionRecordsByTaskId = useMemo(() => {
|
|
243
|
+
const merged = {
|
|
244
|
+
...executionRecordsByTaskId,
|
|
245
|
+
};
|
|
246
|
+
for (const records of Object.values(subprojectExecutionRecordsByProjectId)) {
|
|
247
|
+
Object.assign(merged, records);
|
|
248
|
+
}
|
|
249
|
+
return merged;
|
|
250
|
+
}, [executionRecordsByTaskId, subprojectExecutionRecordsByProjectId]);
|
|
291
251
|
const taskSelectionUniverse = useMemo(() => isWizardMode
|
|
292
252
|
? wizardTasksWithDisplayAssignees
|
|
293
253
|
: tasksWithDisplayAssignees, [isWizardMode, wizardTasksWithDisplayAssignees, tasksWithDisplayAssignees]);
|
|
@@ -377,18 +337,19 @@ export function TaskBoardWorkspace() {
|
|
|
377
337
|
const handleSelectProject = useCallback((projectId) => {
|
|
378
338
|
setSelectedProjectId((previousProjectId) => {
|
|
379
339
|
if (previousProjectId !== projectId) {
|
|
380
|
-
|
|
340
|
+
setTaskSelection(null, { pinInWizard: false });
|
|
381
341
|
setWizardForceOverview(false);
|
|
382
342
|
}
|
|
383
343
|
return projectId;
|
|
384
344
|
});
|
|
385
|
-
}, []);
|
|
345
|
+
}, [setTaskSelection]);
|
|
386
346
|
const clearSelectedProjectData = useCallback(() => {
|
|
387
347
|
setTasks([]);
|
|
388
348
|
setDependencies([]);
|
|
349
|
+
setExecutionRecordsByTaskId({});
|
|
389
350
|
setSubprojectTaskCounts({});
|
|
390
351
|
setSubprojectTasksByProjectId({});
|
|
391
|
-
|
|
352
|
+
setSubprojectExecutionRecordsByProjectId({});
|
|
392
353
|
}, []);
|
|
393
354
|
const isPlanning = selectedProject?.project.status === "Planning";
|
|
394
355
|
const selectedTask = useMemo(() => taskSelectionUniverse.find((t) => t.taskId === selectedTaskId) ?? null, [taskSelectionUniverse, selectedTaskId]);
|
|
@@ -421,7 +382,8 @@ export function TaskBoardWorkspace() {
|
|
|
421
382
|
const taskById = new Map(tasksWithDisplayAssignees.map((task) => [task.taskId, task]));
|
|
422
383
|
return blockerIds.some((blockerId) => {
|
|
423
384
|
const blockerTask = taskById.get(blockerId);
|
|
424
|
-
return !blockerTask ||
|
|
385
|
+
return (!blockerTask ||
|
|
386
|
+
normalizeTaskStatus(blockerTask.status, blockerTask.executionState) !== "Done");
|
|
425
387
|
});
|
|
426
388
|
}, [
|
|
427
389
|
selectedTask,
|
|
@@ -450,7 +412,7 @@ export function TaskBoardWorkspace() {
|
|
|
450
412
|
const selectedTaskStatus = useMemo(() => {
|
|
451
413
|
if (!selectedTask)
|
|
452
414
|
return null;
|
|
453
|
-
return normalizeTaskStatus(selectedTask.status, selectedTask.
|
|
415
|
+
return normalizeTaskStatus(selectedTask.status, selectedTask.executionState);
|
|
454
416
|
}, [selectedTask]);
|
|
455
417
|
const selectedTaskHasResultData = useMemo(() => {
|
|
456
418
|
return !!selectedTask?.resultData?.trim();
|
|
@@ -466,18 +428,9 @@ export function TaskBoardWorkspace() {
|
|
|
466
428
|
selectedTaskStatus,
|
|
467
429
|
selectedTaskHasResultData,
|
|
468
430
|
]);
|
|
469
|
-
const currentAgentStatus = useMemo(() => {
|
|
470
|
-
return getTaskAgentStatus(selectedTask, agentStatusesById);
|
|
471
|
-
}, [selectedTask, agentStatusesById]);
|
|
472
431
|
const displayAgentId = useMemo(() => {
|
|
473
|
-
if (!currentAgentId)
|
|
474
|
-
return null;
|
|
475
|
-
if (isClosedAgentStatus(currentAgentStatus) &&
|
|
476
|
-
selectedTaskStatus !== "Done") {
|
|
477
|
-
return null;
|
|
478
|
-
}
|
|
479
432
|
return currentAgentId;
|
|
480
|
-
}, [currentAgentId
|
|
433
|
+
}, [currentAgentId]);
|
|
481
434
|
useEffect(() => {
|
|
482
435
|
if (isMobile && selectedTaskId) {
|
|
483
436
|
setMobileActiveTab(1); // Task tab
|
|
@@ -520,55 +473,34 @@ export function TaskBoardWorkspace() {
|
|
|
520
473
|
return a.sortOrder - b.sortOrder;
|
|
521
474
|
return a.createdAt.localeCompare(b.createdAt);
|
|
522
475
|
});
|
|
523
|
-
const
|
|
524
|
-
const blockersByTaskId = new Map();
|
|
525
|
-
for (const dependency of dependencies) {
|
|
526
|
-
if (dependency.dependencyType !== "BlockedBy")
|
|
527
|
-
continue;
|
|
528
|
-
const blockerIds = blockersByTaskId.get(dependency.taskId) || [];
|
|
529
|
-
blockerIds.push(dependency.dependsOnTaskId);
|
|
530
|
-
blockersByTaskId.set(dependency.taskId, blockerIds);
|
|
531
|
-
}
|
|
532
|
-
const openTasks = sortedTasks.filter((task) => normalizeTaskStatus(task.status, task.executionStatus) !== "Done");
|
|
533
|
-
const blockedTaskIds = new Set();
|
|
534
|
-
for (const task of openTasks) {
|
|
535
|
-
const blockerIds = blockersByTaskId.get(task.taskId) || [];
|
|
536
|
-
const isBlocked = blockerIds.some((blockerId) => {
|
|
537
|
-
const blockerTask = taskById.get(blockerId);
|
|
538
|
-
if (!blockerTask)
|
|
539
|
-
return true;
|
|
540
|
-
return (normalizeTaskStatus(blockerTask.status, blockerTask.executionStatus) !== "Done");
|
|
541
|
-
});
|
|
542
|
-
if (isBlocked)
|
|
543
|
-
blockedTaskIds.add(task.taskId);
|
|
544
|
-
}
|
|
476
|
+
const openTasks = sortedTasks.filter((task) => normalizeTaskStatus(task.status, task.executionState) !== "Done");
|
|
545
477
|
let runningCount = 0;
|
|
546
478
|
let approvalCount = 0;
|
|
479
|
+
let blockedCount = 0;
|
|
547
480
|
let attentionTask = null;
|
|
548
481
|
let attentionAgentId = null;
|
|
549
|
-
let attentionAgentStatus;
|
|
550
482
|
let attentionExecutionDisplay = null;
|
|
551
483
|
for (const task of openTasks) {
|
|
552
|
-
const
|
|
484
|
+
const record = getTaskExecutionRecordById(wizardExecutionRecordsByTaskId, task.taskId);
|
|
485
|
+
const executionStateFromBackend = record?.executionState ?? task.executionState;
|
|
486
|
+
const normalizedTaskStatus = normalizeTaskStatus(task.status, executionStateFromBackend);
|
|
553
487
|
const agentId = getLinkedAgentId(task);
|
|
554
|
-
const
|
|
555
|
-
const
|
|
556
|
-
const
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
task.executionStatus === "Queued" ||
|
|
561
|
-
normalizedAgentStatus === "running");
|
|
488
|
+
const executionDisplay = getTaskExecutionDisplay(record);
|
|
489
|
+
const isBlocked = executionStateFromBackend === "WaitingForDependency";
|
|
490
|
+
const isRunning = executionStateFromBackend === "Working";
|
|
491
|
+
if (isBlocked) {
|
|
492
|
+
blockedCount += 1;
|
|
493
|
+
}
|
|
562
494
|
if (isRunning) {
|
|
563
495
|
runningCount += 1;
|
|
564
496
|
}
|
|
565
497
|
if (normalizedTaskStatus === "Review") {
|
|
566
498
|
approvalCount += 1;
|
|
567
499
|
}
|
|
568
|
-
|
|
500
|
+
const needsAttention = !!executionDisplay?.needsAttention || normalizedTaskStatus === "Review";
|
|
501
|
+
if (!attentionTask && !isBlocked && needsAttention) {
|
|
569
502
|
attentionTask = task;
|
|
570
503
|
attentionAgentId = agentId;
|
|
571
|
-
attentionAgentStatus = agentStatus;
|
|
572
504
|
attentionExecutionDisplay = executionDisplay;
|
|
573
505
|
}
|
|
574
506
|
}
|
|
@@ -579,34 +511,77 @@ export function TaskBoardWorkspace() {
|
|
|
579
511
|
else if (runningCount > 0) {
|
|
580
512
|
summaryState = "working";
|
|
581
513
|
}
|
|
582
|
-
else if (
|
|
514
|
+
else if (blockedCount === openTasks.length) {
|
|
583
515
|
summaryState = "blocked";
|
|
584
516
|
}
|
|
585
517
|
return {
|
|
586
518
|
task: attentionTask,
|
|
587
519
|
agentId: attentionAgentId,
|
|
588
|
-
agentStatus: attentionAgentStatus,
|
|
589
520
|
executionDisplay: attentionExecutionDisplay,
|
|
590
521
|
summaryState,
|
|
591
522
|
stats: {
|
|
592
523
|
totalCount: sortedTasks.length,
|
|
593
524
|
completedCount: sortedTasks.length - openTasks.length,
|
|
594
525
|
approvalCount,
|
|
595
|
-
blockedCount
|
|
526
|
+
blockedCount,
|
|
596
527
|
runningCount,
|
|
597
528
|
},
|
|
598
529
|
};
|
|
599
|
-
}, [wizardTasksWithDisplayAssignees,
|
|
530
|
+
}, [wizardTasksWithDisplayAssignees, wizardExecutionRecordsByTaskId]);
|
|
600
531
|
const wizardDisplayedTask = wizardForceOverview
|
|
601
532
|
? null
|
|
602
533
|
: selectedTask ?? wizardPinnedTask ?? wizardAttentionState.task;
|
|
603
534
|
const wizardDisplayedAgentId = wizardDisplayedTask
|
|
604
535
|
? getLinkedAgentId(wizardDisplayedTask)
|
|
605
536
|
: null;
|
|
606
|
-
const wizardDisplayedAgentStatus = getAgentStatusById(agentStatusesById, wizardDisplayedAgentId);
|
|
607
537
|
const wizardDisplayedExecutionDisplay = wizardDisplayedTask
|
|
608
|
-
? getTaskExecutionDisplay(
|
|
538
|
+
? getTaskExecutionDisplay(getTaskExecutionRecordById(wizardExecutionRecordsByTaskId, wizardDisplayedTask.taskId))
|
|
609
539
|
: null;
|
|
540
|
+
useEffect(() => {
|
|
541
|
+
const attentionSnapshot = JSON.stringify({
|
|
542
|
+
selectedTaskId,
|
|
543
|
+
selectedTaskSource,
|
|
544
|
+
wizardPinnedTaskId,
|
|
545
|
+
wizardForceOverview,
|
|
546
|
+
attentionTaskId: wizardAttentionState.task?.taskId ?? null,
|
|
547
|
+
attentionAgentId: wizardAttentionState.agentId ?? null,
|
|
548
|
+
summaryState: wizardAttentionState.summaryState,
|
|
549
|
+
totalCount: wizardAttentionState.stats.totalCount,
|
|
550
|
+
completedCount: wizardAttentionState.stats.completedCount,
|
|
551
|
+
approvalCount: wizardAttentionState.stats.approvalCount,
|
|
552
|
+
blockedCount: wizardAttentionState.stats.blockedCount,
|
|
553
|
+
runningCount: wizardAttentionState.stats.runningCount,
|
|
554
|
+
displayedTaskId: wizardDisplayedTask?.taskId ?? null,
|
|
555
|
+
displayedExecutionLabel: wizardDisplayedExecutionDisplay?.label ?? null,
|
|
556
|
+
displayedNeedsAttention: wizardDisplayedExecutionDisplay?.needsAttention ?? null,
|
|
557
|
+
});
|
|
558
|
+
if (previousWizardAttentionSignatureRef.current === attentionSnapshot) {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
previousWizardAttentionSignatureRef.current = attentionSnapshot;
|
|
562
|
+
}, [
|
|
563
|
+
selectedTaskId,
|
|
564
|
+
selectedTaskSource,
|
|
565
|
+
wizardAttentionState,
|
|
566
|
+
wizardDisplayedExecutionDisplay?.label,
|
|
567
|
+
wizardDisplayedExecutionDisplay?.needsAttention,
|
|
568
|
+
wizardDisplayedTask?.taskId,
|
|
569
|
+
wizardForceOverview,
|
|
570
|
+
wizardPinnedTaskId,
|
|
571
|
+
]);
|
|
572
|
+
useEffect(() => {
|
|
573
|
+
if (!wizardDisplayedTask ||
|
|
574
|
+
(wizardDisplayedExecutionDisplay?.label !== "Questions" &&
|
|
575
|
+
wizardDisplayedTask.executionState !== "WaitingForInput" &&
|
|
576
|
+
wizardDisplayedTask.executionState !== "WaitingForUser")) {
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
}, [
|
|
580
|
+
wizardDisplayedTask,
|
|
581
|
+
wizardDisplayedAgentId,
|
|
582
|
+
wizardDisplayedExecutionDisplay?.label,
|
|
583
|
+
selectedProjectId,
|
|
584
|
+
]);
|
|
610
585
|
const clearWizardCloseTransitionTimeout = useCallback(() => {
|
|
611
586
|
if (wizardCloseTransitionTimeoutRef.current !== null) {
|
|
612
587
|
window.clearTimeout(wizardCloseTransitionTimeoutRef.current);
|
|
@@ -627,48 +602,25 @@ export function TaskBoardWorkspace() {
|
|
|
627
602
|
return a.sortOrder - b.sortOrder;
|
|
628
603
|
return a.createdAt.localeCompare(b.createdAt);
|
|
629
604
|
});
|
|
630
|
-
const taskById = new Map(sortedTasks.map((task) => [task.taskId, task]));
|
|
631
|
-
const blockersByTaskId = new Map();
|
|
632
|
-
for (const dependency of dependencies) {
|
|
633
|
-
if (dependency.dependencyType !== "BlockedBy")
|
|
634
|
-
continue;
|
|
635
|
-
const blockerIds = blockersByTaskId.get(dependency.taskId) || [];
|
|
636
|
-
blockerIds.push(dependency.dependsOnTaskId);
|
|
637
|
-
blockersByTaskId.set(dependency.taskId, blockerIds);
|
|
638
|
-
}
|
|
639
605
|
const openTasks = sortedTasks.filter((task) => {
|
|
640
606
|
if (excludedTaskId && task.taskId === excludedTaskId)
|
|
641
607
|
return false;
|
|
642
|
-
return
|
|
608
|
+
return normalizeTaskStatus(task.status, task.executionState) !== "Done";
|
|
643
609
|
});
|
|
644
|
-
const blockedTaskIds = new Set();
|
|
645
610
|
for (const task of openTasks) {
|
|
646
|
-
const
|
|
647
|
-
const
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
if (isBlocked)
|
|
654
|
-
blockedTaskIds.add(task.taskId);
|
|
655
|
-
}
|
|
656
|
-
for (const task of openTasks) {
|
|
657
|
-
const isBlocked = blockedTaskIds.has(task.taskId);
|
|
658
|
-
const agentId = getLinkedAgentId(task);
|
|
659
|
-
const agentStatus = getTaskAgentStatus(task, agentStatusesById);
|
|
660
|
-
const executionDisplay = getTaskExecutionDisplay(task.executionStatus, agentStatus, task.status);
|
|
661
|
-
const normalizedAgentStatus = normalizeAgentStatus(agentStatus);
|
|
662
|
-
const isRunning = !isBlocked &&
|
|
663
|
-
(task.executionStatus === "Running" ||
|
|
664
|
-
task.executionStatus === "Queued" ||
|
|
665
|
-
normalizedAgentStatus === "running");
|
|
666
|
-
if (!isBlocked && executionDisplay?.needsAttention) {
|
|
611
|
+
const record = getTaskExecutionRecordById(wizardExecutionRecordsByTaskId, task.taskId);
|
|
612
|
+
const executionStateFromBackend = record?.executionState ?? task.executionState;
|
|
613
|
+
const isBlocked = executionStateFromBackend === "WaitingForDependency";
|
|
614
|
+
const executionDisplay = getTaskExecutionDisplay(record);
|
|
615
|
+
const normalizedTaskStatus = normalizeTaskStatus(task.status, executionStateFromBackend);
|
|
616
|
+
const needsAttention = !!executionDisplay?.needsAttention ||
|
|
617
|
+
normalizedTaskStatus === "Review";
|
|
618
|
+
if (!isBlocked && needsAttention) {
|
|
667
619
|
return task.taskId;
|
|
668
620
|
}
|
|
669
621
|
}
|
|
670
622
|
return null;
|
|
671
|
-
}, [
|
|
623
|
+
}, [wizardExecutionRecordsByTaskId, wizardTasksWithDisplayAssignees]);
|
|
672
624
|
// ── data fetching ──
|
|
673
625
|
const refreshProjects = useCallback(async (options) => {
|
|
674
626
|
projectsRequestCountRef.current += 1;
|
|
@@ -767,6 +719,14 @@ export function TaskBoardWorkspace() {
|
|
|
767
719
|
}
|
|
768
720
|
}
|
|
769
721
|
}, []);
|
|
722
|
+
const refreshExecutionState = useCallback(async (projectId) => {
|
|
723
|
+
const result = await getExecutionState({ projectId });
|
|
724
|
+
if (result.type !== "success") {
|
|
725
|
+
toast.error(result.summary || "Failed to load execution state");
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
setExecutionRecordsByTaskId(indexTaskExecutionRecords(result.data?.records));
|
|
729
|
+
}, []);
|
|
770
730
|
const refreshSubprojectTaskCounts = useCallback(async () => {
|
|
771
731
|
const requestProjectId = selectedProjectId;
|
|
772
732
|
latestSubprojectCountsProjectIdRef.current = requestProjectId;
|
|
@@ -774,6 +734,7 @@ export function TaskBoardWorkspace() {
|
|
|
774
734
|
if (latestSubprojectCountsProjectIdRef.current === requestProjectId) {
|
|
775
735
|
setSubprojectTaskCounts({});
|
|
776
736
|
setSubprojectTasksByProjectId({});
|
|
737
|
+
setSubprojectExecutionRecordsByProjectId({});
|
|
777
738
|
}
|
|
778
739
|
return;
|
|
779
740
|
}
|
|
@@ -781,15 +742,23 @@ export function TaskBoardWorkspace() {
|
|
|
781
742
|
try {
|
|
782
743
|
const results = await Promise.all(directSubprojects.map(async (subproject) => {
|
|
783
744
|
const projectId = subproject.project.projectId;
|
|
784
|
-
const
|
|
785
|
-
|
|
786
|
-
|
|
745
|
+
const [tasksResponse, executionResponse] = await Promise.all([
|
|
746
|
+
getTasks(projectId),
|
|
747
|
+
getExecutionState({ projectId }),
|
|
748
|
+
]);
|
|
749
|
+
if (tasksResponse.type !== "success") {
|
|
750
|
+
return [projectId, null, null, null];
|
|
787
751
|
}
|
|
788
|
-
const tasksForProject =
|
|
752
|
+
const tasksForProject = tasksResponse.data || [];
|
|
753
|
+
const recordsByTaskId = executionResponse.type === "success"
|
|
754
|
+
? indexTaskExecutionRecords(executionResponse.data?.records)
|
|
755
|
+
: {};
|
|
756
|
+
const tasksWithExecution = overlayTaskExecutionList(tasksForProject, recordsByTaskId);
|
|
789
757
|
return [
|
|
790
758
|
projectId,
|
|
791
|
-
buildTaskCounts(
|
|
759
|
+
buildTaskCounts(tasksWithExecution),
|
|
792
760
|
tasksForProject,
|
|
761
|
+
recordsByTaskId,
|
|
793
762
|
];
|
|
794
763
|
}));
|
|
795
764
|
if (latestSubprojectCountsProjectIdRef.current !== requestProjectId) {
|
|
@@ -797,14 +766,18 @@ export function TaskBoardWorkspace() {
|
|
|
797
766
|
}
|
|
798
767
|
const nextCounts = {};
|
|
799
768
|
const nextTasksByProjectId = {};
|
|
800
|
-
|
|
769
|
+
const nextExecutionByProjectId = {};
|
|
770
|
+
for (const [projectId, counts, taskList, executionByTaskId] of results) {
|
|
801
771
|
if (counts)
|
|
802
772
|
nextCounts[projectId] = counts;
|
|
803
773
|
if (taskList)
|
|
804
774
|
nextTasksByProjectId[projectId] = taskList;
|
|
775
|
+
if (executionByTaskId)
|
|
776
|
+
nextExecutionByProjectId[projectId] = executionByTaskId;
|
|
805
777
|
}
|
|
806
778
|
setSubprojectTaskCounts(nextCounts);
|
|
807
779
|
setSubprojectTasksByProjectId(isWizardMode ? nextTasksByProjectId : {});
|
|
780
|
+
setSubprojectExecutionRecordsByProjectId(isWizardMode ? nextExecutionByProjectId : {});
|
|
808
781
|
}
|
|
809
782
|
finally {
|
|
810
783
|
setSubprojectCountsLoading(false);
|
|
@@ -825,8 +798,8 @@ export function TaskBoardWorkspace() {
|
|
|
825
798
|
if (!firstTask)
|
|
826
799
|
return;
|
|
827
800
|
autoSelectedProjectIdsRef.current.add(selectedProjectId);
|
|
828
|
-
|
|
829
|
-
}, [selectedProjectId, selectedTaskId, tasks]);
|
|
801
|
+
setTaskSelection(firstTask.taskId, { source: "system" });
|
|
802
|
+
}, [selectedProjectId, selectedTaskId, setTaskSelection, tasks]);
|
|
830
803
|
useEffect(() => {
|
|
831
804
|
if (!wizardPinnedTaskId)
|
|
832
805
|
return;
|
|
@@ -835,24 +808,95 @@ export function TaskBoardWorkspace() {
|
|
|
835
808
|
}
|
|
836
809
|
setWizardPinnedTaskId(null);
|
|
837
810
|
}, [taskSelectionUniverse, wizardPinnedTaskId]);
|
|
811
|
+
// Pin to a task as soon as it shows Questions/WaitingForInput/WaitingForUser, so we don't
|
|
812
|
+
// switch away when another subproject also gets Questions (wizardAttentionState
|
|
813
|
+
// would pick a different task and we'd lose the questionnaire to agent mismatch).
|
|
814
|
+
useEffect(() => {
|
|
815
|
+
if (!isWizardMode)
|
|
816
|
+
return;
|
|
817
|
+
if (wizardForceOverview)
|
|
818
|
+
return;
|
|
819
|
+
if (selectedTaskId)
|
|
820
|
+
return;
|
|
821
|
+
if (wizardPinnedTaskId)
|
|
822
|
+
return;
|
|
823
|
+
if (!wizardDisplayedTask?.taskId)
|
|
824
|
+
return;
|
|
825
|
+
const needsInput = wizardDisplayedExecutionDisplay?.label === "Questions" ||
|
|
826
|
+
wizardDisplayedExecutionDisplay?.label === "Waiting for input" ||
|
|
827
|
+
wizardDisplayedExecutionDisplay?.label === "Waiting for user";
|
|
828
|
+
if (!needsInput)
|
|
829
|
+
return;
|
|
830
|
+
setWizardPinnedTaskId(wizardDisplayedTask.taskId);
|
|
831
|
+
}, [
|
|
832
|
+
isWizardMode,
|
|
833
|
+
wizardForceOverview,
|
|
834
|
+
selectedTaskId,
|
|
835
|
+
wizardPinnedTaskId,
|
|
836
|
+
wizardDisplayedTask?.taskId,
|
|
837
|
+
wizardDisplayedExecutionDisplay?.label,
|
|
838
|
+
]);
|
|
838
839
|
useEffect(() => {
|
|
839
840
|
if (!isWizardMode)
|
|
840
841
|
return;
|
|
841
842
|
if (wizardForceOverview)
|
|
842
843
|
return;
|
|
843
|
-
if (selectedTaskId
|
|
844
|
+
if (selectedTaskId)
|
|
844
845
|
return;
|
|
845
846
|
const attentionTaskId = wizardAttentionState.task?.taskId;
|
|
846
|
-
if (!attentionTaskId)
|
|
847
|
+
if (!attentionTaskId) {
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
if (wizardPinnedTaskId === attentionTaskId)
|
|
851
|
+
return;
|
|
852
|
+
// Don't auto-pin away from a task that has Questions, Waiting for input, or Waiting for user—
|
|
853
|
+
// switching would unmount its AgentTerminal and cause the questionnaire to be
|
|
854
|
+
// dispatched to the wrong terminal (agent mismatch). Keep user on current task.
|
|
855
|
+
const displayedNeedsInput = wizardDisplayedExecutionDisplay?.label === "Questions" ||
|
|
856
|
+
wizardDisplayedExecutionDisplay?.label === "Waiting for input" ||
|
|
857
|
+
wizardDisplayedExecutionDisplay?.label === "Waiting for user";
|
|
858
|
+
if (displayedNeedsInput && wizardDisplayedTask?.taskId) {
|
|
847
859
|
return;
|
|
860
|
+
}
|
|
848
861
|
setWizardPinnedTaskId(attentionTaskId);
|
|
849
862
|
}, [
|
|
850
863
|
isWizardMode,
|
|
851
864
|
selectedTaskId,
|
|
865
|
+
selectedTaskSource,
|
|
866
|
+
wizardDisplayedExecutionDisplay?.label,
|
|
867
|
+
wizardDisplayedTask?.taskId,
|
|
852
868
|
wizardPinnedTaskId,
|
|
853
869
|
wizardForceOverview,
|
|
870
|
+
wizardAttentionState.summaryState,
|
|
854
871
|
wizardAttentionState.task?.taskId,
|
|
855
872
|
]);
|
|
873
|
+
useEffect(() => {
|
|
874
|
+
if (!isWizardMode)
|
|
875
|
+
return;
|
|
876
|
+
if (selectedTaskSource === "user")
|
|
877
|
+
return;
|
|
878
|
+
if (!selectedTask)
|
|
879
|
+
return;
|
|
880
|
+
if (normalizeTaskStatus(selectedTask.status, selectedTask.executionState) !== "Done") {
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
const nextAttentionTaskId = findNextAttentionTaskId(selectedTask.taskId);
|
|
884
|
+
if (nextAttentionTaskId) {
|
|
885
|
+
setWizardForceOverview(false);
|
|
886
|
+
setTaskSelection(nextAttentionTaskId, {
|
|
887
|
+
source: "system",
|
|
888
|
+
pinInWizard: true,
|
|
889
|
+
});
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
setTaskSelection(null, { pinInWizard: false });
|
|
893
|
+
}, [
|
|
894
|
+
findNextAttentionTaskId,
|
|
895
|
+
isWizardMode,
|
|
896
|
+
selectedTask,
|
|
897
|
+
selectedTaskSource,
|
|
898
|
+
setTaskSelection,
|
|
899
|
+
]);
|
|
856
900
|
useEffect(() => {
|
|
857
901
|
if (isWizardMode)
|
|
858
902
|
return;
|
|
@@ -902,70 +946,21 @@ export function TaskBoardWorkspace() {
|
|
|
902
946
|
if (!selectedProjectId)
|
|
903
947
|
return;
|
|
904
948
|
await refreshTasks(selectedProjectId);
|
|
949
|
+
await refreshExecutionState(selectedProjectId);
|
|
905
950
|
await refreshDependencies(selectedProjectId);
|
|
906
951
|
await refreshSubprojectTaskCounts();
|
|
907
952
|
}, [
|
|
908
953
|
selectedProjectId,
|
|
909
954
|
refreshTasks,
|
|
955
|
+
refreshExecutionState,
|
|
910
956
|
refreshDependencies,
|
|
911
957
|
refreshSubprojectTaskCounts,
|
|
912
958
|
]);
|
|
913
|
-
const refreshAgentStatuses = useCallback(async (taskList) => {
|
|
914
|
-
const agentIds = Array.from(new Set(taskList
|
|
915
|
-
.map((task) => getLinkedAgentId(task))
|
|
916
|
-
.filter((id) => typeof id === "string" && id.length > 0)));
|
|
917
|
-
if (agentIds.length === 0) {
|
|
918
|
-
setAgentStatusesById({});
|
|
919
|
-
return;
|
|
920
|
-
}
|
|
921
|
-
try {
|
|
922
|
-
const response = await getActiveAgents({
|
|
923
|
-
limit: 1000,
|
|
924
|
-
includeOwned: true,
|
|
925
|
-
includeShared: true,
|
|
926
|
-
excludeClosed: false,
|
|
927
|
-
});
|
|
928
|
-
const wanted = new Set(agentIds.map((id) => id.toLowerCase()));
|
|
929
|
-
const apiStatuses = {};
|
|
930
|
-
for (const agent of response.agents || []) {
|
|
931
|
-
const normalizedAgentId = agent.id.toLowerCase();
|
|
932
|
-
if (wanted.has(normalizedAgentId)) {
|
|
933
|
-
apiStatuses[normalizedAgentId] =
|
|
934
|
-
normalizeAgentStatus(agent.status) ?? agent.status;
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
setAgentStatusesById((prev) => {
|
|
938
|
-
const merged = { ...prev };
|
|
939
|
-
for (const [id, apiStatus] of Object.entries(apiStatuses)) {
|
|
940
|
-
const existing = merged[id];
|
|
941
|
-
const existingNormalized = existing !== undefined ? normalizeAgentStatus(existing) : undefined;
|
|
942
|
-
if (existingNormalized === "waitingForInput" ||
|
|
943
|
-
existingNormalized === "waitingForApproval" ||
|
|
944
|
-
existingNormalized === "error" ||
|
|
945
|
-
existingNormalized === "costLimitReached") {
|
|
946
|
-
const apiNormalized = normalizeAgentStatus(apiStatus);
|
|
947
|
-
if (apiNormalized !== "waitingForInput" &&
|
|
948
|
-
apiNormalized !== "waitingForApproval" &&
|
|
949
|
-
apiNormalized !== "error" &&
|
|
950
|
-
apiNormalized !== "costLimitReached" &&
|
|
951
|
-
apiNormalized !== "completed" &&
|
|
952
|
-
apiNormalized !== "closed") {
|
|
953
|
-
continue;
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
merged[id] = apiStatus;
|
|
957
|
-
}
|
|
958
|
-
return merged;
|
|
959
|
-
});
|
|
960
|
-
}
|
|
961
|
-
catch {
|
|
962
|
-
// best-effort only
|
|
963
|
-
}
|
|
964
|
-
}, []);
|
|
965
959
|
// Sync refs with latest function/state values for use in WebSocket timeout callback
|
|
966
960
|
useEffect(() => {
|
|
967
961
|
refreshProjectsRef.current = refreshProjects;
|
|
968
962
|
refreshTasksRef.current = refreshTasks;
|
|
963
|
+
refreshExecutionStateRef.current = refreshExecutionState;
|
|
969
964
|
refreshDependenciesRef.current = refreshDependencies;
|
|
970
965
|
refreshSubprojectTaskCountsRef.current = refreshSubprojectTaskCounts;
|
|
971
966
|
selectedProjectIdRef.current = selectedProjectId;
|
|
@@ -974,51 +969,13 @@ export function TaskBoardWorkspace() {
|
|
|
974
969
|
}, [
|
|
975
970
|
refreshProjects,
|
|
976
971
|
refreshTasks,
|
|
972
|
+
refreshExecutionState,
|
|
977
973
|
refreshDependencies,
|
|
978
974
|
refreshSubprojectTaskCounts,
|
|
979
975
|
selectedProjectId,
|
|
980
976
|
directSubprojects,
|
|
981
977
|
taskSelectionUniverse,
|
|
982
978
|
]);
|
|
983
|
-
// Ensure task-board receives live agent status updates (including WaitingForInput)
|
|
984
|
-
// without requiring the terminal panel to be opened. Re-run on socket reconnect
|
|
985
|
-
// so subscriptions are restored after page reload / reconnect.
|
|
986
|
-
useEffect(() => {
|
|
987
|
-
const socketVersion = editContext?.socketConnectionVersion ?? null;
|
|
988
|
-
if (taskBoardSocketVersionRef.current !== socketVersion) {
|
|
989
|
-
taskBoardSocketVersionRef.current = socketVersion;
|
|
990
|
-
taskBoardSubscribedAgentIdsRef.current.clear();
|
|
991
|
-
}
|
|
992
|
-
if (!selectedProjectId)
|
|
993
|
-
return;
|
|
994
|
-
const desiredAgentIds = new Set(taskSelectionUniverse
|
|
995
|
-
.map((task) => getLinkedAgentId(task))
|
|
996
|
-
.filter((id) => typeof id === "string" && id.length > 0)
|
|
997
|
-
.map((id) => id.toLowerCase()));
|
|
998
|
-
// Delay one tick so socket listeners are attached before pending dialog replay
|
|
999
|
-
// is emitted by the server for subscribed agents.
|
|
1000
|
-
const subscribeTimeout = window.setTimeout(() => {
|
|
1001
|
-
const socket = globalThis.editorSocket;
|
|
1002
|
-
if (!socket || socket.readyState !== WebSocket.OPEN)
|
|
1003
|
-
return;
|
|
1004
|
-
for (const agentId of desiredAgentIds) {
|
|
1005
|
-
if (taskBoardSubscribedAgentIdsRef.current.has(agentId))
|
|
1006
|
-
continue;
|
|
1007
|
-
socket.send(JSON.stringify({
|
|
1008
|
-
type: "agent:subscribe",
|
|
1009
|
-
agentId,
|
|
1010
|
-
}));
|
|
1011
|
-
taskBoardSubscribedAgentIdsRef.current.add(agentId);
|
|
1012
|
-
}
|
|
1013
|
-
}, 0);
|
|
1014
|
-
return () => {
|
|
1015
|
-
window.clearTimeout(subscribeTimeout);
|
|
1016
|
-
};
|
|
1017
|
-
}, [
|
|
1018
|
-
selectedProjectId,
|
|
1019
|
-
taskSelectionUniverse,
|
|
1020
|
-
editContext?.socketConnectionVersion,
|
|
1021
|
-
]);
|
|
1022
979
|
const handleRunOrchestrator = useCallback(async (options) => {
|
|
1023
980
|
if (!selectedProjectId)
|
|
1024
981
|
return;
|
|
@@ -1070,9 +1027,6 @@ export function TaskBoardWorkspace() {
|
|
|
1070
1027
|
return;
|
|
1071
1028
|
setRunningOrchestrator(true);
|
|
1072
1029
|
try {
|
|
1073
|
-
if (task.executionStatus === "Idle") {
|
|
1074
|
-
await updateTask({ taskId: task.taskId, executionStatus: "Queued" });
|
|
1075
|
-
}
|
|
1076
1030
|
const result = await runOrchestrator(taskProjectId);
|
|
1077
1031
|
if (result.type !== "success") {
|
|
1078
1032
|
toast.error(result.summary || "Failed to launch agent");
|
|
@@ -1104,7 +1058,6 @@ export function TaskBoardWorkspace() {
|
|
|
1104
1058
|
const reopenResult = await updateTask({
|
|
1105
1059
|
taskId: selectedTask.taskId,
|
|
1106
1060
|
status: "Todo",
|
|
1107
|
-
executionStatus: "Idle",
|
|
1108
1061
|
});
|
|
1109
1062
|
if (reopenResult.type !== "success") {
|
|
1110
1063
|
toast.error(reopenResult.summary || "Failed to reopen task");
|
|
@@ -1124,15 +1077,6 @@ export function TaskBoardWorkspace() {
|
|
|
1124
1077
|
}
|
|
1125
1078
|
setRunningOrchestrator(true);
|
|
1126
1079
|
try {
|
|
1127
|
-
const queueResult = await updateTask({
|
|
1128
|
-
taskId: selectedTask.taskId,
|
|
1129
|
-
executionStatus: "Queued",
|
|
1130
|
-
});
|
|
1131
|
-
if (queueResult.type !== "success") {
|
|
1132
|
-
toast.error(queueResult.summary || "Failed to queue agent");
|
|
1133
|
-
await refreshVisibleTaskData();
|
|
1134
|
-
return;
|
|
1135
|
-
}
|
|
1136
1080
|
const result = await runOrchestrator(taskProjectId);
|
|
1137
1081
|
if (result.type !== "success") {
|
|
1138
1082
|
toast.error(result.summary || "Failed to launch agent");
|
|
@@ -1176,17 +1120,25 @@ export function TaskBoardWorkspace() {
|
|
|
1176
1120
|
}
|
|
1177
1121
|
toast.success("Planning agent started");
|
|
1178
1122
|
await refreshTasks(selectedProjectId);
|
|
1123
|
+
await refreshExecutionState(selectedProjectId);
|
|
1179
1124
|
await refreshDependencies(selectedProjectId);
|
|
1180
1125
|
const plan = tasks.find((t) => String(t.taskType ?? "").toLowerCase() === "plan") ??
|
|
1181
1126
|
tasks.find((t) => t.title === "Project Plan") ??
|
|
1182
1127
|
null;
|
|
1183
1128
|
if (plan)
|
|
1184
|
-
|
|
1129
|
+
setTaskSelection(plan.taskId, { source: "system" });
|
|
1185
1130
|
}
|
|
1186
1131
|
catch {
|
|
1187
1132
|
toast.error("Failed to start planning");
|
|
1188
1133
|
}
|
|
1189
|
-
}, [
|
|
1134
|
+
}, [
|
|
1135
|
+
selectedProjectId,
|
|
1136
|
+
refreshTasks,
|
|
1137
|
+
refreshExecutionState,
|
|
1138
|
+
refreshDependencies,
|
|
1139
|
+
setTaskSelection,
|
|
1140
|
+
tasks,
|
|
1141
|
+
]);
|
|
1190
1142
|
const handleProjectStatusChange = useCallback(async (status) => {
|
|
1191
1143
|
if (!selectedProject)
|
|
1192
1144
|
return;
|
|
@@ -1215,8 +1167,8 @@ export function TaskBoardWorkspace() {
|
|
|
1215
1167
|
}, [selectedProject, refreshProjects]);
|
|
1216
1168
|
const handleSelectMyTask = useCallback((task) => {
|
|
1217
1169
|
handleSelectProject(task.projectId);
|
|
1218
|
-
|
|
1219
|
-
}, [handleSelectProject]);
|
|
1170
|
+
setTaskSelection(task.taskId, { source: "user" });
|
|
1171
|
+
}, [handleSelectProject, setTaskSelection]);
|
|
1220
1172
|
useEffect(() => {
|
|
1221
1173
|
setTaskBoardNavState({
|
|
1222
1174
|
projectTitle: selectedProject?.project.title ?? "Tasks",
|
|
@@ -1326,9 +1278,11 @@ export function TaskBoardWorkspace() {
|
|
|
1326
1278
|
setProjectViewLoadingId(selectedProjectId);
|
|
1327
1279
|
let cancelled = false;
|
|
1328
1280
|
const refreshTasksFn = refreshTasksRef.current;
|
|
1281
|
+
const refreshExecutionStateFn = refreshExecutionStateRef.current;
|
|
1329
1282
|
const refreshDependenciesFn = refreshDependenciesRef.current;
|
|
1330
1283
|
void Promise.all([
|
|
1331
1284
|
refreshTasksFn?.(selectedProjectId),
|
|
1285
|
+
refreshExecutionStateFn?.(selectedProjectId),
|
|
1332
1286
|
refreshDependenciesFn?.(selectedProjectId),
|
|
1333
1287
|
]).finally(() => {
|
|
1334
1288
|
if (cancelled)
|
|
@@ -1347,24 +1301,19 @@ export function TaskBoardWorkspace() {
|
|
|
1347
1301
|
useEffect(() => {
|
|
1348
1302
|
void refreshSubprojectTaskCounts();
|
|
1349
1303
|
}, [refreshSubprojectTaskCounts]);
|
|
1350
|
-
useEffect(() => {
|
|
1351
|
-
if (!selectedProjectId) {
|
|
1352
|
-
setAgentStatusesById({});
|
|
1353
|
-
return;
|
|
1354
|
-
}
|
|
1355
|
-
void refreshAgentStatuses(taskSelectionUniverse);
|
|
1356
|
-
}, [selectedProjectId, taskSelectionUniverse, refreshAgentStatuses]);
|
|
1357
1304
|
useEffect(() => {
|
|
1358
1305
|
previousTaskStatusesRef.current = {};
|
|
1306
|
+
previousWizardAttentionSignatureRef.current = "";
|
|
1359
1307
|
}, [selectedProjectId]);
|
|
1360
1308
|
useEffect(() => {
|
|
1361
1309
|
if (!selectedProjectId)
|
|
1362
1310
|
return;
|
|
1363
1311
|
const previousStatuses = previousTaskStatusesRef.current;
|
|
1364
1312
|
const currentStatuses = {};
|
|
1313
|
+
const transitions = [];
|
|
1365
1314
|
let movedToDone = false;
|
|
1366
|
-
for (const task of
|
|
1367
|
-
const currentStatus = normalizeTaskStatus(task.status, task.
|
|
1315
|
+
for (const task of tasksWithDisplayAssignees) {
|
|
1316
|
+
const currentStatus = normalizeTaskStatus(task.status, task.executionState);
|
|
1368
1317
|
currentStatuses[task.taskId] = currentStatus;
|
|
1369
1318
|
const previousStatus = previousStatuses[task.taskId];
|
|
1370
1319
|
if (previousStatus &&
|
|
@@ -1372,14 +1321,27 @@ export function TaskBoardWorkspace() {
|
|
|
1372
1321
|
currentStatus === "Done") {
|
|
1373
1322
|
movedToDone = true;
|
|
1374
1323
|
}
|
|
1324
|
+
if (!previousStatus || previousStatus !== currentStatus) {
|
|
1325
|
+
transitions.push({
|
|
1326
|
+
taskId: task.taskId,
|
|
1327
|
+
title: task.title,
|
|
1328
|
+
previousStatus: previousStatus ?? null,
|
|
1329
|
+
currentStatus,
|
|
1330
|
+
rawStatus: task.status ?? null,
|
|
1331
|
+
executionState: task.executionState ?? null,
|
|
1332
|
+
});
|
|
1333
|
+
}
|
|
1375
1334
|
}
|
|
1376
1335
|
previousTaskStatusesRef.current = currentStatuses;
|
|
1336
|
+
if (transitions.length > 0) {
|
|
1337
|
+
// transitions tracked for potential future use
|
|
1338
|
+
}
|
|
1377
1339
|
if (movedToDone && canCreate && !isPlanning && !runningOrchestrator) {
|
|
1378
1340
|
void handleRunOrchestrator({ silentWhenNothingToLaunch: true });
|
|
1379
1341
|
}
|
|
1380
1342
|
}, [
|
|
1381
1343
|
selectedProjectId,
|
|
1382
|
-
|
|
1344
|
+
tasksWithDisplayAssignees,
|
|
1383
1345
|
canCreate,
|
|
1384
1346
|
isPlanning,
|
|
1385
1347
|
runningOrchestrator,
|
|
@@ -1394,6 +1356,64 @@ export function TaskBoardWorkspace() {
|
|
|
1394
1356
|
return;
|
|
1395
1357
|
const unsubscribe = addListener((message) => {
|
|
1396
1358
|
const messageType = message?.type;
|
|
1359
|
+
if (messageType === "task:state-changed") {
|
|
1360
|
+
const payload = message?.payload;
|
|
1361
|
+
const projectId = payload?.projectId ?? payload?.ProjectId ?? payload?.record?.projectId;
|
|
1362
|
+
const record = payload?.record;
|
|
1363
|
+
const currentProjectId = selectedProjectIdRef.current;
|
|
1364
|
+
if (!projectId || !currentProjectId || !record?.taskId)
|
|
1365
|
+
return;
|
|
1366
|
+
if (projectId === currentProjectId) {
|
|
1367
|
+
setExecutionRecordsByTaskId((prev) => {
|
|
1368
|
+
const current = prev[record.taskId];
|
|
1369
|
+
if (current && current.version > record.version) {
|
|
1370
|
+
return prev;
|
|
1371
|
+
}
|
|
1372
|
+
if (current &&
|
|
1373
|
+
current.version === record.version &&
|
|
1374
|
+
current.taskStatus === record.taskStatus &&
|
|
1375
|
+
current.executionState === record.executionState &&
|
|
1376
|
+
current.attentionReason === record.attentionReason &&
|
|
1377
|
+
current.waitingOnDialogId === record.waitingOnDialogId &&
|
|
1378
|
+
current.waitingOnToolCallId === record.waitingOnToolCallId &&
|
|
1379
|
+
current.agentId === record.agentId) {
|
|
1380
|
+
return prev;
|
|
1381
|
+
}
|
|
1382
|
+
return {
|
|
1383
|
+
...prev,
|
|
1384
|
+
[record.taskId]: record,
|
|
1385
|
+
};
|
|
1386
|
+
});
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
if (directSubprojectIdsRef.current.has(projectId)) {
|
|
1390
|
+
setSubprojectExecutionRecordsByProjectId((prev) => {
|
|
1391
|
+
const projectRecords = prev[projectId] ?? {};
|
|
1392
|
+
const current = projectRecords[record.taskId];
|
|
1393
|
+
if (current && (current.version ?? 0) > (record.version ?? 0)) {
|
|
1394
|
+
return prev;
|
|
1395
|
+
}
|
|
1396
|
+
return {
|
|
1397
|
+
...prev,
|
|
1398
|
+
[projectId]: {
|
|
1399
|
+
...projectRecords,
|
|
1400
|
+
[record.taskId]: record,
|
|
1401
|
+
},
|
|
1402
|
+
};
|
|
1403
|
+
});
|
|
1404
|
+
if (wsSubprojectRefreshTimeoutRef.current !== null) {
|
|
1405
|
+
window.clearTimeout(wsSubprojectRefreshTimeoutRef.current);
|
|
1406
|
+
wsSubprojectRefreshTimeoutRef.current = null;
|
|
1407
|
+
}
|
|
1408
|
+
wsSubprojectRefreshTimeoutRef.current = window.setTimeout(() => {
|
|
1409
|
+
wsSubprojectRefreshTimeoutRef.current = null;
|
|
1410
|
+
const refreshSubprojectsFn = refreshSubprojectTaskCountsRef.current;
|
|
1411
|
+
if (refreshSubprojectsFn)
|
|
1412
|
+
void refreshSubprojectsFn();
|
|
1413
|
+
}, 250);
|
|
1414
|
+
}
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
1397
1417
|
if (messageType !== "task:updated" &&
|
|
1398
1418
|
messageType !== "task:created" &&
|
|
1399
1419
|
messageType !== "task:deleted") {
|
|
@@ -1432,10 +1452,12 @@ export function TaskBoardWorkspace() {
|
|
|
1432
1452
|
const pid = selectedProjectIdRef.current;
|
|
1433
1453
|
const refreshProjectsFn = refreshProjectsRef.current;
|
|
1434
1454
|
const refreshTasksFn = refreshTasksRef.current;
|
|
1455
|
+
const refreshExecutionStateFn = refreshExecutionStateRef.current;
|
|
1435
1456
|
const refreshDepsFn = refreshDependenciesRef.current;
|
|
1436
|
-
if (!pid || !refreshTasksFn || !refreshDepsFn)
|
|
1457
|
+
if (!pid || !refreshTasksFn || !refreshDepsFn || !refreshExecutionStateFn)
|
|
1437
1458
|
return;
|
|
1438
1459
|
void refreshTasksFn(pid);
|
|
1460
|
+
void refreshExecutionStateFn(pid);
|
|
1439
1461
|
void refreshDepsFn(pid);
|
|
1440
1462
|
if (refreshProjectsFn)
|
|
1441
1463
|
void refreshProjectsFn();
|
|
@@ -1461,96 +1483,6 @@ export function TaskBoardWorkspace() {
|
|
|
1461
1483
|
});
|
|
1462
1484
|
return () => unsubscribe();
|
|
1463
1485
|
}, [editContext, refreshProjects]);
|
|
1464
|
-
useEffect(() => {
|
|
1465
|
-
const addListener = editContext?.addSocketMessageListener;
|
|
1466
|
-
if (!addListener)
|
|
1467
|
-
return;
|
|
1468
|
-
const unsubscribe = addListener((message) => {
|
|
1469
|
-
const type = message?.type;
|
|
1470
|
-
if (!type)
|
|
1471
|
-
return;
|
|
1472
|
-
const payload = message?.payload ?? {};
|
|
1473
|
-
const agentId = payload?.agentId;
|
|
1474
|
-
if (!agentId || typeof agentId !== "string")
|
|
1475
|
-
return;
|
|
1476
|
-
const trackedAgentIds = new Set(tasksRef.current
|
|
1477
|
-
.map((task) => getLinkedAgentId(task))
|
|
1478
|
-
.filter((id) => typeof id === "string" && id.length > 0)
|
|
1479
|
-
.map((id) => id.toLowerCase()));
|
|
1480
|
-
if (!trackedAgentIds.has(agentId.toLowerCase()))
|
|
1481
|
-
return;
|
|
1482
|
-
const queueProjectsRefresh = () => {
|
|
1483
|
-
if (wsProjectRefreshTimeoutRef.current !== null) {
|
|
1484
|
-
window.clearTimeout(wsProjectRefreshTimeoutRef.current);
|
|
1485
|
-
wsProjectRefreshTimeoutRef.current = null;
|
|
1486
|
-
}
|
|
1487
|
-
wsProjectRefreshTimeoutRef.current = window.setTimeout(() => {
|
|
1488
|
-
wsProjectRefreshTimeoutRef.current = null;
|
|
1489
|
-
const refreshProjectsFn = refreshProjectsRef.current;
|
|
1490
|
-
if (!refreshProjectsFn)
|
|
1491
|
-
return;
|
|
1492
|
-
void refreshProjectsFn();
|
|
1493
|
-
}, 500);
|
|
1494
|
-
};
|
|
1495
|
-
if (type === "agent:run:status") {
|
|
1496
|
-
const status = normalizeAgentStatus(payload?.data?.state ?? payload?.state);
|
|
1497
|
-
if (status === undefined)
|
|
1498
|
-
return;
|
|
1499
|
-
const normalizedAgentId = agentId.toLowerCase();
|
|
1500
|
-
setAgentStatusesById((prev) => ({
|
|
1501
|
-
...prev,
|
|
1502
|
-
[normalizedAgentId]: status,
|
|
1503
|
-
}));
|
|
1504
|
-
queueProjectsRefresh();
|
|
1505
|
-
return;
|
|
1506
|
-
}
|
|
1507
|
-
if (type === "agent-dialog-request") {
|
|
1508
|
-
const normalizedAgentId = agentId.toLowerCase();
|
|
1509
|
-
setAgentStatusesById((prev) => ({
|
|
1510
|
-
...prev,
|
|
1511
|
-
[normalizedAgentId]: "waitingForInput",
|
|
1512
|
-
}));
|
|
1513
|
-
queueProjectsRefresh();
|
|
1514
|
-
return;
|
|
1515
|
-
}
|
|
1516
|
-
if (type === "agent:run:start") {
|
|
1517
|
-
const normalizedAgentId = agentId.toLowerCase();
|
|
1518
|
-
setAgentStatusesById((prev) => ({
|
|
1519
|
-
...prev,
|
|
1520
|
-
[normalizedAgentId]: "running",
|
|
1521
|
-
}));
|
|
1522
|
-
queueProjectsRefresh();
|
|
1523
|
-
return;
|
|
1524
|
-
}
|
|
1525
|
-
if (type === "agent:run:complete") {
|
|
1526
|
-
const normalizedAgentId = agentId.toLowerCase();
|
|
1527
|
-
setAgentStatusesById((prev) => ({
|
|
1528
|
-
...prev,
|
|
1529
|
-
[normalizedAgentId]: "completed",
|
|
1530
|
-
}));
|
|
1531
|
-
queueProjectsRefresh();
|
|
1532
|
-
return;
|
|
1533
|
-
}
|
|
1534
|
-
if (type === "agent:run:error") {
|
|
1535
|
-
const normalizedAgentId = agentId.toLowerCase();
|
|
1536
|
-
setAgentStatusesById((prev) => ({
|
|
1537
|
-
...prev,
|
|
1538
|
-
[normalizedAgentId]: "error",
|
|
1539
|
-
}));
|
|
1540
|
-
queueProjectsRefresh();
|
|
1541
|
-
return;
|
|
1542
|
-
}
|
|
1543
|
-
if (type === "agent:run:closed") {
|
|
1544
|
-
const normalizedAgentId = agentId.toLowerCase();
|
|
1545
|
-
setAgentStatusesById((prev) => ({
|
|
1546
|
-
...prev,
|
|
1547
|
-
[normalizedAgentId]: "closed",
|
|
1548
|
-
}));
|
|
1549
|
-
queueProjectsRefresh();
|
|
1550
|
-
}
|
|
1551
|
-
});
|
|
1552
|
-
return () => unsubscribe();
|
|
1553
|
-
}, [editContext]);
|
|
1554
1486
|
// Clean up pending debounced refresh on unmount only.
|
|
1555
1487
|
useEffect(() => {
|
|
1556
1488
|
return () => {
|
|
@@ -1562,10 +1494,6 @@ export function TaskBoardWorkspace() {
|
|
|
1562
1494
|
window.clearTimeout(wsSubprojectRefreshTimeoutRef.current);
|
|
1563
1495
|
wsSubprojectRefreshTimeoutRef.current = null;
|
|
1564
1496
|
}
|
|
1565
|
-
if (wsProjectRefreshTimeoutRef.current !== null) {
|
|
1566
|
-
window.clearTimeout(wsProjectRefreshTimeoutRef.current);
|
|
1567
|
-
wsProjectRefreshTimeoutRef.current = null;
|
|
1568
|
-
}
|
|
1569
1497
|
if (wizardCloseTransitionTimeoutRef.current !== null) {
|
|
1570
1498
|
window.clearTimeout(wizardCloseTransitionTimeoutRef.current);
|
|
1571
1499
|
wizardCloseTransitionTimeoutRef.current = null;
|
|
@@ -1577,21 +1505,28 @@ export function TaskBoardWorkspace() {
|
|
|
1577
1505
|
if (!selectedTaskId)
|
|
1578
1506
|
return;
|
|
1579
1507
|
if (!taskSelectionUniverse.some((t) => t.taskId === selectedTaskId)) {
|
|
1580
|
-
|
|
1508
|
+
setTaskSelection(null);
|
|
1581
1509
|
}
|
|
1582
|
-
}, [
|
|
1510
|
+
}, [
|
|
1511
|
+
isWizardMode,
|
|
1512
|
+
selectedTaskId,
|
|
1513
|
+
setTaskSelection,
|
|
1514
|
+
taskSelectionUniverse,
|
|
1515
|
+
]);
|
|
1583
1516
|
// No special auto-selection by project status — the agent panel is driven by
|
|
1584
1517
|
// whichever task the user selects.
|
|
1585
1518
|
// The backend now creates the Plan task and triggers the planner agent
|
|
1586
1519
|
// during project creation (TaskService.CreateProjectAsync), so we no longer
|
|
1587
1520
|
// need to auto-trigger planning from the frontend.
|
|
1588
|
-
const handleCloseWizardTask = useCallback(() => {
|
|
1521
|
+
const handleCloseWizardTask = useCallback((options) => {
|
|
1589
1522
|
const closingProjectId = selectedProjectId;
|
|
1590
1523
|
const closedTaskId = wizardDisplayedTask?.taskId ?? selectedTaskId ?? wizardPinnedTaskId;
|
|
1591
1524
|
clearWizardCloseTransitionTimeout();
|
|
1592
|
-
|
|
1593
|
-
setWizardPinnedTaskId(null);
|
|
1525
|
+
setTaskSelection(null, { pinInWizard: false });
|
|
1594
1526
|
setWizardForceOverview(true);
|
|
1527
|
+
if (!options?.advanceToNext) {
|
|
1528
|
+
return;
|
|
1529
|
+
}
|
|
1595
1530
|
wizardCloseTransitionTimeoutRef.current = window.setTimeout(() => {
|
|
1596
1531
|
wizardCloseTransitionTimeoutRef.current = null;
|
|
1597
1532
|
if (!closingProjectId)
|
|
@@ -1601,8 +1536,10 @@ export function TaskBoardWorkspace() {
|
|
|
1601
1536
|
const nextAttentionTaskId = findNextAttentionTaskId(closedTaskId);
|
|
1602
1537
|
setWizardForceOverview(false);
|
|
1603
1538
|
if (nextAttentionTaskId) {
|
|
1604
|
-
|
|
1605
|
-
|
|
1539
|
+
setTaskSelection(nextAttentionTaskId, {
|
|
1540
|
+
source: "system",
|
|
1541
|
+
pinInWizard: true,
|
|
1542
|
+
});
|
|
1606
1543
|
}
|
|
1607
1544
|
}, 1000);
|
|
1608
1545
|
}, [
|
|
@@ -1612,33 +1549,30 @@ export function TaskBoardWorkspace() {
|
|
|
1612
1549
|
wizardPinnedTaskId,
|
|
1613
1550
|
clearWizardCloseTransitionTimeout,
|
|
1614
1551
|
findNextAttentionTaskId,
|
|
1552
|
+
setTaskSelection,
|
|
1615
1553
|
]);
|
|
1616
|
-
const wizardCommunicationPanel = useMemo(() => (_jsx(WizardCommunicationCenter, { task: wizardDisplayedTask, agentId: wizardDisplayedAgentId, agentStatus:
|
|
1554
|
+
const wizardCommunicationPanel = useMemo(() => (_jsx(WizardCommunicationCenter, { task: wizardDisplayedTask, agentId: wizardDisplayedAgentId, agentStatus: undefined, executionDisplay: wizardDisplayedExecutionDisplay, summaryState: wizardAttentionState.summaryState, stats: wizardAttentionState.stats, projectTitle: selectedProject?.project.title ?? "No project selected", projectDescription: selectedProject?.project.description, taskProjectTitle: wizardDisplayedTask &&
|
|
1617
1555
|
wizardDisplayedTask.projectId !== selectedProjectId
|
|
1618
1556
|
? directSubprojects.find((subproject) => subproject.project.projectId ===
|
|
1619
1557
|
wizardDisplayedTask.projectId)?.project.title
|
|
1620
|
-
: undefined, allTasks: wizardTasksWithDisplayAssignees, dependencies: dependencies, canEdit: canEditTasks,
|
|
1621
|
-
handleCloseWizardTask();
|
|
1558
|
+
: undefined, allTasks: wizardTasksWithDisplayAssignees, dependencies: dependencies, canEdit: canEditTasks, onContentChanged: () => {
|
|
1622
1559
|
void refreshVisibleTaskData();
|
|
1560
|
+
}, onTaskChanged: () => {
|
|
1561
|
+
handleCloseWizardTask({ advanceToNext: true });
|
|
1562
|
+
void refreshVisibleTaskData();
|
|
1563
|
+
}, onTaskApproved: async () => {
|
|
1564
|
+
await refreshVisibleTaskData();
|
|
1565
|
+
handleCloseWizardTask({ advanceToNext: true });
|
|
1623
1566
|
}, onInteractionSubmitted: () => {
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
setAgentStatusesById((prev) => ({
|
|
1627
|
-
...prev,
|
|
1628
|
-
[normalizedId]: "running",
|
|
1629
|
-
}));
|
|
1630
|
-
}
|
|
1631
|
-
handleCloseWizardTask();
|
|
1632
|
-
}, onCloseTask: handleCloseWizardTask, onSelectTask: (taskId) => {
|
|
1567
|
+
handleCloseWizardTask({ advanceToNext: true });
|
|
1568
|
+
}, onCloseTask: () => handleCloseWizardTask(), onSelectTask: (taskId) => {
|
|
1633
1569
|
clearWizardCloseTransitionTimeout();
|
|
1634
1570
|
setWizardForceOverview(false);
|
|
1635
|
-
|
|
1636
|
-
setSelectedTaskId(taskId);
|
|
1571
|
+
setTaskSelection(taskId, { source: "user", pinInWizard: true });
|
|
1637
1572
|
} })), [
|
|
1638
1573
|
wizardAttentionState,
|
|
1639
1574
|
wizardDisplayedTask,
|
|
1640
1575
|
wizardDisplayedAgentId,
|
|
1641
|
-
wizardDisplayedAgentStatus,
|
|
1642
1576
|
wizardDisplayedExecutionDisplay,
|
|
1643
1577
|
canEditTasks,
|
|
1644
1578
|
selectedProjectId,
|
|
@@ -1646,6 +1580,8 @@ export function TaskBoardWorkspace() {
|
|
|
1646
1580
|
refreshVisibleTaskData,
|
|
1647
1581
|
clearWizardCloseTransitionTimeout,
|
|
1648
1582
|
handleCloseWizardTask,
|
|
1583
|
+
selectedTaskId,
|
|
1584
|
+
wizardPinnedTaskId,
|
|
1649
1585
|
selectedProject?.project.title,
|
|
1650
1586
|
selectedProject?.project.description,
|
|
1651
1587
|
]);
|
|
@@ -1654,30 +1590,31 @@ export function TaskBoardWorkspace() {
|
|
|
1654
1590
|
if (isProjectViewLoading) {
|
|
1655
1591
|
return (_jsx("div", { className: "flex h-full min-h-[240px] items-center justify-center rounded-lg border border-dashed border-slate-200 bg-slate-50/70", "data-testid": "taskboard-project-loading-state", children: _jsxs("div", { className: "flex flex-col items-center gap-3 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-6 w-6 animate-spin" }), _jsx("div", { children: "Loading project..." })] }) }));
|
|
1656
1592
|
}
|
|
1657
|
-
if (!selectedProjectId) {
|
|
1593
|
+
if (!selectedProjectId || (!selectedProject && !isProjectsLoading)) {
|
|
1658
1594
|
return (_jsx("div", { className: "text-muted-foreground p-6 text-sm", children: "Select a project to view tasks." }));
|
|
1659
1595
|
}
|
|
1660
1596
|
if (isWizardMode) {
|
|
1661
1597
|
return (_jsx(WizardView, { projectId: selectedProjectId, tasks: tasksWithDisplayAssignees, projectTasksLoading: isTasksLoading, dependencies: dependencies, subprojectTaskLists: wizardSubprojectTaskLists, subprojectTasksLoading: subprojectCountsLoading, selectedTaskId: wizardDisplayedTask?.taskId ?? null, onSelectTask: (id) => {
|
|
1662
1598
|
setWizardForceOverview(false);
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
}, agentStatusesById: agentStatusesById, projectTitle: selectedProject?.project.title ?? "Current project", projectDescription: selectedProject?.project.description, canEditProperties: selectedProject?.permission === "Owner", communicationPanel: wizardCommunicationPanel }));
|
|
1599
|
+
setTaskSelection(id, { source: "user", pinInWizard: true });
|
|
1600
|
+
}, projectTitle: selectedProject?.project.title ?? "No project selected", projectDescription: selectedProject?.project.description, canEditProperties: selectedProject?.permission === "Owner", communicationPanel: wizardCommunicationPanel }));
|
|
1666
1601
|
}
|
|
1667
1602
|
if (isListView) {
|
|
1668
|
-
return (_jsxs("div", { className: "grid gap-3", children: [_jsx(ProjectDashboard, { projectId: selectedProjectId, selectedProjectTitle: selectedProject?.project.title ?? "
|
|
1603
|
+
return (_jsxs("div", { className: "grid gap-3", children: [_jsx(ProjectDashboard, { projectId: selectedProjectId, selectedProjectTitle: selectedProject?.project.title ?? "No project selected", selectedProjectDescription: selectedProject?.project.description, selectedProjectStatus: selectedProject?.project.status, selectedProjectCostUsed: selectedProjectCumulativeCostUsed, selectedProjectCostLimit: selectedProject?.project.costLimit ?? null, canEditProjectStatus: selectedProject?.permission === "Owner", savingProjectStatus: savingProjectStatus, onStatusChange: handleProjectStatusChange, selectedProjectTaskCounts: selectedProjectTaskCounts, subprojects: directSubprojects, taskCountsByProjectId: subprojectTaskCounts, loading: subprojectCountsLoading, parentProjectId: parentProject?.project.projectId ?? null, parentProjectTitle: parentProject?.project.title ?? null, onSelectProject: handleSelectProject, canEditProperties: selectedProject?.permission === "Owner" }), _jsx(ListView, { tasks: tasksWithDisplayAssignees, onSelectTask: (id) => setTaskSelection(id, { source: "user" }), selectedTaskId: selectedTaskId, onTasksChanged: () => {
|
|
1669
1604
|
void refreshVisibleTaskData();
|
|
1670
|
-
}, projectId: selectedProjectId, permission: selectedProject?.permission,
|
|
1605
|
+
}, projectId: selectedProjectId, permission: selectedProject?.permission, activeTab: activeTab, onSetActiveTab: setActiveTab, canCreateTask: canEditTasks, onCreateTask: () => setCreateDialogOpen(true) })] }));
|
|
1671
1606
|
}
|
|
1672
|
-
return (_jsxs("div", { className: "flex h-full min-h-0 flex-col gap-3", children: [_jsx(ProjectDashboard, { projectId: selectedProjectId, selectedProjectTitle: selectedProject?.project.title ?? "
|
|
1607
|
+
return (_jsxs("div", { className: "flex h-full min-h-0 flex-col gap-3", children: [_jsx(ProjectDashboard, { projectId: selectedProjectId, selectedProjectTitle: selectedProject?.project.title ?? "No project selected", selectedProjectDescription: selectedProject?.project.description, selectedProjectStatus: selectedProject?.project.status, selectedProjectCostUsed: selectedProjectCumulativeCostUsed, selectedProjectCostLimit: selectedProject?.project.costLimit ?? null, canEditProjectStatus: selectedProject?.permission === "Owner", savingProjectStatus: savingProjectStatus, onStatusChange: handleProjectStatusChange, selectedProjectTaskCounts: selectedProjectTaskCounts, subprojects: directSubprojects, taskCountsByProjectId: subprojectTaskCounts, loading: subprojectCountsLoading, parentProjectId: parentProject?.project.projectId ?? null, parentProjectTitle: parentProject?.project.title ?? null, onSelectProject: handleSelectProject, canEditProperties: selectedProject?.permission === "Owner" }), _jsx(KanbanView, { tasks: tasksWithDisplayAssignees, dependencies: dependencies, onSelectTask: (id) => setTaskSelection(id, { source: "user" }), selectedTaskId: selectedTaskId, onTasksChanged: () => {
|
|
1673
1608
|
void refreshVisibleTaskData();
|
|
1674
|
-
}, projectId: selectedProjectId, permission: selectedProject?.permission,
|
|
1609
|
+
}, projectId: selectedProjectId, permission: selectedProject?.permission, activeTab: activeTab, onSetActiveTab: setActiveTab, canCreateTask: canEditTasks, onCreateTask: () => setCreateDialogOpen(true) })] }));
|
|
1675
1610
|
}, [
|
|
1676
1611
|
isProjectViewLoading,
|
|
1677
1612
|
selectedProjectId,
|
|
1613
|
+
selectedProject,
|
|
1678
1614
|
isWizardMode,
|
|
1679
1615
|
isListView,
|
|
1680
1616
|
isTasksLoading,
|
|
1617
|
+
isProjectsLoading,
|
|
1681
1618
|
tasksWithDisplayAssignees,
|
|
1682
1619
|
wizardSubprojectTaskLists,
|
|
1683
1620
|
selectedTaskId,
|
|
@@ -1688,7 +1625,6 @@ export function TaskBoardWorkspace() {
|
|
|
1688
1625
|
selectedProject?.project.status,
|
|
1689
1626
|
selectedProject?.project.costLimit,
|
|
1690
1627
|
selectedProjectCumulativeCostUsed,
|
|
1691
|
-
agentStatusesById,
|
|
1692
1628
|
dependencies,
|
|
1693
1629
|
directSubprojects,
|
|
1694
1630
|
parentProject?.project.projectId,
|
|
@@ -1716,14 +1652,13 @@ export function TaskBoardWorkspace() {
|
|
|
1716
1652
|
selectedTask?.assigneeDisplayName,
|
|
1717
1653
|
selectedTask?.assigneeId,
|
|
1718
1654
|
]);
|
|
1719
|
-
const taskDetailPanel = useMemo(() => (_jsx(TaskDetailPanel, { project: selectedTaskProject, task: selectedTask, allTasks: taskSelectionUniverse, dependencies: dependencies,
|
|
1655
|
+
const taskDetailPanel = useMemo(() => (_jsx(TaskDetailPanel, { project: selectedTaskProject, task: selectedTask, allTasks: taskSelectionUniverse, dependencies: dependencies, onTaskChanged: () => {
|
|
1720
1656
|
void refreshVisibleTaskData();
|
|
1721
|
-
}, onSelectTask: (taskId) =>
|
|
1657
|
+
}, onSelectTask: (taskId) => setTaskSelection(taskId, { source: "user" }), onClose: () => setTaskSelection(null), variant: "panel" })), [
|
|
1722
1658
|
selectedTaskProject,
|
|
1723
1659
|
selectedTask,
|
|
1724
1660
|
taskSelectionUniverse,
|
|
1725
1661
|
dependencies,
|
|
1726
|
-
agentStatusesById,
|
|
1727
1662
|
refreshVisibleTaskData,
|
|
1728
1663
|
]);
|
|
1729
1664
|
const agentTerminalPanel = useMemo(() => (_jsx(TaskAgentPanel, { agentId: displayAgentId, mode: agentPanelMode, label: selectedTaskIsPlan ? "Planner" : "Task Agent", hasAssignedAgentProfile: !selectedTaskIsPlan && selectedTaskHasAssignedAgentProfile, assignedAgentProfileName: !selectedTaskIsPlan && selectedTaskHasAssignedAgentProfile
|
|
@@ -1741,7 +1676,7 @@ export function TaskBoardWorkspace() {
|
|
|
1741
1676
|
agentPanelMode === "no-agent" &&
|
|
1742
1677
|
!selectedTaskHasAssignedAgentProfile
|
|
1743
1678
|
? () => setAssignAgentDialogOpen(true)
|
|
1744
|
-
: undefined, assignedAgentStatus:
|
|
1679
|
+
: undefined, assignedAgentStatus: undefined, onReopenTask: selectedTask && canEditTasks
|
|
1745
1680
|
? () => void handleReopenSelectedTask()
|
|
1746
1681
|
: undefined })), [
|
|
1747
1682
|
displayAgentId,
|
|
@@ -1755,11 +1690,9 @@ export function TaskBoardWorkspace() {
|
|
|
1755
1690
|
handleStartPlanning,
|
|
1756
1691
|
selectedTaskIsBlocked,
|
|
1757
1692
|
canEditTasks,
|
|
1758
|
-
currentAgentStatus,
|
|
1759
1693
|
selectedTask,
|
|
1760
1694
|
handleReopenSelectedTask,
|
|
1761
1695
|
]);
|
|
1762
|
-
const slotContext = editContext?.getActiveSlotContext();
|
|
1763
1696
|
const previewItemVersion = useMemo(() => {
|
|
1764
1697
|
if (!currentItemDescriptor)
|
|
1765
1698
|
return null;
|
|
@@ -1917,12 +1850,24 @@ export function TaskBoardWorkspace() {
|
|
|
1917
1850
|
]);
|
|
1918
1851
|
const itemPreviewPanel = useMemo(() => {
|
|
1919
1852
|
const hasCurrentItem = !!currentItemDescriptor;
|
|
1920
|
-
|
|
1921
|
-
|
|
1853
|
+
const previewSidebar = (_jsx("div", { className: "relative z-10 flex h-full min-h-0 flex-col bg-white", children: _jsx("div", { className: "min-h-0 flex-1", children: _jsx(MainContentTree, { mode: "normal" }) }) }));
|
|
1854
|
+
const renderPreviewSplit = (editorContent) => (_jsx(Splitter, { localStorageKey: "taskboard-preview-tree-splitter", panels: [
|
|
1855
|
+
{
|
|
1856
|
+
name: "content-tree",
|
|
1857
|
+
defaultSize: 320,
|
|
1858
|
+
content: previewSidebar,
|
|
1859
|
+
},
|
|
1860
|
+
{
|
|
1861
|
+
name: "editor-preview",
|
|
1862
|
+
defaultSize: "auto",
|
|
1863
|
+
content: (_jsx("div", { className: "min-h-0 min-w-0 h-full overflow-hidden bg-gray-50", children: editorContent })),
|
|
1864
|
+
},
|
|
1865
|
+
], splitterClassName: "bg-gray-200 hover:bg-gray-300 w-[6px]", className: "h-full" }));
|
|
1866
|
+
if (!currentItemDescriptor) {
|
|
1867
|
+
return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsx("div", { className: "border-b border-gray-200 bg-white px-3 py-2", children: hasCurrentItem ? (_jsxs("div", { className: "min-w-0", children: [hasMultipleContextItems ? (_jsx(Select, { value: selectedContextItemValue, onValueChange: handleSelectContextItem, options: contextItemOptions, placeholder: "Select context item", size: "xs", searchable: true, className: "h-7 rounded-md bg-white", maxWidth: 420, "data-testid": "taskboard-context-item-selector" })) : (_jsx("div", { className: "truncate text-sm font-medium text-slate-900", children: previewItemName || "Loading item..." })), _jsxs("div", { className: "mt-0.5 flex items-center gap-2", children: [_jsx("span", { className: "truncate text-xs text-slate-500", children: previewItemPath || "Path unavailable" }), _jsx("span", { className: "shrink-0 rounded bg-slate-100 px-1.5 py-0.5 text-[10px] font-medium text-slate-600", children: selectedContextItemValue.split("|")[1] || "" }), previewItemVersion != null && (_jsxs("span", { className: "shrink-0 rounded bg-slate-100 px-1.5 py-0.5 text-[10px] font-medium text-slate-600", children: ["v", previewItemVersion] }))] })] })) : (_jsx("div", { className: "text-xs text-slate-500", children: "No item loaded" })) }), _jsx("div", { className: "min-h-0 flex-1 overflow-hidden bg-gray-50", children: renderPreviewSplit(_jsxs("div", { className: "flex min-h-0 h-full flex-col items-center justify-center gap-3 p-6 text-center", children: [_jsx("div", { className: "text-muted-foreground text-sm font-medium", children: "Editor preview unavailable" }), _jsx("div", { className: "text-muted-foreground text-xs", children: "Open the editor context item to preview and edit it here." })] })) })] }));
|
|
1922
1868
|
}
|
|
1923
|
-
return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsx("div", { className: "border-b border-gray-200 bg-white px-3 py-2", children: hasCurrentItem ? (_jsxs("div", { className: "min-w-0", children: [hasMultipleContextItems ? (_jsx(Select, { value: selectedContextItemValue, onValueChange: handleSelectContextItem, options: contextItemOptions, placeholder: "Select context item", size: "xs", searchable: true, className: "h-7 rounded-md bg-white", maxWidth: 420, "data-testid": "taskboard-context-item-selector" })) : (_jsx("div", { className: "truncate text-sm font-medium text-slate-900", children: previewItemName || "Loading item..." })), _jsxs("div", { className: "mt-0.5 flex items-center gap-2", children: [_jsx("span", { className: "truncate text-xs text-slate-500", children: previewItemPath || "Path unavailable" }), _jsx("span", { className: "shrink-0 rounded bg-slate-100 px-1.5 py-0.5 text-[10px] font-medium text-slate-600", children: currentItemDescriptor?.language }), previewItemVersion != null && (_jsxs("span", { className: "shrink-0 rounded bg-slate-100 px-1.5 py-0.5 text-[10px] font-medium text-slate-600", children: ["v", previewItemVersion] }))] })] })) : (_jsx("div", { className: "text-xs text-slate-500", children: "No item loaded" })) }), _jsx("div", { className: "min-h-0 flex-1 overflow-hidden bg-gray-50", children:
|
|
1869
|
+
return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsx("div", { className: "border-b border-gray-200 bg-white px-3 py-2", children: hasCurrentItem ? (_jsxs("div", { className: "min-w-0", children: [hasMultipleContextItems ? (_jsx(Select, { value: selectedContextItemValue, onValueChange: handleSelectContextItem, options: contextItemOptions, placeholder: "Select context item", size: "xs", searchable: true, className: "h-7 rounded-md bg-white", maxWidth: 420, "data-testid": "taskboard-context-item-selector" })) : (_jsx("div", { className: "truncate text-sm font-medium text-slate-900", children: previewItemName || "Loading item..." })), _jsxs("div", { className: "mt-0.5 flex items-center gap-2", children: [_jsx("span", { className: "truncate text-xs text-slate-500", children: previewItemPath || "Path unavailable" }), _jsx("span", { className: "shrink-0 rounded bg-slate-100 px-1.5 py-0.5 text-[10px] font-medium text-slate-600", children: currentItemDescriptor?.language }), previewItemVersion != null && (_jsxs("span", { className: "shrink-0 rounded bg-slate-100 px-1.5 py-0.5 text-[10px] font-medium text-slate-600", children: ["v", previewItemVersion] }))] })] })) : (_jsx("div", { className: "text-xs text-slate-500", children: "No item loaded" })) }), _jsx("div", { className: "min-h-0 flex-1 overflow-hidden bg-gray-50", children: renderPreviewSplit(_jsx(TaskboardPreviewEditor, { itemDescriptor: currentItemDescriptor })) })] }));
|
|
1924
1870
|
}, [
|
|
1925
|
-
slotContext,
|
|
1926
1871
|
currentItemDescriptor,
|
|
1927
1872
|
contextItemOptions,
|
|
1928
1873
|
hasMultipleContextItems,
|
|
@@ -2052,6 +1997,7 @@ export function TaskBoardWorkspace() {
|
|
|
2052
1997
|
? "task-board-wizard-splitter-v2"
|
|
2053
1998
|
: "task-board-main-splitter" })) }), selectedProjectId && (_jsx(CreateTaskDialog, { open: createDialogOpen, onOpenChange: setCreateDialogOpen, projectId: selectedProjectId, onCreated: () => {
|
|
2054
1999
|
void refreshTasks(selectedProjectId);
|
|
2000
|
+
void refreshExecutionState(selectedProjectId);
|
|
2055
2001
|
void refreshDependencies(selectedProjectId);
|
|
2056
2002
|
} })), _jsx(CreateProjectDialog, { open: createProjectOpen, onOpenChange: setCreateProjectOpen, projects: projects, onCreated: async ({ projectId: newProjectId, openInWizardMode }) => {
|
|
2057
2003
|
if (openInWizardMode) {
|
|
@@ -2068,6 +2014,7 @@ export function TaskBoardWorkspace() {
|
|
|
2068
2014
|
if (!selectedProjectId)
|
|
2069
2015
|
return;
|
|
2070
2016
|
await refreshTasks(selectedProjectId);
|
|
2017
|
+
await refreshExecutionState(selectedProjectId);
|
|
2071
2018
|
await refreshDependencies(selectedProjectId);
|
|
2072
2019
|
} })] }));
|
|
2073
2020
|
}
|