@parhelia/core 0.1.12468 → 0.1.12477
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/config/config.js +2 -21
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +0 -2
- package/dist/config/types.js.map +1 -1
- package/dist/editor/ConfirmationDialog.js +4 -20
- package/dist/editor/ConfirmationDialog.js.map +1 -1
- package/dist/editor/ContentTree.js +7 -19
- package/dist/editor/ContentTree.js.map +1 -1
- package/dist/editor/Editor.js.map +1 -1
- package/dist/editor/PictureCropper.js +41 -45
- package/dist/editor/PictureCropper.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.js +8 -97
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/AgentTerminalStatusBar.js +0 -65
- package/dist/editor/ai/AgentTerminalStatusBar.js.map +1 -1
- package/dist/editor/ai/Agents.js +0 -19
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.js +0 -5
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/ContentInspectorPopover.d.ts +0 -1
- package/dist/editor/ai/ContentInspectorPopover.js +8 -22
- package/dist/editor/ai/ContentInspectorPopover.js.map +1 -1
- package/dist/editor/ai/ToolCallDisplay.d.ts +0 -2
- package/dist/editor/ai/ToolCallDisplay.js +11 -54
- package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
- package/dist/editor/ai/dialogs/AgentDialogHandler.js +3 -32
- package/dist/editor/ai/dialogs/AgentDialogHandler.js.map +1 -1
- package/dist/editor/ai/dialogs/QuestionnaireInline.js +20 -55
- package/dist/editor/ai/dialogs/QuestionnaireInline.js.map +1 -1
- package/dist/editor/ai/dialogs/agentDialogTypes.d.ts +4 -7
- package/dist/editor/ai/dialogs/agentDialogTypes.js.map +1 -1
- package/dist/editor/ai/types.d.ts +0 -2
- package/dist/editor/services/agentService.d.ts +0 -27
- package/dist/editor/services/agentService.js +2 -11
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.js +3 -54
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/services/serviceHelper.js +2 -5
- package/dist/editor/services/serviceHelper.js.map +1 -1
- package/dist/editor/settings/About.js +4 -40
- package/dist/editor/settings/About.js.map +1 -1
- package/dist/editor/settings/panels/ProjectTemplateAgentPanel.d.ts +1 -7
- package/dist/editor/settings/panels/ProjectTemplateAgentPanel.js +3 -3
- package/dist/editor/settings/panels/ProjectTemplateAgentPanel.js.map +1 -1
- package/dist/editor/settings/panels/ProjectTemplatesPanel.js +84 -165
- package/dist/editor/settings/panels/ProjectTemplatesPanel.js.map +1 -1
- package/dist/editor/settings/panels/index.d.ts +0 -1
- package/dist/editor/settings/panels/index.js +0 -1
- package/dist/editor/settings/panels/index.js.map +1 -1
- package/dist/editor/sidebar/NavigationPanelItem.js +1 -1
- package/dist/editor/sidebar/NavigationPanelItem.js.map +1 -1
- package/dist/editor/sidebar/WorkspaceButton.js +1 -1
- package/dist/editor/sidebar/WorkspaceButton.js.map +1 -1
- package/dist/editor/tree-indicators/GutterColumns.js +4 -24
- package/dist/editor/tree-indicators/GutterColumns.js.map +1 -1
- package/dist/editor/tree-indicators/types.d.ts +0 -10
- package/dist/editor/ui/Splitter.d.ts +0 -1
- package/dist/editor/ui/Splitter.js +1 -7
- package/dist/editor/ui/Splitter.js.map +1 -1
- package/dist/licensing/LicenseContext.js +4 -40
- package/dist/licensing/LicenseContext.js.map +1 -1
- package/dist/licensing/LicenseOverlay.js +1 -1
- package/dist/licensing/LicenseOverlay.js.map +1 -1
- package/dist/licensing/types.d.ts +1 -3
- 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 +354 -165
- package/dist/task-board/TaskBoardWorkspace.js.map +1 -1
- package/dist/task-board/assigneeDisplay.js +3 -1
- package/dist/task-board/assigneeDisplay.js.map +1 -1
- package/dist/task-board/components/CreateProjectDialog.js +1 -2
- package/dist/task-board/components/CreateProjectDialog.js.map +1 -1
- package/dist/task-board/components/ProjectDashboard.js +1 -2
- package/dist/task-board/components/ProjectDashboard.js.map +1 -1
- package/dist/task-board/components/ProjectSettingsDialog.d.ts +0 -1
- package/dist/task-board/components/ProjectSettingsDialog.js +12 -146
- package/dist/task-board/components/ProjectSettingsDialog.js.map +1 -1
- package/dist/task-board/components/TaskAgentPanel.d.ts +0 -1
- package/dist/task-board/components/TaskAgentPanel.js +2 -2
- package/dist/task-board/components/TaskAgentPanel.js.map +1 -1
- package/dist/task-board/components/TaskAssigneePicker.js +2 -2
- package/dist/task-board/components/TaskAssigneePicker.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/TaskDetailDialog.d.ts +0 -1
- package/dist/task-board/components/TaskDetailDialog.js +2 -2
- package/dist/task-board/components/TaskDetailDialog.js.map +1 -1
- package/dist/task-board/components/TaskDetailPanel.d.ts +0 -1
- package/dist/task-board/components/TaskDetailPanel.js +8 -23
- package/dist/task-board/components/TaskDetailPanel.js.map +1 -1
- package/dist/task-board/components/TaskRow.js +2 -3
- package/dist/task-board/components/TaskRow.js.map +1 -1
- package/dist/task-board/components/WizardTaskDetailsPanel.js +1 -2
- package/dist/task-board/components/WizardTaskDetailsPanel.js.map +1 -1
- package/dist/task-board/services/taskService.d.ts +1 -17
- package/dist/task-board/services/taskService.js +0 -56
- package/dist/task-board/services/taskService.js.map +1 -1
- package/dist/task-board/taskExecutionRecords.js +0 -2
- package/dist/task-board/taskExecutionRecords.js.map +1 -1
- package/dist/task-board/types.d.ts +1 -78
- package/dist/task-board/utils/taskDependencyOrdering.d.ts +0 -6
- package/dist/task-board/utils/taskDependencyOrdering.js +1 -138
- package/dist/task-board/utils/taskDependencyOrdering.js.map +1 -1
- package/dist/task-board/views/DependencyGraphView.d.ts +2 -5
- package/dist/task-board/views/DependencyGraphView.js +69 -261
- package/dist/task-board/views/DependencyGraphView.js.map +1 -1
- package/dist/task-board/views/KanbanView.js +1 -8
- package/dist/task-board/views/KanbanView.js.map +1 -1
- package/dist/task-board/views/ListView.js +1 -1
- package/dist/task-board/views/ListView.js.map +1 -1
- package/dist/task-board/views/WizardView.js +24 -30
- package/dist/task-board/views/WizardView.js.map +1 -1
- package/dist/tour/Tour.js +47 -0
- package/dist/tour/Tour.js.map +1 -1
- package/package.json +1 -1
- package/dist/editor/settings/panels/PersistentLogsPanel.d.ts +0 -2
- package/dist/editor/settings/panels/PersistentLogsPanel.js +0 -244
- package/dist/editor/settings/panels/PersistentLogsPanel.js.map +0 -1
- package/dist/task-board/components/ProjectExecutionUserPicker.d.ts +0 -14
- package/dist/task-board/components/ProjectExecutionUserPicker.js +0 -65
- package/dist/task-board/components/ProjectExecutionUserPicker.js.map +0 -1
- package/dist/task-board/components/TaskboardPersistentLogPanel.d.ts +0 -11
- package/dist/task-board/components/TaskboardPersistentLogPanel.js +0 -141
- package/dist/task-board/components/TaskboardPersistentLogPanel.js.map +0 -1
- package/dist/task-board/persistentLogCopy.d.ts +0 -7
- package/dist/task-board/persistentLogCopy.js +0 -80
- package/dist/task-board/persistentLogCopy.js.map +0 -1
- package/dist/task-board/persistentLogLabels.d.ts +0 -38
- package/dist/task-board/persistentLogLabels.js +0 -189
- package/dist/task-board/persistentLogLabels.js.map +0 -1
- package/dist/task-board/useTaskBoardAgentPanelState.d.ts +0 -34
- package/dist/task-board/useTaskBoardAgentPanelState.js +0 -283
- package/dist/task-board/useTaskBoardAgentPanelState.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useEffect, useMemo, useRef
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef } from "react";
|
|
3
3
|
import { ReactFlow, Controls, MiniMap, useNodesState, useEdgesState, Handle, Position, MarkerType, } from "@xyflow/react";
|
|
4
4
|
import Dagre from "@dagrejs/dagre";
|
|
5
5
|
import "@xyflow/react/dist/style.css";
|
|
@@ -10,7 +10,7 @@ import { cn } from "../../lib/utils";
|
|
|
10
10
|
import { saveGraphLayout } from "../services/taskService";
|
|
11
11
|
import { Button } from "../../components/ui/button";
|
|
12
12
|
import { Badge } from "../../components/ui/badge";
|
|
13
|
-
import { Tooltip, TooltipContent, TooltipTrigger
|
|
13
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "../../components/ui/tooltip";
|
|
14
14
|
import { Circle, PlayCircle, Clock3, Eye, CheckCircle2, ArrowUp, ArrowDown, Minus, Flame, GitBranch, RotateCcw, Loader2, Plus, Trash2, } from "lucide-react";
|
|
15
15
|
const NODE_WIDTH = 220;
|
|
16
16
|
const NODE_HEIGHT = 80;
|
|
@@ -46,37 +46,24 @@ function TaskGraphNode({ data }) {
|
|
|
46
46
|
const priorityCfg = data.priority ? PRIORITY_CONFIG[data.priority] : null;
|
|
47
47
|
const PriorityIcon = priorityCfg?.icon;
|
|
48
48
|
const actionButtonClass = "nodrag nopan absolute z-20 flex h-5 w-5 items-center justify-center rounded-full border border-slate-200 bg-white text-slate-500 shadow-sm transition-colors hover:border-blue-200 hover:bg-blue-50 hover:text-blue-600";
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const dependentTooltipSide = data.dependencySourceHandle === "right" ? "right" : "bottom";
|
|
53
|
-
const subtaskButtonClass = data.hierarchySourceHandle === "right"
|
|
54
|
-
? "top-1/2 right-0 translate-x-[calc(100%+6px)] -translate-y-1/2"
|
|
55
|
-
: "bottom-0 left-1/2 -translate-x-1/2 translate-y-[calc(100%+6px)]";
|
|
56
|
-
const subtaskTooltipSide = data.hierarchySourceHandle === "right" ? "right" : "bottom";
|
|
57
|
-
const nodeSurfaceClass = data.isSelected
|
|
58
|
-
? "ring-primary border-primary/50 ring-2 bg-white"
|
|
59
|
-
: data.isBlocked
|
|
60
|
-
? "border-red-200 bg-red-50/70"
|
|
61
|
-
: data.status === "Done"
|
|
62
|
-
? "border-emerald-200 bg-emerald-50/80"
|
|
63
|
-
: "border-slate-200 bg-white hover:border-slate-300 hover:shadow-md";
|
|
64
|
-
return (_jsxs("div", { className: cn("relative cursor-pointer rounded-lg border px-3 py-2 shadow-sm transition-all", nodeSurfaceClass), style: { width: NODE_WIDTH }, "data-testid": `dependency-graph-node-${data.taskId}`, children: [_jsx(Handle, { type: "target", position: Position.Left, id: "left", className: HANDLE_CLASS, "data-testid": `dependency-graph-target-left-${data.taskId}` }), _jsx(Handle, { type: "target", position: Position.Top, id: "top", className: HANDLE_CLASS, "data-testid": `dependency-graph-target-top-${data.taskId}` }), _jsxs("div", { className: "flex items-start justify-between gap-1", children: [_jsxs("div", { className: "flex min-w-0 flex-1 items-center justify-between gap-1", children: [_jsx("span", { className: "truncate text-[10px] font-medium text-slate-400", children: data.taskKey || data.taskId.slice(0, 8).toUpperCase() }), PriorityIcon && (_jsx(PriorityIcon, { className: cn("h-3 w-3 shrink-0", priorityCfg.color) }))] }), data.onRemoveTask && (_jsx("button", { type: "button", className: "-mt-px -mr-0.5 shrink-0 rounded p-0.5 text-slate-400 transition-colors hover:bg-red-50 hover:text-red-600", "aria-label": "Remove task", "data-testid": `dependency-graph-remove-task-${data.taskId}`, onClick: (event) => {
|
|
49
|
+
return (_jsxs("div", { className: cn("relative cursor-pointer rounded-lg border bg-white px-3 py-2 shadow-sm transition-all", data.isSelected
|
|
50
|
+
? "ring-primary border-primary/50 ring-2"
|
|
51
|
+
: "border-slate-200 hover:border-slate-300 hover:shadow-md", data.isBlocked && !data.isSelected && "border-red-200 bg-red-50/40"), style: { width: NODE_WIDTH }, "data-testid": `dependency-graph-node-${data.taskId}`, children: [_jsx(Handle, { type: "target", position: Position.Left, id: "left", className: HANDLE_CLASS, "data-testid": `dependency-graph-target-left-${data.taskId}` }), _jsx(Handle, { type: "target", position: Position.Top, id: "top", className: HANDLE_CLASS, "data-testid": `dependency-graph-target-top-${data.taskId}` }), _jsxs("div", { className: "flex items-start justify-between gap-1", children: [_jsxs("div", { className: "flex min-w-0 flex-1 items-center justify-between gap-1", children: [_jsx("span", { className: "truncate text-[10px] font-medium text-slate-400", children: data.taskKey || data.taskId.slice(0, 8).toUpperCase() }), PriorityIcon && (_jsx(PriorityIcon, { className: cn("h-3 w-3 shrink-0", priorityCfg.color) }))] }), data.onRemoveTask && (_jsx("button", { type: "button", className: "text-slate-400 hover:bg-red-50 hover:text-red-600 -mr-0.5 -mt-px shrink-0 rounded p-0.5 transition-colors", "aria-label": "Remove task", "data-testid": `dependency-graph-remove-task-${data.taskId}`, onClick: (event) => {
|
|
65
52
|
event.stopPropagation();
|
|
66
53
|
data.onRemoveTask?.(data.taskId);
|
|
67
|
-
}, children: _jsx(Trash2, { className: "h-2.5 w-2.5", strokeWidth: 1.5 }) }))] }), _jsx("div", { className: "mt-0.5 truncate text-xs font-medium text-slate-800", children: data.title }), _jsxs("div", { className: "mt-1 flex items-center gap-1.5", children: [executionDisplay ? (_jsxs(Badge, { variant: executionDisplay.variant, className: cn("h-5 max-w-full px-1.5 text-[10px] font-bold tracking-wider
|
|
54
|
+
}, children: _jsx(Trash2, { className: "h-2.5 w-2.5", strokeWidth: 1.5 }) }))] }), _jsx("div", { className: "mt-0.5 truncate text-xs font-medium text-slate-800", children: data.title }), _jsxs("div", { className: "mt-1 flex items-center gap-1.5", children: [executionDisplay ? (_jsxs(Badge, { variant: executionDisplay.variant, className: cn("h-5 max-w-full px-1.5 text-[10px] font-bold uppercase tracking-wider", executionDisplay.className), children: [executionDisplay.showSpinner && (_jsx(Loader2, { className: "mr-1 h-2.5 w-2.5 animate-spin" })), executionDisplay.label] })) : (_jsxs("span", { className: cn("inline-flex items-center gap-0.5 rounded-full px-1.5 py-0 text-[10px] font-medium", statusStyle.bg, statusStyle.text), children: [_jsx(StatusIcon, { className: "h-2.5 w-2.5" }), getTaskStatusLabel(status)] })), data.assigneeDisplayName && (_jsxs("span", { className: "truncate text-[10px] text-slate-400", children: ["@", data.assigneeDisplayName] }))] }), data.onAddDependentTaskFromNode && (_jsxs(Tooltip, { delayDuration: 250, children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", className: cn(actionButtonClass, "top-1/2 right-0 translate-x-[calc(100%+6px)] -translate-y-1/2"), "aria-label": "Add dependent task", "data-testid": `dependency-graph-add-dependent-task-${data.taskId}`, onClick: (event) => {
|
|
68
55
|
event.stopPropagation();
|
|
69
56
|
data.onSelect(data.taskId);
|
|
70
57
|
data.onAddDependentTaskFromNode?.(data.taskId);
|
|
71
58
|
}, onMouseDown: (event) => {
|
|
72
59
|
event.stopPropagation();
|
|
73
|
-
}, children: _jsx(Plus, { className: "h-3 w-3", strokeWidth: 1.75 }) }) }), _jsx(TooltipContent, { side:
|
|
60
|
+
}, children: _jsx(Plus, { className: "h-3 w-3", strokeWidth: 1.75 }) }) }), _jsx(TooltipContent, { side: "right", sideOffset: 8, children: "Add dependent task" })] })), data.onAddChildTaskFromNode && (_jsxs(Tooltip, { delayDuration: 250, children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", className: cn(actionButtonClass, "bottom-0 left-1/2 -translate-x-1/2 translate-y-[calc(100%+6px)]"), "aria-label": "Add subtask", "data-testid": `dependency-graph-add-subtask-${data.taskId}`, onClick: (event) => {
|
|
74
61
|
event.stopPropagation();
|
|
75
62
|
data.onSelect(data.taskId);
|
|
76
63
|
data.onAddChildTaskFromNode?.(data.taskId);
|
|
77
64
|
}, onMouseDown: (event) => {
|
|
78
65
|
event.stopPropagation();
|
|
79
|
-
}, children: _jsx(Plus, { className: "h-3 w-3", strokeWidth: 1.75 }) }) }), _jsx(TooltipContent, { side:
|
|
66
|
+
}, children: _jsx(Plus, { className: "h-3 w-3", strokeWidth: 1.75 }) }) }), _jsx(TooltipContent, { side: "bottom", sideOffset: 8, children: "Add subtask" })] })), _jsx(Handle, { type: "source", position: Position.Right, id: "right", className: HANDLE_CLASS, "data-testid": `dependency-graph-source-right-${data.taskId}` }), _jsx(Handle, { type: "source", position: Position.Bottom, id: "bottom", className: HANDLE_CLASS, "data-testid": `dependency-graph-source-bottom-${data.taskId}` })] }));
|
|
80
67
|
}
|
|
81
68
|
const nodeTypes = {
|
|
82
69
|
taskNode: TaskGraphNode,
|
|
@@ -84,30 +71,13 @@ const nodeTypes = {
|
|
|
84
71
|
const HIERARCHY_CHILD_INDENT = 24;
|
|
85
72
|
const HIERARCHY_CHILD_GAP = 32;
|
|
86
73
|
const HIERARCHY_SUBTREE_GAP = 80;
|
|
87
|
-
function normalizeGraphOrientation(orientation) {
|
|
88
|
-
return orientation === "vertical" ? "vertical" : "horizontal";
|
|
89
|
-
}
|
|
90
|
-
function getDependencyHandles(orientation) {
|
|
91
|
-
return orientation === "horizontal"
|
|
92
|
-
? { source: "right", target: "left" }
|
|
93
|
-
: { source: "bottom", target: "top" };
|
|
94
|
-
}
|
|
95
|
-
function getHierarchyHandles(orientation, autoLayoutStrategy) {
|
|
96
|
-
if (autoLayoutStrategy !== "hierarchy") {
|
|
97
|
-
return getDependencyHandles(orientation);
|
|
98
|
-
}
|
|
99
|
-
return orientation === "horizontal"
|
|
100
|
-
? { source: "bottom", target: "left" }
|
|
101
|
-
: { source: "right", target: "left" };
|
|
102
|
-
}
|
|
103
74
|
/**
|
|
104
75
|
* Compound layout for the hierarchy strategy: each parent–children group is
|
|
105
76
|
* laid out vertically (parent on top, children stacked below with a slight
|
|
106
77
|
* indent), and then the resulting subtree "super-nodes" are arranged
|
|
107
|
-
*
|
|
108
|
-
* them.
|
|
78
|
+
* left-to-right via Dagre using the dependency edges between them.
|
|
109
79
|
*/
|
|
110
|
-
function layoutGraphHierarchy(nodes, edges
|
|
80
|
+
function layoutGraphHierarchy(nodes, edges) {
|
|
111
81
|
if (nodes.length === 0)
|
|
112
82
|
return { nodes, edges };
|
|
113
83
|
const childrenOf = new Map();
|
|
@@ -152,11 +122,7 @@ function layoutGraphHierarchy(nodes, edges, orientation) {
|
|
|
152
122
|
}
|
|
153
123
|
}
|
|
154
124
|
const g = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));
|
|
155
|
-
g.setGraph({
|
|
156
|
-
rankdir: orientation === "horizontal" ? "LR" : "TB",
|
|
157
|
-
nodesep: 40,
|
|
158
|
-
ranksep: HIERARCHY_SUBTREE_GAP,
|
|
159
|
-
});
|
|
125
|
+
g.setGraph({ rankdir: "LR", nodesep: 40, ranksep: HIERARCHY_SUBTREE_GAP });
|
|
160
126
|
for (const [rootId, data] of subtrees) {
|
|
161
127
|
g.setNode(rootId, { width: data.width, height: data.height });
|
|
162
128
|
}
|
|
@@ -198,7 +164,7 @@ function layoutGraphHierarchy(nodes, edges, orientation) {
|
|
|
198
164
|
function layoutGraph(nodes, edges, orientation, autoLayoutStrategy) {
|
|
199
165
|
if (autoLayoutStrategy === "hierarchy" &&
|
|
200
166
|
edges.some((e) => e.data?.relationship === "hierarchy")) {
|
|
201
|
-
return layoutGraphHierarchy(nodes, edges
|
|
167
|
+
return layoutGraphHierarchy(nodes, edges);
|
|
202
168
|
}
|
|
203
169
|
const g = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));
|
|
204
170
|
g.setGraph({
|
|
@@ -233,8 +199,6 @@ function buildGraph(tasks, dependencies, selectedTaskId, selectedDependencyId, b
|
|
|
233
199
|
const childRelationshipKeys = new Set(visibleTasks
|
|
234
200
|
.filter((task) => !!task.parentTaskId)
|
|
235
201
|
.map((task) => `${task.parentTaskId}->${task.taskId}`));
|
|
236
|
-
const dependencyHandles = getDependencyHandles(orientation);
|
|
237
|
-
const hierarchyHandles = getHierarchyHandles(orientation, autoLayoutStrategy);
|
|
238
202
|
const nodes = visibleTasks.map((task) => ({
|
|
239
203
|
id: task.taskId,
|
|
240
204
|
type: "taskNode",
|
|
@@ -251,27 +215,26 @@ function buildGraph(tasks, dependencies, selectedTaskId, selectedDependencyId, b
|
|
|
251
215
|
isSelected: task.taskId === selectedTaskId,
|
|
252
216
|
isBlocked: blockedTaskIds.has(task.taskId),
|
|
253
217
|
orientation,
|
|
254
|
-
dependencySourceHandle: dependencyHandles.source,
|
|
255
|
-
hierarchySourceHandle: hierarchyHandles.source,
|
|
256
218
|
onSelect,
|
|
257
219
|
onAddDependentTaskFromNode,
|
|
258
220
|
onAddChildTaskFromNode,
|
|
259
221
|
onRemoveTask,
|
|
260
222
|
},
|
|
261
223
|
}));
|
|
262
|
-
const depSourceHandle =
|
|
263
|
-
const depTargetHandle =
|
|
264
|
-
const hierSourceHandle =
|
|
265
|
-
const hierTargetHandle =
|
|
224
|
+
const depSourceHandle = orientation === "horizontal" ? "right" : "bottom";
|
|
225
|
+
const depTargetHandle = orientation === "horizontal" ? "left" : "top";
|
|
226
|
+
const hierSourceHandle = autoLayoutStrategy === "hierarchy" ? "bottom" : depSourceHandle;
|
|
227
|
+
const hierTargetHandle = autoLayoutStrategy === "hierarchy" ? "left" : depTargetHandle;
|
|
266
228
|
const dependencyEdges = dependencies
|
|
267
229
|
.filter((dep) => taskMap.has(dep.taskId) &&
|
|
268
230
|
taskMap.has(dep.dependsOnTaskId) &&
|
|
269
231
|
!childRelationshipKeys.has(`${dep.dependsOnTaskId}->${dep.taskId}`))
|
|
270
232
|
.map((dep) => {
|
|
271
233
|
const isBlockedBy = dep.dependencyType === "BlockedBy";
|
|
272
|
-
const strokeColor = isBlockedBy
|
|
273
|
-
|
|
274
|
-
|
|
234
|
+
const strokeColor = isBlockedBy
|
|
235
|
+
? EDGE_BLOCKED_BY
|
|
236
|
+
: EDGE_RELATED;
|
|
237
|
+
const isSelected = dep.taskId === selectedTaskId && dep.dependsOnTaskId === selectedDependencyId;
|
|
275
238
|
return {
|
|
276
239
|
id: dep.dependencyId,
|
|
277
240
|
source: dep.dependsOnTaskId,
|
|
@@ -301,7 +264,8 @@ function buildGraph(tasks, dependencies, selectedTaskId, selectedDependencyId, b
|
|
|
301
264
|
};
|
|
302
265
|
});
|
|
303
266
|
const hierarchyEdges = visibleTasks
|
|
304
|
-
.filter((task) => !!task.parentTaskId &&
|
|
267
|
+
.filter((task) => !!task.parentTaskId &&
|
|
268
|
+
taskMap.has(task.parentTaskId))
|
|
305
269
|
.map((task) => ({
|
|
306
270
|
id: `parent:${task.parentTaskId}:${task.taskId}`,
|
|
307
271
|
source: task.parentTaskId,
|
|
@@ -356,15 +320,12 @@ function buildChildRelationshipKeys(tasks) {
|
|
|
356
320
|
.filter((task) => !!task.parentTaskId)
|
|
357
321
|
.map((task) => `${task.parentTaskId}->${task.taskId}`));
|
|
358
322
|
}
|
|
359
|
-
function isHierarchyConnectionValid(connection, tasks
|
|
360
|
-
if (autoLayoutStrategy !== "hierarchy") {
|
|
361
|
-
return false;
|
|
362
|
-
}
|
|
323
|
+
function isHierarchyConnectionValid(connection, tasks) {
|
|
363
324
|
const { source, sourceHandle, target, targetHandle } = connection;
|
|
364
325
|
if (!source || !target || source === target) {
|
|
365
326
|
return false;
|
|
366
327
|
}
|
|
367
|
-
if (sourceHandle !==
|
|
328
|
+
if (sourceHandle !== "bottom") {
|
|
368
329
|
return false;
|
|
369
330
|
}
|
|
370
331
|
if (targetHandle && targetHandle !== "left" && targetHandle !== "top") {
|
|
@@ -384,12 +345,12 @@ function isHierarchyConnectionValid(connection, tasks, orientation, autoLayoutSt
|
|
|
384
345
|
}
|
|
385
346
|
return !buildChildRelationshipKeys(tasks).has(`${source}->${target}`);
|
|
386
347
|
}
|
|
387
|
-
function isDependencyConnectionValid(connection, tasks, dependencies
|
|
348
|
+
function isDependencyConnectionValid(connection, tasks, dependencies) {
|
|
388
349
|
const { source, sourceHandle, target, targetHandle } = connection;
|
|
389
350
|
if (!source || !target || source === target) {
|
|
390
351
|
return false;
|
|
391
352
|
}
|
|
392
|
-
if (sourceHandle !==
|
|
353
|
+
if (sourceHandle !== "right") {
|
|
393
354
|
return false;
|
|
394
355
|
}
|
|
395
356
|
if (targetHandle && targetHandle !== "left" && targetHandle !== "top") {
|
|
@@ -404,18 +365,15 @@ function isDependencyConnectionValid(connection, tasks, dependencies, orientatio
|
|
|
404
365
|
}
|
|
405
366
|
return !buildChildRelationshipKeys(tasks).has(`${source}->${target}`);
|
|
406
367
|
}
|
|
407
|
-
function isGraphConnectionValid(connection, tasks, dependencies
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
connection.sourceHandle === hierarchySourceHandle) {
|
|
411
|
-
return isHierarchyConnectionValid(connection, tasks, orientation, autoLayoutStrategy);
|
|
368
|
+
function isGraphConnectionValid(connection, tasks, dependencies) {
|
|
369
|
+
if (connection.sourceHandle === "bottom") {
|
|
370
|
+
return isHierarchyConnectionValid(connection, tasks);
|
|
412
371
|
}
|
|
413
|
-
return isDependencyConnectionValid(connection, tasks, dependencies
|
|
372
|
+
return isDependencyConnectionValid(connection, tasks, dependencies);
|
|
414
373
|
}
|
|
415
|
-
function createLayoutSignature(layoutKey, nodes, viewport
|
|
374
|
+
function createLayoutSignature(layoutKey, nodes, viewport) {
|
|
416
375
|
return JSON.stringify({
|
|
417
376
|
layoutKey,
|
|
418
|
-
orientation,
|
|
419
377
|
nodes: nodes
|
|
420
378
|
.map((node) => ({
|
|
421
379
|
taskId: node.id,
|
|
@@ -432,43 +390,12 @@ function createLayoutSignature(layoutKey, nodes, viewport, orientation) {
|
|
|
432
390
|
: null,
|
|
433
391
|
});
|
|
434
392
|
}
|
|
435
|
-
function createGraphSyncSignature(nodes, edges) {
|
|
436
|
-
return JSON.stringify({
|
|
437
|
-
nodes: nodes
|
|
438
|
-
.map((node) => ({
|
|
439
|
-
id: node.id,
|
|
440
|
-
x: node.position.x,
|
|
441
|
-
y: node.position.y,
|
|
442
|
-
selected: node.data.isSelected,
|
|
443
|
-
status: node.data.status,
|
|
444
|
-
executionState: node.data.executionState ?? null,
|
|
445
|
-
blocked: node.data.isBlocked,
|
|
446
|
-
assignee: node.data.assigneeDisplayName ?? null,
|
|
447
|
-
}))
|
|
448
|
-
.sort((a, b) => a.id.localeCompare(b.id)),
|
|
449
|
-
edges: edges
|
|
450
|
-
.map((edge) => ({
|
|
451
|
-
id: edge.id,
|
|
452
|
-
source: edge.source,
|
|
453
|
-
target: edge.target,
|
|
454
|
-
sourceHandle: edge.sourceHandle ?? null,
|
|
455
|
-
targetHandle: edge.targetHandle ?? null,
|
|
456
|
-
}))
|
|
457
|
-
.sort((a, b) => a.id.localeCompare(b.id)),
|
|
458
|
-
});
|
|
459
|
-
}
|
|
460
393
|
export function DependencyGraphView(props) {
|
|
461
|
-
const { projectId, layoutKey, tasks, dependencies, savedLayout, selectedTaskId, selectedDependencyId = null, onSelectTask, onSelectDependency, onClearDependencySelection, permission, canPersistLayout: canPersistLayoutOverride, onPersistLayout, onLayoutSaved, highlightBlockedTasks = true, showExecutionStateBadges = true, emptyStateTitle = "No tasks to visualize", emptyStateDescription = "Create tasks in this project to see them in the dependency graph.", orientation = "vertical", autoLayoutStrategy = "allEdges",
|
|
394
|
+
const { projectId, layoutKey, tasks, dependencies, savedLayout, selectedTaskId, selectedDependencyId = null, onSelectTask, onSelectDependency, onClearDependencySelection, permission, canPersistLayout: canPersistLayoutOverride, onPersistLayout, onLayoutSaved, highlightBlockedTasks = true, showExecutionStateBadges = true, emptyStateTitle = "No tasks to visualize", emptyStateDescription = "Create tasks in this project to see them in the dependency graph.", orientation = "vertical", autoLayoutStrategy = "allEdges", layoutSaveDebounceMs = SAVE_DEBOUNCE_MS, onAddDependentTaskFromNode, onAddChildTaskFromNode, onRemoveTask, allowDependencyConnect = false, onCreateDependency, onCreateChildRelationship, miniMapWidth, miniMapHeight, } = props;
|
|
462
395
|
const canPersistLayout = typeof canPersistLayoutOverride === "boolean"
|
|
463
396
|
? canPersistLayoutOverride
|
|
464
397
|
: permission === "Owner" || permission === "Editor";
|
|
465
398
|
const effectiveLayoutKey = layoutKey ?? projectId ?? "graph";
|
|
466
|
-
const fallbackOrientation = normalizeGraphOrientation(orientation);
|
|
467
|
-
const savedLayoutOrientation = savedLayout
|
|
468
|
-
? normalizeGraphOrientation(savedLayout.orientation ?? fallbackOrientation)
|
|
469
|
-
: null;
|
|
470
|
-
const [transientOrientation, setTransientOrientation] = useState(null);
|
|
471
|
-
const effectiveOrientation = transientOrientation ?? savedLayoutOrientation ?? fallbackOrientation;
|
|
472
399
|
const taskMap = useMemo(() => {
|
|
473
400
|
const m = new Map();
|
|
474
401
|
for (const t of tasks)
|
|
@@ -480,11 +407,6 @@ export function DependencyGraphView(props) {
|
|
|
480
407
|
return new Set();
|
|
481
408
|
}
|
|
482
409
|
const blocked = new Set();
|
|
483
|
-
for (const task of tasks) {
|
|
484
|
-
if ((task.executionBlockedByProjectIds?.length ?? 0) > 0) {
|
|
485
|
-
blocked.add(task.taskId);
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
410
|
for (const dep of dependencies) {
|
|
489
411
|
if (dep.dependencyType !== "BlockedBy")
|
|
490
412
|
continue;
|
|
@@ -497,13 +419,13 @@ export function DependencyGraphView(props) {
|
|
|
497
419
|
return blocked;
|
|
498
420
|
}, [dependencies, highlightBlockedTasks, taskMap]);
|
|
499
421
|
const onSelect = useCallback((taskId) => onSelectTask(taskId), [onSelectTask]);
|
|
500
|
-
const { nodes: initialNodes, edges: initialEdges } = useMemo(() => buildGraph(tasks, dependencies, selectedTaskId, selectedDependencyId, blockedTaskIds,
|
|
422
|
+
const { nodes: initialNodes, edges: initialEdges } = useMemo(() => buildGraph(tasks, dependencies, selectedTaskId, selectedDependencyId, blockedTaskIds, orientation, showExecutionStateBadges, autoLayoutStrategy, onSelect, onAddDependentTaskFromNode, onAddChildTaskFromNode, onRemoveTask), [
|
|
501
423
|
tasks,
|
|
502
424
|
dependencies,
|
|
503
425
|
selectedTaskId,
|
|
504
426
|
selectedDependencyId,
|
|
505
427
|
blockedTaskIds,
|
|
506
|
-
|
|
428
|
+
orientation,
|
|
507
429
|
showExecutionStateBadges,
|
|
508
430
|
autoLayoutStrategy,
|
|
509
431
|
onSelect,
|
|
@@ -514,7 +436,6 @@ export function DependencyGraphView(props) {
|
|
|
514
436
|
const [nodes, setNodes, onNodesChangeBase] = useNodesState(initialNodes);
|
|
515
437
|
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
|
516
438
|
const nodesRef = useRef(nodes);
|
|
517
|
-
const suppressNodeSelectionUntilRef = useRef(0);
|
|
518
439
|
const onNodesChange = useCallback((changes) => {
|
|
519
440
|
onNodesChangeBase(changes);
|
|
520
441
|
const hasPositionChange = changes.some((c) => c.type === "position" && c.position);
|
|
@@ -532,36 +453,22 @@ export function DependencyGraphView(props) {
|
|
|
532
453
|
const pendingViewportRef = useRef(null);
|
|
533
454
|
const fitViewOnNextSyncRef = useRef(false);
|
|
534
455
|
const skipNextMoveEndSaveRef = useRef(false);
|
|
535
|
-
const
|
|
456
|
+
const saveAfterViewportRestoreRef = useRef(false);
|
|
536
457
|
const lastSavedSignatureRef = useRef(null);
|
|
537
458
|
const lastAppliedLayoutKeyRef = useRef(null);
|
|
538
459
|
const lastAppliedLayoutSignatureRef = useRef(null);
|
|
539
|
-
const lastAppliedGraphSignatureRef = useRef(null);
|
|
540
|
-
const lastAppliedNodeIdSignatureRef = useRef(null);
|
|
541
460
|
const savedLayoutSignature = useMemo(() => savedLayout
|
|
542
461
|
? JSON.stringify({
|
|
543
|
-
orientation: savedLayoutOrientation,
|
|
544
462
|
updatedAt: savedLayout.updatedAt ?? null,
|
|
545
463
|
nodes: savedLayout.nodes ?? [],
|
|
546
464
|
viewport: savedLayout.viewport ?? null,
|
|
547
465
|
})
|
|
548
|
-
: "null", [savedLayout
|
|
549
|
-
|
|
550
|
-
setTransientOrientation(null);
|
|
551
|
-
pendingSaveOrientationRef.current = null;
|
|
552
|
-
}, [effectiveLayoutKey]);
|
|
553
|
-
useEffect(() => {
|
|
554
|
-
if (transientOrientation &&
|
|
555
|
-
savedLayoutOrientation &&
|
|
556
|
-
transientOrientation === savedLayoutOrientation) {
|
|
557
|
-
setTransientOrientation(null);
|
|
558
|
-
}
|
|
559
|
-
}, [savedLayoutOrientation, transientOrientation]);
|
|
560
|
-
const persistLayout = useCallback(async (nextNodes, viewport, nextOrientation) => {
|
|
466
|
+
: "null", [savedLayout]);
|
|
467
|
+
const persistLayout = useCallback(async (nextNodes, viewport) => {
|
|
561
468
|
if (!canPersistLayout) {
|
|
562
469
|
return;
|
|
563
470
|
}
|
|
564
|
-
const signature = createLayoutSignature(effectiveLayoutKey, nextNodes, viewport
|
|
471
|
+
const signature = createLayoutSignature(effectiveLayoutKey, nextNodes, viewport);
|
|
565
472
|
if (signature === lastSavedSignatureRef.current) {
|
|
566
473
|
return;
|
|
567
474
|
}
|
|
@@ -572,7 +479,6 @@ export function DependencyGraphView(props) {
|
|
|
572
479
|
x: node.position.x,
|
|
573
480
|
y: node.position.y,
|
|
574
481
|
})),
|
|
575
|
-
orientation: nextOrientation,
|
|
576
482
|
viewport: viewport == null
|
|
577
483
|
? null
|
|
578
484
|
: {
|
|
@@ -589,7 +495,6 @@ export function DependencyGraphView(props) {
|
|
|
589
495
|
projectId,
|
|
590
496
|
nodes: layoutPayload.nodes,
|
|
591
497
|
viewport: layoutPayload.viewport,
|
|
592
|
-
orientation: layoutPayload.orientation,
|
|
593
498
|
});
|
|
594
499
|
if (result.type !== "success") {
|
|
595
500
|
toast.error(result.summary || "Failed to save graph layout");
|
|
@@ -601,7 +506,6 @@ export function DependencyGraphView(props) {
|
|
|
601
506
|
lastSavedSignatureRef.current = signature;
|
|
602
507
|
if (nextLayout) {
|
|
603
508
|
const incomingSignature = JSON.stringify({
|
|
604
|
-
orientation: normalizeGraphOrientation(nextLayout.orientation ?? nextOrientation),
|
|
605
509
|
updatedAt: nextLayout.updatedAt ?? null,
|
|
606
510
|
nodes: nextLayout.nodes ?? [],
|
|
607
511
|
viewport: nextLayout.viewport ?? null,
|
|
@@ -618,16 +522,15 @@ export function DependencyGraphView(props) {
|
|
|
618
522
|
}, [
|
|
619
523
|
canPersistLayout,
|
|
620
524
|
effectiveLayoutKey,
|
|
621
|
-
effectiveOrientation,
|
|
622
525
|
onLayoutSaved,
|
|
623
526
|
onPersistLayout,
|
|
624
527
|
projectId,
|
|
625
528
|
]);
|
|
626
|
-
const queueLayoutSave = useCallback((nextNodes, viewport
|
|
529
|
+
const queueLayoutSave = useCallback((nextNodes, viewport) => {
|
|
627
530
|
if (!canPersistLayout) {
|
|
628
531
|
return;
|
|
629
532
|
}
|
|
630
|
-
const signature = createLayoutSignature(effectiveLayoutKey, nextNodes, viewport
|
|
533
|
+
const signature = createLayoutSignature(effectiveLayoutKey, nextNodes, viewport);
|
|
631
534
|
if (signature === lastSavedSignatureRef.current) {
|
|
632
535
|
return;
|
|
633
536
|
}
|
|
@@ -636,20 +539,14 @@ export function DependencyGraphView(props) {
|
|
|
636
539
|
saveTimeoutRef.current = null;
|
|
637
540
|
}
|
|
638
541
|
if (layoutSaveDebounceMs === 0) {
|
|
639
|
-
void persistLayout(nextNodes, viewport
|
|
542
|
+
void persistLayout(nextNodes, viewport);
|
|
640
543
|
return;
|
|
641
544
|
}
|
|
642
545
|
saveTimeoutRef.current = window.setTimeout(() => {
|
|
643
546
|
saveTimeoutRef.current = null;
|
|
644
|
-
void persistLayout(nextNodes, viewport
|
|
547
|
+
void persistLayout(nextNodes, viewport);
|
|
645
548
|
}, layoutSaveDebounceMs);
|
|
646
|
-
}, [
|
|
647
|
-
canPersistLayout,
|
|
648
|
-
effectiveLayoutKey,
|
|
649
|
-
effectiveOrientation,
|
|
650
|
-
layoutSaveDebounceMs,
|
|
651
|
-
persistLayout,
|
|
652
|
-
]);
|
|
549
|
+
}, [canPersistLayout, effectiveLayoutKey, layoutSaveDebounceMs, persistLayout]);
|
|
653
550
|
const restoreViewport = useCallback(() => {
|
|
654
551
|
const instance = reactFlowRef.current;
|
|
655
552
|
if (!instance || nodesRef.current.length === 0) {
|
|
@@ -661,7 +558,7 @@ export function DependencyGraphView(props) {
|
|
|
661
558
|
skipNextMoveEndSaveRef.current = true;
|
|
662
559
|
window.requestAnimationFrame(() => {
|
|
663
560
|
reactFlowRef.current?.setViewport(viewport, { duration: 0 });
|
|
664
|
-
lastSavedSignatureRef.current = createLayoutSignature(effectiveLayoutKey, nodesRef.current, viewport
|
|
561
|
+
lastSavedSignatureRef.current = createLayoutSignature(effectiveLayoutKey, nodesRef.current, viewport);
|
|
665
562
|
});
|
|
666
563
|
return;
|
|
667
564
|
}
|
|
@@ -672,73 +569,39 @@ export function DependencyGraphView(props) {
|
|
|
672
569
|
skipNextMoveEndSaveRef.current = true;
|
|
673
570
|
window.requestAnimationFrame(() => {
|
|
674
571
|
reactFlowRef.current?.fitView({ padding: 0.2 });
|
|
675
|
-
if (!
|
|
572
|
+
if (!saveAfterViewportRestoreRef.current) {
|
|
676
573
|
return;
|
|
677
574
|
}
|
|
678
|
-
|
|
679
|
-
pendingSaveOrientationRef.current = null;
|
|
575
|
+
saveAfterViewportRestoreRef.current = false;
|
|
680
576
|
const viewport = reactFlowRef.current?.getViewport() ?? null;
|
|
681
|
-
queueLayoutSave(nodesRef.current, viewport
|
|
577
|
+
queueLayoutSave(nodesRef.current, viewport);
|
|
682
578
|
});
|
|
683
|
-
}, [effectiveLayoutKey,
|
|
579
|
+
}, [effectiveLayoutKey, queueLayoutSave]);
|
|
684
580
|
useEffect(() => {
|
|
685
581
|
const shouldApplySavedLayout = lastAppliedLayoutKeyRef.current !== effectiveLayoutKey ||
|
|
686
582
|
lastAppliedLayoutSignatureRef.current !== savedLayoutSignature;
|
|
687
|
-
const nextNodeIdSignature = initialNodes.map((node) => node.id).join("|");
|
|
688
|
-
const savedLayoutNodeCount = savedLayout?.nodes?.length ?? 0;
|
|
689
|
-
const shouldAutoLayoutForHierarchy = autoLayoutStrategy === "hierarchy" &&
|
|
690
|
-
(savedLayoutNodeCount < initialNodes.length ||
|
|
691
|
-
(lastAppliedNodeIdSignatureRef.current !== null &&
|
|
692
|
-
lastAppliedNodeIdSignatureRef.current !== nextNodeIdSignature));
|
|
693
583
|
const nextNodes = shouldApplySavedLayout
|
|
694
|
-
?
|
|
695
|
-
|
|
696
|
-
: applySavedLayout(initialNodes, savedLayout)
|
|
697
|
-
: shouldAutoLayoutForHierarchy
|
|
698
|
-
? initialNodes
|
|
699
|
-
: preserveNodePositions(nodesRef.current, initialNodes);
|
|
700
|
-
const nextGraphSignature = createGraphSyncSignature(nextNodes, initialEdges);
|
|
701
|
-
if (!shouldApplySavedLayout &&
|
|
702
|
-
!shouldAutoLayoutForHierarchy &&
|
|
703
|
-
nextGraphSignature === lastAppliedGraphSignatureRef.current) {
|
|
704
|
-
return;
|
|
705
|
-
}
|
|
584
|
+
? applySavedLayout(initialNodes, savedLayout)
|
|
585
|
+
: preserveNodePositions(nodesRef.current, initialNodes);
|
|
706
586
|
setNodes(nextNodes);
|
|
707
587
|
setEdges(initialEdges);
|
|
708
588
|
nodesRef.current = nextNodes;
|
|
709
|
-
lastAppliedGraphSignatureRef.current = nextGraphSignature;
|
|
710
|
-
lastAppliedNodeIdSignatureRef.current = nextNodeIdSignature;
|
|
711
589
|
if (shouldApplySavedLayout) {
|
|
712
590
|
lastAppliedLayoutKeyRef.current = effectiveLayoutKey;
|
|
713
591
|
lastAppliedLayoutSignatureRef.current = savedLayoutSignature;
|
|
714
|
-
pendingViewportRef.current =
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
shouldAutoLayoutForHierarchy || !savedLayout?.viewport;
|
|
718
|
-
if (!savedLayout?.viewport || shouldAutoLayoutForHierarchy) {
|
|
592
|
+
pendingViewportRef.current = savedLayout?.viewport ?? null;
|
|
593
|
+
fitViewOnNextSyncRef.current = !savedLayout?.viewport;
|
|
594
|
+
if (!savedLayout?.viewport) {
|
|
719
595
|
lastSavedSignatureRef.current = savedLayout?.nodes?.length
|
|
720
|
-
? createLayoutSignature(effectiveLayoutKey, nextNodes, null
|
|
596
|
+
? createLayoutSignature(effectiveLayoutKey, nextNodes, null)
|
|
721
597
|
: null;
|
|
722
598
|
}
|
|
723
|
-
if (shouldAutoLayoutForHierarchy) {
|
|
724
|
-
pendingSaveOrientationRef.current = effectiveOrientation;
|
|
725
|
-
}
|
|
726
|
-
restoreViewport();
|
|
727
|
-
return;
|
|
728
|
-
}
|
|
729
|
-
if (shouldAutoLayoutForHierarchy) {
|
|
730
|
-
pendingViewportRef.current = null;
|
|
731
|
-
fitViewOnNextSyncRef.current = true;
|
|
732
|
-
pendingSaveOrientationRef.current = effectiveOrientation;
|
|
733
599
|
restoreViewport();
|
|
734
600
|
}
|
|
735
601
|
}, [
|
|
736
|
-
autoLayoutStrategy,
|
|
737
602
|
initialEdges,
|
|
738
603
|
initialNodes,
|
|
739
604
|
effectiveLayoutKey,
|
|
740
|
-
effectiveOrientation,
|
|
741
|
-
projectId,
|
|
742
605
|
restoreViewport,
|
|
743
606
|
savedLayout,
|
|
744
607
|
savedLayoutSignature,
|
|
@@ -755,18 +618,18 @@ export function DependencyGraphView(props) {
|
|
|
755
618
|
}, []);
|
|
756
619
|
const hasVisibleNodes = initialNodes.length > 0;
|
|
757
620
|
const handleAutoLayout = useCallback(() => {
|
|
758
|
-
const autoLayout = buildGraph(tasks, dependencies, selectedTaskId, selectedDependencyId, blockedTaskIds,
|
|
621
|
+
const autoLayout = buildGraph(tasks, dependencies, selectedTaskId, selectedDependencyId, blockedTaskIds, orientation, showExecutionStateBadges, autoLayoutStrategy, onSelect, onAddDependentTaskFromNode, onAddChildTaskFromNode, onRemoveTask);
|
|
759
622
|
setNodes(autoLayout.nodes);
|
|
760
623
|
setEdges(autoLayout.edges);
|
|
761
624
|
nodesRef.current = autoLayout.nodes;
|
|
762
625
|
fitViewOnNextSyncRef.current = true;
|
|
763
|
-
|
|
626
|
+
saveAfterViewportRestoreRef.current = true;
|
|
764
627
|
restoreViewport();
|
|
765
628
|
}, [
|
|
766
629
|
blockedTaskIds,
|
|
767
630
|
dependencies,
|
|
768
|
-
effectiveOrientation,
|
|
769
631
|
onSelect,
|
|
632
|
+
orientation,
|
|
770
633
|
showExecutionStateBadges,
|
|
771
634
|
autoLayoutStrategy,
|
|
772
635
|
onRemoveTask,
|
|
@@ -780,14 +643,8 @@ export function DependencyGraphView(props) {
|
|
|
780
643
|
tasks,
|
|
781
644
|
]);
|
|
782
645
|
const isValidConnection = useCallback((connection) => allowDependencyConnect
|
|
783
|
-
? isGraphConnectionValid(connection, tasks, dependencies
|
|
784
|
-
: false, [
|
|
785
|
-
allowDependencyConnect,
|
|
786
|
-
autoLayoutStrategy,
|
|
787
|
-
dependencies,
|
|
788
|
-
effectiveOrientation,
|
|
789
|
-
tasks,
|
|
790
|
-
]);
|
|
646
|
+
? isGraphConnectionValid(connection, tasks, dependencies)
|
|
647
|
+
: false, [allowDependencyConnect, dependencies, tasks]);
|
|
791
648
|
const handleConnect = useCallback((connection) => {
|
|
792
649
|
if (!allowDependencyConnect) {
|
|
793
650
|
return;
|
|
@@ -796,64 +653,24 @@ export function DependencyGraphView(props) {
|
|
|
796
653
|
if (!source || !target) {
|
|
797
654
|
return;
|
|
798
655
|
}
|
|
799
|
-
if (!isGraphConnectionValid(connection, tasks, dependencies
|
|
656
|
+
if (!isGraphConnectionValid(connection, tasks, dependencies)) {
|
|
800
657
|
return;
|
|
801
658
|
}
|
|
802
|
-
|
|
803
|
-
if (
|
|
804
|
-
sourceHandle === getHierarchyHandles(effectiveOrientation, "hierarchy").source) {
|
|
659
|
+
onSelectTask(target);
|
|
660
|
+
if (sourceHandle === "bottom") {
|
|
805
661
|
onCreateChildRelationship?.(source, target);
|
|
662
|
+
return;
|
|
806
663
|
}
|
|
807
|
-
|
|
808
|
-
onCreateDependency?.(source, target);
|
|
809
|
-
}
|
|
810
|
-
window.requestAnimationFrame(() => {
|
|
811
|
-
onSelectTask(target);
|
|
812
|
-
});
|
|
664
|
+
onCreateDependency?.(source, target);
|
|
813
665
|
}, [
|
|
814
666
|
allowDependencyConnect,
|
|
815
|
-
autoLayoutStrategy,
|
|
816
667
|
dependencies,
|
|
817
|
-
effectiveOrientation,
|
|
818
668
|
onCreateChildRelationship,
|
|
819
669
|
onCreateDependency,
|
|
820
670
|
onSelectTask,
|
|
821
671
|
tasks,
|
|
822
672
|
]);
|
|
823
|
-
|
|
824
|
-
if (nextOrientation === effectiveOrientation) {
|
|
825
|
-
return;
|
|
826
|
-
}
|
|
827
|
-
setTransientOrientation(nextOrientation);
|
|
828
|
-
const autoLayout = buildGraph(tasks, dependencies, selectedTaskId, selectedDependencyId, blockedTaskIds, nextOrientation, showExecutionStateBadges, autoLayoutStrategy, onSelect, onAddDependentTaskFromNode, onAddChildTaskFromNode, onRemoveTask);
|
|
829
|
-
setNodes(autoLayout.nodes);
|
|
830
|
-
setEdges(autoLayout.edges);
|
|
831
|
-
nodesRef.current = autoLayout.nodes;
|
|
832
|
-
fitViewOnNextSyncRef.current = true;
|
|
833
|
-
pendingSaveOrientationRef.current = nextOrientation;
|
|
834
|
-
restoreViewport();
|
|
835
|
-
}, [
|
|
836
|
-
autoLayoutStrategy,
|
|
837
|
-
blockedTaskIds,
|
|
838
|
-
dependencies,
|
|
839
|
-
effectiveOrientation,
|
|
840
|
-
onAddChildTaskFromNode,
|
|
841
|
-
onAddDependentTaskFromNode,
|
|
842
|
-
onRemoveTask,
|
|
843
|
-
onSelect,
|
|
844
|
-
restoreViewport,
|
|
845
|
-
selectedDependencyId,
|
|
846
|
-
selectedTaskId,
|
|
847
|
-
setEdges,
|
|
848
|
-
setNodes,
|
|
849
|
-
showExecutionStateBadges,
|
|
850
|
-
tasks,
|
|
851
|
-
]);
|
|
852
|
-
return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsxs("div", { className: "flex shrink-0 items-center justify-between gap-3 px-3 pt-2", children: [hasVisibleNodes && highlightBlockedTasks ? (_jsxs("div", { className: "flex items-center gap-2 text-[11px] text-slate-400", children: [_jsxs("span", { className: "inline-flex items-center gap-1", children: [_jsx("span", { className: "inline-block h-0.5 w-4 rounded-sm bg-orange-400" }), "Blocked by"] }), _jsxs("span", { className: "inline-flex items-center gap-1", children: [_jsx("span", { className: "inline-block h-0.5 w-4 rounded-sm bg-slate-400" }), "Related"] }), _jsxs("span", { className: "inline-flex items-center gap-1", children: [_jsx("span", { className: "inline-block h-0.5 w-4 rounded-sm bg-blue-500" }), "Child task"] })] })) : (_jsx("div", {})), hasVisibleNodes && (_jsxs("div", { className: "flex items-center gap-2", children: [showOrientationToggle && (_jsxs("div", { className: "inline-flex rounded-md border border-slate-200 bg-white p-0.5", "data-testid": "dependency-graph-orientation-toggle", children: [_jsx("button", { type: "button", className: cn("rounded px-2.5 py-1 text-xs font-medium transition-colors", effectiveOrientation === "horizontal"
|
|
853
|
-
? "bg-slate-900 text-white"
|
|
854
|
-
: "text-slate-500 hover:bg-slate-100 hover:text-slate-700"), "data-testid": "dependency-graph-orientation-horizontal", onClick: () => handleOrientationChange("horizontal"), children: "Horizontal" }), _jsx("button", { type: "button", className: cn("rounded px-2.5 py-1 text-xs font-medium transition-colors", effectiveOrientation === "vertical"
|
|
855
|
-
? "bg-slate-900 text-white"
|
|
856
|
-
: "text-slate-500 hover:bg-slate-100 hover:text-slate-700"), "data-testid": "dependency-graph-orientation-vertical", onClick: () => handleOrientationChange("vertical"), children: "Vertical" })] })), canPersistLayout && (_jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "h-8 px-3", onClick: handleAutoLayout, children: [_jsx(RotateCcw, { className: "mr-1.5 h-4 w-4" }), "Auto layout"] }))] }))] }), _jsx("div", { className: "min-h-0 flex-1", children: !hasVisibleNodes ? (_jsx("div", { className: "flex h-full items-center justify-center", children: _jsxs("div", { className: "flex max-w-sm flex-col items-center gap-3 text-center", children: [_jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-slate-100", children: _jsx(GitBranch, { className: "h-6 w-6 text-slate-400" }) }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-slate-600", children: emptyStateTitle }), _jsx("p", { className: "mt-1 text-xs text-slate-400", children: emptyStateDescription })] })] }) })) : (_jsxs(ReactFlow, { nodes: nodes, edges: edges, onNodesChange: onNodesChange, onEdgesChange: onEdgesChange, onConnect: handleConnect, onPaneClick: () => {
|
|
673
|
+
return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsxs("div", { className: "flex shrink-0 items-center justify-between gap-3 px-3 pt-2", children: [hasVisibleNodes && highlightBlockedTasks ? (_jsxs("div", { className: "flex items-center gap-2 text-[11px] text-slate-400", children: [_jsxs("span", { className: "inline-flex items-center gap-1", children: [_jsx("span", { className: "inline-block h-2 w-4 rounded-sm border border-orange-300 bg-orange-400" }), "Blocked by"] }), _jsxs("span", { className: "inline-flex items-center gap-1", children: [_jsx("span", { className: "inline-block h-0.5 w-4 rounded-sm bg-slate-400" }), "Related"] }), _jsxs("span", { className: "inline-flex items-center gap-1", children: [_jsx("span", { className: "inline-block h-0.5 w-4 rounded-sm bg-blue-500" }), "Child task"] })] })) : (_jsx("div", {})), hasVisibleNodes && canPersistLayout && (_jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "h-8 px-3", onClick: handleAutoLayout, children: [_jsx(RotateCcw, { className: "mr-1.5 h-4 w-4" }), "Auto layout"] }))] }), _jsx("div", { className: "min-h-0 flex-1", children: !hasVisibleNodes ? (_jsx("div", { className: "flex h-full items-center justify-center", children: _jsxs("div", { className: "flex max-w-sm flex-col items-center gap-3 text-center", children: [_jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-slate-100", children: _jsx(GitBranch, { className: "h-6 w-6 text-slate-400" }) }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-slate-600", children: emptyStateTitle }), _jsx("p", { className: "mt-1 text-xs text-slate-400", children: emptyStateDescription })] })] }) })) : (_jsxs(ReactFlow, { nodes: nodes, edges: edges, onNodesChange: onNodesChange, onEdgesChange: onEdgesChange, onConnect: handleConnect, onPaneClick: () => {
|
|
857
674
|
onClearDependencySelection?.();
|
|
858
675
|
}, onEdgeClick: (_event, edge) => {
|
|
859
676
|
if (edge.data?.relationship === "dependency" &&
|
|
@@ -862,15 +679,9 @@ export function DependencyGraphView(props) {
|
|
|
862
679
|
onSelectDependency?.(edge.data.taskId, edge.data.dependsOnTaskId);
|
|
863
680
|
}
|
|
864
681
|
}, onNodeClick: (_event, node) => {
|
|
865
|
-
if (performance.now() < suppressNodeSelectionUntilRef.current) {
|
|
866
|
-
return;
|
|
867
|
-
}
|
|
868
682
|
onClearDependencySelection?.();
|
|
869
683
|
onSelectTask(node.id);
|
|
870
684
|
}, onNodeDragStart: (_event, node) => {
|
|
871
|
-
if (performance.now() < suppressNodeSelectionUntilRef.current) {
|
|
872
|
-
return;
|
|
873
|
-
}
|
|
874
685
|
onClearDependencySelection?.();
|
|
875
686
|
onSelectTask(node.id);
|
|
876
687
|
}, onInit: (instance) => {
|
|
@@ -886,16 +697,13 @@ export function DependencyGraphView(props) {
|
|
|
886
697
|
return;
|
|
887
698
|
}
|
|
888
699
|
queueLayoutSave(nodesRef.current, viewport);
|
|
889
|
-
}, nodeTypes: nodeTypes, minZoom: 0.2, maxZoom: 2, proOptions: { hideAttribution: true }, nodesDraggable: canPersistLayout, nodesConnectable: allowDependencyConnect, isValidConnection: isValidConnection, elementsSelectable: true, children: [_jsx(Controls, { showInteractive: false, className: "rounded-lg! border-slate-200! shadow-md!" }),
|
|
700
|
+
}, nodeTypes: nodeTypes, minZoom: 0.2, maxZoom: 2, proOptions: { hideAttribution: true }, nodesDraggable: canPersistLayout, nodesConnectable: allowDependencyConnect, isValidConnection: isValidConnection, elementsSelectable: true, children: [_jsx(Controls, { showInteractive: false, className: "rounded-lg! border-slate-200! shadow-md!" }), _jsx(MiniMap, { ...(miniMapWidth != null && miniMapHeight != null
|
|
890
701
|
? { width: miniMapWidth, height: miniMapHeight }
|
|
891
702
|
: {}), nodeColor: (node) => {
|
|
892
|
-
const
|
|
893
|
-
if (d?.isBlocked)
|
|
894
|
-
return "#fecaca";
|
|
895
|
-
const status = d?.status;
|
|
703
|
+
const status = node.data?.status;
|
|
896
704
|
switch (status) {
|
|
897
705
|
case "Done":
|
|
898
|
-
return "#
|
|
706
|
+
return "#86efac";
|
|
899
707
|
case "InProgress":
|
|
900
708
|
return "#93c5fd";
|
|
901
709
|
case "Review":
|
|
@@ -905,6 +713,6 @@ export function DependencyGraphView(props) {
|
|
|
905
713
|
default:
|
|
906
714
|
return "#cbd5e1";
|
|
907
715
|
}
|
|
908
|
-
}, maskColor: "rgba(0, 0, 0, 0.08)", className: "rounded-lg! border-slate-200! shadow-md!" })
|
|
716
|
+
}, maskColor: "rgba(0, 0, 0, 0.08)", className: "rounded-lg! border-slate-200! shadow-md!" })] })) })] }));
|
|
909
717
|
}
|
|
910
718
|
//# sourceMappingURL=DependencyGraphView.js.map
|