@goplasmatic/dataflow-ui 2.0.4 → 2.0.5
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/README.md +3 -1
- package/dist/index.cjs +258 -48
- package/dist/index.d.ts +2 -0
- package/dist/index.js +258 -48
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -17,10 +17,12 @@ A React component library for visualizing and debugging [dataflow-rs](https://gi
|
|
|
17
17
|
## Features
|
|
18
18
|
|
|
19
19
|
- **Workflow Visualization** - Interactive tree view of workflows, tasks, and conditions
|
|
20
|
-
- **Execution Debugging** - Step-by-step execution trace visualization with
|
|
20
|
+
- **Execution Debugging** - Step-by-step execution trace visualization with message snapshots
|
|
21
21
|
- **JSONLogic Viewer** - Visual representation of JSONLogic expressions via [@goplasmatic/datalogic-ui](https://www.npmjs.com/package/@goplasmatic/datalogic-ui)
|
|
22
22
|
- **Theme Support** - Light, dark, and system theme modes
|
|
23
23
|
- **TypeScript** - Full type definitions included
|
|
24
|
+
- **Monaco Editor Integration** - JSON editing with syntax highlighting
|
|
25
|
+
- **Change Highlighting** - Visual diff of message changes at each step
|
|
24
26
|
|
|
25
27
|
## Installation
|
|
26
28
|
|
package/dist/index.cjs
CHANGED
|
@@ -243,6 +243,21 @@ const FileJson = createLucideIcon("FileJson", [
|
|
|
243
243
|
{ d: "M14 18a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1 1 1 0 0 1-1-1v-1a1 1 0 0 0-1-1", key: "mpwhp6" }
|
|
244
244
|
]
|
|
245
245
|
]);
|
|
246
|
+
/**
|
|
247
|
+
* @license lucide-react v0.462.0 - ISC
|
|
248
|
+
*
|
|
249
|
+
* This source code is licensed under the ISC license.
|
|
250
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
251
|
+
*/
|
|
252
|
+
const Folder = createLucideIcon("Folder", [
|
|
253
|
+
[
|
|
254
|
+
"path",
|
|
255
|
+
{
|
|
256
|
+
d: "M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z",
|
|
257
|
+
key: "1kt360"
|
|
258
|
+
}
|
|
259
|
+
]
|
|
260
|
+
]);
|
|
246
261
|
/**
|
|
247
262
|
* @license lucide-react v0.462.0 - ISC
|
|
248
263
|
*
|
|
@@ -19854,31 +19869,38 @@ function TreeNode({
|
|
|
19854
19869
|
};
|
|
19855
19870
|
const debugStateClass = debugState ? `df-tree-node-${debugState}` : "";
|
|
19856
19871
|
const currentClass = isCurrent ? "df-tree-node-current-step" : "";
|
|
19857
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
19858
|
-
|
|
19859
|
-
|
|
19860
|
-
{
|
|
19861
|
-
|
|
19862
|
-
|
|
19863
|
-
|
|
19864
|
-
|
|
19865
|
-
|
|
19866
|
-
"
|
|
19867
|
-
{
|
|
19868
|
-
|
|
19869
|
-
|
|
19870
|
-
|
|
19871
|
-
|
|
19872
|
-
|
|
19873
|
-
|
|
19874
|
-
|
|
19875
|
-
|
|
19876
|
-
|
|
19877
|
-
|
|
19878
|
-
|
|
19879
|
-
|
|
19880
|
-
|
|
19881
|
-
|
|
19872
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
19873
|
+
"div",
|
|
19874
|
+
{
|
|
19875
|
+
className: `df-tree-node ${debugStateClass} ${currentClass}`,
|
|
19876
|
+
"data-current-step": isCurrent ? "true" : void 0,
|
|
19877
|
+
children: [
|
|
19878
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
19879
|
+
"div",
|
|
19880
|
+
{
|
|
19881
|
+
className: `df-tree-node-content ${isSelected ? "df-tree-node-selected" : ""}`,
|
|
19882
|
+
style: { paddingLeft: `${level * 16 + 8}px` },
|
|
19883
|
+
onClick: handleClick,
|
|
19884
|
+
children: [
|
|
19885
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
19886
|
+
"span",
|
|
19887
|
+
{
|
|
19888
|
+
className: "df-tree-toggle",
|
|
19889
|
+
onClick: hasChildren ? handleToggle : void 0,
|
|
19890
|
+
style: { visibility: hasChildren ? "visible" : "hidden" },
|
|
19891
|
+
children: isExpanded ? /* @__PURE__ */ jsxRuntime.jsx(ChevronDown, { size: 14 }) : /* @__PURE__ */ jsxRuntime.jsx(ChevronRight, { size: 14 })
|
|
19892
|
+
}
|
|
19893
|
+
),
|
|
19894
|
+
icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "df-tree-icon", style: iconColor ? { color: iconColor } : void 0, children: icon }),
|
|
19895
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "df-tree-label", children: label }),
|
|
19896
|
+
debugState && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "df-tree-debug-indicator", children: /* @__PURE__ */ jsxRuntime.jsx(DebugStateIcon, { state: debugState, conditionResult }) })
|
|
19897
|
+
]
|
|
19898
|
+
}
|
|
19899
|
+
),
|
|
19900
|
+
isExpanded && hasChildren && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "df-tree-children", children: children2 })
|
|
19901
|
+
]
|
|
19902
|
+
}
|
|
19903
|
+
);
|
|
19882
19904
|
}
|
|
19883
19905
|
const TREE_COLORS = {
|
|
19884
19906
|
workflow: "#0078d4",
|
|
@@ -19891,8 +19913,10 @@ const TREE_COLORS = {
|
|
|
19891
19913
|
// VSCode teal/green
|
|
19892
19914
|
validation: "#ce9178",
|
|
19893
19915
|
// VSCode orange
|
|
19894
|
-
tasks: "#9d9d9d"
|
|
19916
|
+
tasks: "#9d9d9d",
|
|
19895
19917
|
// VSCode gray
|
|
19918
|
+
folder: "#dcb67a"
|
|
19919
|
+
// VSCode folder yellow/gold
|
|
19896
19920
|
};
|
|
19897
19921
|
function TaskNode({
|
|
19898
19922
|
task,
|
|
@@ -20091,20 +20115,177 @@ function WorkflowNode({
|
|
|
20091
20115
|
}
|
|
20092
20116
|
);
|
|
20093
20117
|
}
|
|
20118
|
+
function FolderNode({
|
|
20119
|
+
folder,
|
|
20120
|
+
level,
|
|
20121
|
+
selection: selection2,
|
|
20122
|
+
onSelect,
|
|
20123
|
+
expandedNodes,
|
|
20124
|
+
toggleNode,
|
|
20125
|
+
debugMode = false
|
|
20126
|
+
}) {
|
|
20127
|
+
const folderId = `folder-${folder.fullPath}`;
|
|
20128
|
+
const isExpanded = expandedNodes.has(folderId);
|
|
20129
|
+
const hasChildren = folder.folders.size > 0 || folder.workflows.length > 0;
|
|
20130
|
+
const sortedFolders = Array.from(folder.folders.values()).sort(
|
|
20131
|
+
(a, b) => a.name.localeCompare(b.name)
|
|
20132
|
+
);
|
|
20133
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
20134
|
+
TreeNode,
|
|
20135
|
+
{
|
|
20136
|
+
label: `${folder.name} (${folder.totalWorkflowCount})`,
|
|
20137
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(Folder, { size: 14 }),
|
|
20138
|
+
iconColor: TREE_COLORS.folder,
|
|
20139
|
+
isExpanded,
|
|
20140
|
+
hasChildren,
|
|
20141
|
+
level,
|
|
20142
|
+
onToggle: () => toggleNode(folderId),
|
|
20143
|
+
onClick: () => toggleNode(folderId),
|
|
20144
|
+
children: [
|
|
20145
|
+
sortedFolders.map((childFolder) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
20146
|
+
FolderNode,
|
|
20147
|
+
{
|
|
20148
|
+
folder: childFolder,
|
|
20149
|
+
level: level + 1,
|
|
20150
|
+
selection: selection2,
|
|
20151
|
+
onSelect,
|
|
20152
|
+
expandedNodes,
|
|
20153
|
+
toggleNode,
|
|
20154
|
+
debugMode
|
|
20155
|
+
},
|
|
20156
|
+
childFolder.fullPath
|
|
20157
|
+
)),
|
|
20158
|
+
folder.workflows.map((workflow) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
20159
|
+
WorkflowNode,
|
|
20160
|
+
{
|
|
20161
|
+
workflow,
|
|
20162
|
+
level: level + 1,
|
|
20163
|
+
selection: selection2,
|
|
20164
|
+
onSelect,
|
|
20165
|
+
expandedNodes,
|
|
20166
|
+
toggleNode,
|
|
20167
|
+
debugMode
|
|
20168
|
+
},
|
|
20169
|
+
workflow.id
|
|
20170
|
+
))
|
|
20171
|
+
]
|
|
20172
|
+
}
|
|
20173
|
+
);
|
|
20174
|
+
}
|
|
20175
|
+
function parsePath(path) {
|
|
20176
|
+
if (!path) return [];
|
|
20177
|
+
const trimmed = path.replace(/^\/+|\/+$/g, "");
|
|
20178
|
+
if (!trimmed) return [];
|
|
20179
|
+
return trimmed.split("/").filter((segment) => segment.length > 0);
|
|
20180
|
+
}
|
|
20181
|
+
function createFolderNode(name, fullPath) {
|
|
20182
|
+
return {
|
|
20183
|
+
name,
|
|
20184
|
+
fullPath,
|
|
20185
|
+
folders: /* @__PURE__ */ new Map(),
|
|
20186
|
+
workflows: [],
|
|
20187
|
+
totalWorkflowCount: 0
|
|
20188
|
+
};
|
|
20189
|
+
}
|
|
20190
|
+
function calculateTotalCount(node) {
|
|
20191
|
+
let count = node.workflows.length;
|
|
20192
|
+
for (const child of node.folders.values()) {
|
|
20193
|
+
count += calculateTotalCount(child);
|
|
20194
|
+
}
|
|
20195
|
+
node.totalWorkflowCount = count;
|
|
20196
|
+
return count;
|
|
20197
|
+
}
|
|
20198
|
+
function buildFolderTree(workflows) {
|
|
20199
|
+
const tree = {
|
|
20200
|
+
folders: /* @__PURE__ */ new Map(),
|
|
20201
|
+
workflows: []
|
|
20202
|
+
};
|
|
20203
|
+
const sortedWorkflows = [...workflows].sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
|
|
20204
|
+
for (const workflow of sortedWorkflows) {
|
|
20205
|
+
const segments = parsePath(workflow.path);
|
|
20206
|
+
if (segments.length === 0) {
|
|
20207
|
+
tree.workflows.push(workflow);
|
|
20208
|
+
} else {
|
|
20209
|
+
let currentLevel = tree.folders;
|
|
20210
|
+
let currentPath = "";
|
|
20211
|
+
for (let i = 0; i < segments.length; i++) {
|
|
20212
|
+
const segment = segments[i];
|
|
20213
|
+
currentPath = currentPath ? `${currentPath}/${segment}` : segment;
|
|
20214
|
+
if (!currentLevel.has(segment)) {
|
|
20215
|
+
currentLevel.set(segment, createFolderNode(segment, currentPath));
|
|
20216
|
+
}
|
|
20217
|
+
const folder = currentLevel.get(segment);
|
|
20218
|
+
if (i === segments.length - 1) {
|
|
20219
|
+
folder.workflows.push(workflow);
|
|
20220
|
+
} else {
|
|
20221
|
+
currentLevel = folder.folders;
|
|
20222
|
+
}
|
|
20223
|
+
}
|
|
20224
|
+
}
|
|
20225
|
+
}
|
|
20226
|
+
for (const folder of tree.folders.values()) {
|
|
20227
|
+
calculateTotalCount(folder);
|
|
20228
|
+
}
|
|
20229
|
+
return tree;
|
|
20230
|
+
}
|
|
20231
|
+
function getFirstLevelFolderIds(tree) {
|
|
20232
|
+
return Array.from(tree.folders.keys()).map((name) => `folder-${name}`);
|
|
20233
|
+
}
|
|
20234
|
+
function getParentFolderIds(path) {
|
|
20235
|
+
const segments = parsePath(path);
|
|
20236
|
+
if (segments.length === 0) return [];
|
|
20237
|
+
const ids = [];
|
|
20238
|
+
let currentPath = "";
|
|
20239
|
+
for (const segment of segments) {
|
|
20240
|
+
currentPath = currentPath ? `${currentPath}/${segment}` : segment;
|
|
20241
|
+
ids.push(`folder-${currentPath}`);
|
|
20242
|
+
}
|
|
20243
|
+
return ids;
|
|
20244
|
+
}
|
|
20094
20245
|
function TreeView({ workflows, selection: selection2, onSelect, debugMode = false }) {
|
|
20095
20246
|
const debuggerContext = useDebugger();
|
|
20096
20247
|
const effectiveDebugContext = debugMode ? debuggerContext : null;
|
|
20248
|
+
const folderTree = require$$0.useMemo(() => buildFolderTree(workflows), [workflows]);
|
|
20249
|
+
const sortedRootFolders = require$$0.useMemo(() => {
|
|
20250
|
+
return Array.from(folderTree.folders.values()).sort(
|
|
20251
|
+
(a, b) => a.name.localeCompare(b.name)
|
|
20252
|
+
);
|
|
20253
|
+
}, [folderTree]);
|
|
20254
|
+
const rootWorkflows = folderTree.workflows;
|
|
20097
20255
|
const [expandedNodes, setExpandedNodes] = require$$0.useState(() => {
|
|
20098
20256
|
const initial = /* @__PURE__ */ new Set(["workflows-root"]);
|
|
20099
|
-
|
|
20100
|
-
initial.add(`workflow-${workflows[0].id}`);
|
|
20101
|
-
}
|
|
20257
|
+
getFirstLevelFolderIds(folderTree).forEach((id2) => initial.add(id2));
|
|
20102
20258
|
return initial;
|
|
20103
20259
|
});
|
|
20104
|
-
|
|
20105
|
-
|
|
20106
|
-
|
|
20260
|
+
require$$0.useEffect(() => {
|
|
20261
|
+
setExpandedNodes((prev) => {
|
|
20262
|
+
const next = new Set(prev);
|
|
20263
|
+
next.add("workflows-root");
|
|
20264
|
+
getFirstLevelFolderIds(folderTree).forEach((id2) => next.add(id2));
|
|
20265
|
+
return next;
|
|
20266
|
+
});
|
|
20267
|
+
}, [folderTree]);
|
|
20268
|
+
require$$0.useEffect(() => {
|
|
20269
|
+
const allWorkflows = [...folderTree.workflows];
|
|
20270
|
+
function collectWorkflows(folders) {
|
|
20271
|
+
for (const folder of folders.values()) {
|
|
20272
|
+
allWorkflows.push(...folder.workflows);
|
|
20273
|
+
collectWorkflows(folder.folders);
|
|
20274
|
+
}
|
|
20275
|
+
}
|
|
20276
|
+
collectWorkflows(folderTree.folders);
|
|
20277
|
+
if (allWorkflows.length > 0) {
|
|
20278
|
+
allWorkflows.sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
|
|
20279
|
+
setExpandedNodes((prev) => {
|
|
20280
|
+
const next = new Set(prev);
|
|
20281
|
+
next.add(`workflow-${allWorkflows[0].id}`);
|
|
20282
|
+
getParentFolderIds(allWorkflows[0].path).forEach((id2) => next.add(id2));
|
|
20283
|
+
return next;
|
|
20284
|
+
});
|
|
20285
|
+
}
|
|
20286
|
+
}, [folderTree]);
|
|
20107
20287
|
const lastSelectedRef = require$$0.useRef(null);
|
|
20288
|
+
const treeContainerRef = require$$0.useRef(null);
|
|
20108
20289
|
require$$0.useEffect(() => {
|
|
20109
20290
|
var _a, _b;
|
|
20110
20291
|
if (!debugMode || !(effectiveDebugContext == null ? void 0 : effectiveDebugContext.currentStep) || effectiveDebugContext.state.currentStepIndex < 0) {
|
|
@@ -20114,9 +20295,13 @@ function TreeView({ workflows, selection: selection2, onSelect, debugMode = fals
|
|
|
20114
20295
|
if (((_a = lastSelectedRef.current) == null ? void 0 : _a.workflowId) === workflow_id && ((_b = lastSelectedRef.current) == null ? void 0 : _b.taskId) === task_id) {
|
|
20115
20296
|
return;
|
|
20116
20297
|
}
|
|
20298
|
+
const workflow = workflows.find((w) => w.id === workflow_id);
|
|
20117
20299
|
setExpandedNodes((prev) => {
|
|
20118
20300
|
const next = new Set(prev);
|
|
20119
20301
|
next.add("workflows-root");
|
|
20302
|
+
if (workflow == null ? void 0 : workflow.path) {
|
|
20303
|
+
getParentFolderIds(workflow.path).forEach((id2) => next.add(id2));
|
|
20304
|
+
}
|
|
20120
20305
|
next.add(`workflow-${workflow_id}`);
|
|
20121
20306
|
next.add(`tasks-${workflow_id}`);
|
|
20122
20307
|
if (task_id) {
|
|
@@ -20125,7 +20310,6 @@ function TreeView({ workflows, selection: selection2, onSelect, debugMode = fals
|
|
|
20125
20310
|
return next;
|
|
20126
20311
|
});
|
|
20127
20312
|
if (task_id) {
|
|
20128
|
-
const workflow = workflows.find((w) => w.id === workflow_id);
|
|
20129
20313
|
const task = workflow == null ? void 0 : workflow.tasks.find((t) => t.id === task_id);
|
|
20130
20314
|
if (workflow && task) {
|
|
20131
20315
|
lastSelectedRef.current = { workflowId: workflow_id, taskId: task_id };
|
|
@@ -20134,6 +20318,16 @@ function TreeView({ workflows, selection: selection2, onSelect, debugMode = fals
|
|
|
20134
20318
|
} else {
|
|
20135
20319
|
lastSelectedRef.current = { workflowId: workflow_id };
|
|
20136
20320
|
}
|
|
20321
|
+
setTimeout(() => {
|
|
20322
|
+
var _a2;
|
|
20323
|
+
const currentStepElement = (_a2 = treeContainerRef.current) == null ? void 0 : _a2.querySelector('[data-current-step="true"]');
|
|
20324
|
+
if (currentStepElement) {
|
|
20325
|
+
currentStepElement.scrollIntoView({
|
|
20326
|
+
behavior: "smooth",
|
|
20327
|
+
block: "nearest"
|
|
20328
|
+
});
|
|
20329
|
+
}
|
|
20330
|
+
}, 50);
|
|
20137
20331
|
}, [debugMode, effectiveDebugContext == null ? void 0 : effectiveDebugContext.currentStep, effectiveDebugContext == null ? void 0 : effectiveDebugContext.state.currentStepIndex, workflows, onSelect]);
|
|
20138
20332
|
const toggleNode = (id2) => {
|
|
20139
20333
|
setExpandedNodes((prev) => {
|
|
@@ -20147,30 +20341,46 @@ function TreeView({ workflows, selection: selection2, onSelect, debugMode = fals
|
|
|
20147
20341
|
});
|
|
20148
20342
|
};
|
|
20149
20343
|
const isRootExpanded = expandedNodes.has("workflows-root");
|
|
20150
|
-
|
|
20344
|
+
const totalWorkflowCount = workflows.length;
|
|
20345
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: treeContainerRef, className: `df-tree-view ${debugMode ? "df-tree-view-debug" : ""}`, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
20151
20346
|
TreeNode,
|
|
20152
20347
|
{
|
|
20153
20348
|
label: "Workflows",
|
|
20154
20349
|
icon: /* @__PURE__ */ jsxRuntime.jsx(Layers, { size: 14 }),
|
|
20155
20350
|
iconColor: TREE_COLORS.workflow,
|
|
20156
20351
|
isExpanded: isRootExpanded,
|
|
20157
|
-
hasChildren:
|
|
20352
|
+
hasChildren: totalWorkflowCount > 0,
|
|
20158
20353
|
level: 0,
|
|
20159
20354
|
onToggle: () => toggleNode("workflows-root"),
|
|
20160
20355
|
onClick: () => toggleNode("workflows-root"),
|
|
20161
|
-
children:
|
|
20162
|
-
|
|
20163
|
-
|
|
20164
|
-
|
|
20165
|
-
|
|
20166
|
-
|
|
20167
|
-
|
|
20168
|
-
|
|
20169
|
-
|
|
20170
|
-
|
|
20171
|
-
|
|
20172
|
-
|
|
20173
|
-
|
|
20356
|
+
children: [
|
|
20357
|
+
sortedRootFolders.map((folder) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
20358
|
+
FolderNode,
|
|
20359
|
+
{
|
|
20360
|
+
folder,
|
|
20361
|
+
level: 1,
|
|
20362
|
+
selection: selection2,
|
|
20363
|
+
onSelect,
|
|
20364
|
+
expandedNodes,
|
|
20365
|
+
toggleNode,
|
|
20366
|
+
debugMode
|
|
20367
|
+
},
|
|
20368
|
+
folder.fullPath
|
|
20369
|
+
)),
|
|
20370
|
+
rootWorkflows.map((workflow) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
20371
|
+
WorkflowNode,
|
|
20372
|
+
{
|
|
20373
|
+
workflow,
|
|
20374
|
+
level: 1,
|
|
20375
|
+
selection: selection2,
|
|
20376
|
+
onSelect,
|
|
20377
|
+
expandedNodes,
|
|
20378
|
+
toggleNode,
|
|
20379
|
+
debugMode
|
|
20380
|
+
},
|
|
20381
|
+
workflow.id
|
|
20382
|
+
))
|
|
20383
|
+
]
|
|
20174
20384
|
}
|
|
20175
20385
|
) });
|
|
20176
20386
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -670,6 +670,8 @@ export declare interface Workflow {
|
|
|
670
670
|
priority?: number;
|
|
671
671
|
/** Optional description */
|
|
672
672
|
description?: string;
|
|
673
|
+
/** Optional folder path for grouping (e.g., "orders/processing") */
|
|
674
|
+
path?: string;
|
|
673
675
|
/** JSONLogic condition (evaluated against metadata only) */
|
|
674
676
|
condition?: JsonLogicValue;
|
|
675
677
|
/** Tasks in this workflow */
|
package/dist/index.js
CHANGED
|
@@ -241,6 +241,21 @@ const FileJson = createLucideIcon("FileJson", [
|
|
|
241
241
|
{ d: "M14 18a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1 1 1 0 0 1-1-1v-1a1 1 0 0 0-1-1", key: "mpwhp6" }
|
|
242
242
|
]
|
|
243
243
|
]);
|
|
244
|
+
/**
|
|
245
|
+
* @license lucide-react v0.462.0 - ISC
|
|
246
|
+
*
|
|
247
|
+
* This source code is licensed under the ISC license.
|
|
248
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
249
|
+
*/
|
|
250
|
+
const Folder = createLucideIcon("Folder", [
|
|
251
|
+
[
|
|
252
|
+
"path",
|
|
253
|
+
{
|
|
254
|
+
d: "M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z",
|
|
255
|
+
key: "1kt360"
|
|
256
|
+
}
|
|
257
|
+
]
|
|
258
|
+
]);
|
|
244
259
|
/**
|
|
245
260
|
* @license lucide-react v0.462.0 - ISC
|
|
246
261
|
*
|
|
@@ -19852,31 +19867,38 @@ function TreeNode({
|
|
|
19852
19867
|
};
|
|
19853
19868
|
const debugStateClass = debugState ? `df-tree-node-${debugState}` : "";
|
|
19854
19869
|
const currentClass = isCurrent ? "df-tree-node-current-step" : "";
|
|
19855
|
-
return /* @__PURE__ */ jsxs(
|
|
19856
|
-
|
|
19857
|
-
|
|
19858
|
-
{
|
|
19859
|
-
|
|
19860
|
-
|
|
19861
|
-
|
|
19862
|
-
|
|
19863
|
-
|
|
19864
|
-
"
|
|
19865
|
-
{
|
|
19866
|
-
|
|
19867
|
-
|
|
19868
|
-
|
|
19869
|
-
|
|
19870
|
-
|
|
19871
|
-
|
|
19872
|
-
|
|
19873
|
-
|
|
19874
|
-
|
|
19875
|
-
|
|
19876
|
-
|
|
19877
|
-
|
|
19878
|
-
|
|
19879
|
-
|
|
19870
|
+
return /* @__PURE__ */ jsxs(
|
|
19871
|
+
"div",
|
|
19872
|
+
{
|
|
19873
|
+
className: `df-tree-node ${debugStateClass} ${currentClass}`,
|
|
19874
|
+
"data-current-step": isCurrent ? "true" : void 0,
|
|
19875
|
+
children: [
|
|
19876
|
+
/* @__PURE__ */ jsxs(
|
|
19877
|
+
"div",
|
|
19878
|
+
{
|
|
19879
|
+
className: `df-tree-node-content ${isSelected ? "df-tree-node-selected" : ""}`,
|
|
19880
|
+
style: { paddingLeft: `${level * 16 + 8}px` },
|
|
19881
|
+
onClick: handleClick,
|
|
19882
|
+
children: [
|
|
19883
|
+
/* @__PURE__ */ jsx(
|
|
19884
|
+
"span",
|
|
19885
|
+
{
|
|
19886
|
+
className: "df-tree-toggle",
|
|
19887
|
+
onClick: hasChildren ? handleToggle : void 0,
|
|
19888
|
+
style: { visibility: hasChildren ? "visible" : "hidden" },
|
|
19889
|
+
children: isExpanded ? /* @__PURE__ */ jsx(ChevronDown, { size: 14 }) : /* @__PURE__ */ jsx(ChevronRight, { size: 14 })
|
|
19890
|
+
}
|
|
19891
|
+
),
|
|
19892
|
+
icon && /* @__PURE__ */ jsx("span", { className: "df-tree-icon", style: iconColor ? { color: iconColor } : void 0, children: icon }),
|
|
19893
|
+
/* @__PURE__ */ jsx("span", { className: "df-tree-label", children: label }),
|
|
19894
|
+
debugState && /* @__PURE__ */ jsx("span", { className: "df-tree-debug-indicator", children: /* @__PURE__ */ jsx(DebugStateIcon, { state: debugState, conditionResult }) })
|
|
19895
|
+
]
|
|
19896
|
+
}
|
|
19897
|
+
),
|
|
19898
|
+
isExpanded && hasChildren && /* @__PURE__ */ jsx("div", { className: "df-tree-children", children: children2 })
|
|
19899
|
+
]
|
|
19900
|
+
}
|
|
19901
|
+
);
|
|
19880
19902
|
}
|
|
19881
19903
|
const TREE_COLORS = {
|
|
19882
19904
|
workflow: "#0078d4",
|
|
@@ -19889,8 +19911,10 @@ const TREE_COLORS = {
|
|
|
19889
19911
|
// VSCode teal/green
|
|
19890
19912
|
validation: "#ce9178",
|
|
19891
19913
|
// VSCode orange
|
|
19892
|
-
tasks: "#9d9d9d"
|
|
19914
|
+
tasks: "#9d9d9d",
|
|
19893
19915
|
// VSCode gray
|
|
19916
|
+
folder: "#dcb67a"
|
|
19917
|
+
// VSCode folder yellow/gold
|
|
19894
19918
|
};
|
|
19895
19919
|
function TaskNode({
|
|
19896
19920
|
task,
|
|
@@ -20089,20 +20113,177 @@ function WorkflowNode({
|
|
|
20089
20113
|
}
|
|
20090
20114
|
);
|
|
20091
20115
|
}
|
|
20116
|
+
function FolderNode({
|
|
20117
|
+
folder,
|
|
20118
|
+
level,
|
|
20119
|
+
selection: selection2,
|
|
20120
|
+
onSelect,
|
|
20121
|
+
expandedNodes,
|
|
20122
|
+
toggleNode,
|
|
20123
|
+
debugMode = false
|
|
20124
|
+
}) {
|
|
20125
|
+
const folderId = `folder-${folder.fullPath}`;
|
|
20126
|
+
const isExpanded = expandedNodes.has(folderId);
|
|
20127
|
+
const hasChildren = folder.folders.size > 0 || folder.workflows.length > 0;
|
|
20128
|
+
const sortedFolders = Array.from(folder.folders.values()).sort(
|
|
20129
|
+
(a, b) => a.name.localeCompare(b.name)
|
|
20130
|
+
);
|
|
20131
|
+
return /* @__PURE__ */ jsxs(
|
|
20132
|
+
TreeNode,
|
|
20133
|
+
{
|
|
20134
|
+
label: `${folder.name} (${folder.totalWorkflowCount})`,
|
|
20135
|
+
icon: /* @__PURE__ */ jsx(Folder, { size: 14 }),
|
|
20136
|
+
iconColor: TREE_COLORS.folder,
|
|
20137
|
+
isExpanded,
|
|
20138
|
+
hasChildren,
|
|
20139
|
+
level,
|
|
20140
|
+
onToggle: () => toggleNode(folderId),
|
|
20141
|
+
onClick: () => toggleNode(folderId),
|
|
20142
|
+
children: [
|
|
20143
|
+
sortedFolders.map((childFolder) => /* @__PURE__ */ jsx(
|
|
20144
|
+
FolderNode,
|
|
20145
|
+
{
|
|
20146
|
+
folder: childFolder,
|
|
20147
|
+
level: level + 1,
|
|
20148
|
+
selection: selection2,
|
|
20149
|
+
onSelect,
|
|
20150
|
+
expandedNodes,
|
|
20151
|
+
toggleNode,
|
|
20152
|
+
debugMode
|
|
20153
|
+
},
|
|
20154
|
+
childFolder.fullPath
|
|
20155
|
+
)),
|
|
20156
|
+
folder.workflows.map((workflow) => /* @__PURE__ */ jsx(
|
|
20157
|
+
WorkflowNode,
|
|
20158
|
+
{
|
|
20159
|
+
workflow,
|
|
20160
|
+
level: level + 1,
|
|
20161
|
+
selection: selection2,
|
|
20162
|
+
onSelect,
|
|
20163
|
+
expandedNodes,
|
|
20164
|
+
toggleNode,
|
|
20165
|
+
debugMode
|
|
20166
|
+
},
|
|
20167
|
+
workflow.id
|
|
20168
|
+
))
|
|
20169
|
+
]
|
|
20170
|
+
}
|
|
20171
|
+
);
|
|
20172
|
+
}
|
|
20173
|
+
function parsePath(path) {
|
|
20174
|
+
if (!path) return [];
|
|
20175
|
+
const trimmed = path.replace(/^\/+|\/+$/g, "");
|
|
20176
|
+
if (!trimmed) return [];
|
|
20177
|
+
return trimmed.split("/").filter((segment) => segment.length > 0);
|
|
20178
|
+
}
|
|
20179
|
+
function createFolderNode(name, fullPath) {
|
|
20180
|
+
return {
|
|
20181
|
+
name,
|
|
20182
|
+
fullPath,
|
|
20183
|
+
folders: /* @__PURE__ */ new Map(),
|
|
20184
|
+
workflows: [],
|
|
20185
|
+
totalWorkflowCount: 0
|
|
20186
|
+
};
|
|
20187
|
+
}
|
|
20188
|
+
function calculateTotalCount(node) {
|
|
20189
|
+
let count = node.workflows.length;
|
|
20190
|
+
for (const child of node.folders.values()) {
|
|
20191
|
+
count += calculateTotalCount(child);
|
|
20192
|
+
}
|
|
20193
|
+
node.totalWorkflowCount = count;
|
|
20194
|
+
return count;
|
|
20195
|
+
}
|
|
20196
|
+
function buildFolderTree(workflows) {
|
|
20197
|
+
const tree = {
|
|
20198
|
+
folders: /* @__PURE__ */ new Map(),
|
|
20199
|
+
workflows: []
|
|
20200
|
+
};
|
|
20201
|
+
const sortedWorkflows = [...workflows].sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
|
|
20202
|
+
for (const workflow of sortedWorkflows) {
|
|
20203
|
+
const segments = parsePath(workflow.path);
|
|
20204
|
+
if (segments.length === 0) {
|
|
20205
|
+
tree.workflows.push(workflow);
|
|
20206
|
+
} else {
|
|
20207
|
+
let currentLevel = tree.folders;
|
|
20208
|
+
let currentPath = "";
|
|
20209
|
+
for (let i = 0; i < segments.length; i++) {
|
|
20210
|
+
const segment = segments[i];
|
|
20211
|
+
currentPath = currentPath ? `${currentPath}/${segment}` : segment;
|
|
20212
|
+
if (!currentLevel.has(segment)) {
|
|
20213
|
+
currentLevel.set(segment, createFolderNode(segment, currentPath));
|
|
20214
|
+
}
|
|
20215
|
+
const folder = currentLevel.get(segment);
|
|
20216
|
+
if (i === segments.length - 1) {
|
|
20217
|
+
folder.workflows.push(workflow);
|
|
20218
|
+
} else {
|
|
20219
|
+
currentLevel = folder.folders;
|
|
20220
|
+
}
|
|
20221
|
+
}
|
|
20222
|
+
}
|
|
20223
|
+
}
|
|
20224
|
+
for (const folder of tree.folders.values()) {
|
|
20225
|
+
calculateTotalCount(folder);
|
|
20226
|
+
}
|
|
20227
|
+
return tree;
|
|
20228
|
+
}
|
|
20229
|
+
function getFirstLevelFolderIds(tree) {
|
|
20230
|
+
return Array.from(tree.folders.keys()).map((name) => `folder-${name}`);
|
|
20231
|
+
}
|
|
20232
|
+
function getParentFolderIds(path) {
|
|
20233
|
+
const segments = parsePath(path);
|
|
20234
|
+
if (segments.length === 0) return [];
|
|
20235
|
+
const ids = [];
|
|
20236
|
+
let currentPath = "";
|
|
20237
|
+
for (const segment of segments) {
|
|
20238
|
+
currentPath = currentPath ? `${currentPath}/${segment}` : segment;
|
|
20239
|
+
ids.push(`folder-${currentPath}`);
|
|
20240
|
+
}
|
|
20241
|
+
return ids;
|
|
20242
|
+
}
|
|
20092
20243
|
function TreeView({ workflows, selection: selection2, onSelect, debugMode = false }) {
|
|
20093
20244
|
const debuggerContext = useDebugger();
|
|
20094
20245
|
const effectiveDebugContext = debugMode ? debuggerContext : null;
|
|
20246
|
+
const folderTree = useMemo(() => buildFolderTree(workflows), [workflows]);
|
|
20247
|
+
const sortedRootFolders = useMemo(() => {
|
|
20248
|
+
return Array.from(folderTree.folders.values()).sort(
|
|
20249
|
+
(a, b) => a.name.localeCompare(b.name)
|
|
20250
|
+
);
|
|
20251
|
+
}, [folderTree]);
|
|
20252
|
+
const rootWorkflows = folderTree.workflows;
|
|
20095
20253
|
const [expandedNodes, setExpandedNodes] = useState(() => {
|
|
20096
20254
|
const initial = /* @__PURE__ */ new Set(["workflows-root"]);
|
|
20097
|
-
|
|
20098
|
-
initial.add(`workflow-${workflows[0].id}`);
|
|
20099
|
-
}
|
|
20255
|
+
getFirstLevelFolderIds(folderTree).forEach((id2) => initial.add(id2));
|
|
20100
20256
|
return initial;
|
|
20101
20257
|
});
|
|
20102
|
-
|
|
20103
|
-
|
|
20104
|
-
|
|
20258
|
+
useEffect(() => {
|
|
20259
|
+
setExpandedNodes((prev) => {
|
|
20260
|
+
const next = new Set(prev);
|
|
20261
|
+
next.add("workflows-root");
|
|
20262
|
+
getFirstLevelFolderIds(folderTree).forEach((id2) => next.add(id2));
|
|
20263
|
+
return next;
|
|
20264
|
+
});
|
|
20265
|
+
}, [folderTree]);
|
|
20266
|
+
useEffect(() => {
|
|
20267
|
+
const allWorkflows = [...folderTree.workflows];
|
|
20268
|
+
function collectWorkflows(folders) {
|
|
20269
|
+
for (const folder of folders.values()) {
|
|
20270
|
+
allWorkflows.push(...folder.workflows);
|
|
20271
|
+
collectWorkflows(folder.folders);
|
|
20272
|
+
}
|
|
20273
|
+
}
|
|
20274
|
+
collectWorkflows(folderTree.folders);
|
|
20275
|
+
if (allWorkflows.length > 0) {
|
|
20276
|
+
allWorkflows.sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
|
|
20277
|
+
setExpandedNodes((prev) => {
|
|
20278
|
+
const next = new Set(prev);
|
|
20279
|
+
next.add(`workflow-${allWorkflows[0].id}`);
|
|
20280
|
+
getParentFolderIds(allWorkflows[0].path).forEach((id2) => next.add(id2));
|
|
20281
|
+
return next;
|
|
20282
|
+
});
|
|
20283
|
+
}
|
|
20284
|
+
}, [folderTree]);
|
|
20105
20285
|
const lastSelectedRef = useRef(null);
|
|
20286
|
+
const treeContainerRef = useRef(null);
|
|
20106
20287
|
useEffect(() => {
|
|
20107
20288
|
var _a, _b;
|
|
20108
20289
|
if (!debugMode || !(effectiveDebugContext == null ? void 0 : effectiveDebugContext.currentStep) || effectiveDebugContext.state.currentStepIndex < 0) {
|
|
@@ -20112,9 +20293,13 @@ function TreeView({ workflows, selection: selection2, onSelect, debugMode = fals
|
|
|
20112
20293
|
if (((_a = lastSelectedRef.current) == null ? void 0 : _a.workflowId) === workflow_id && ((_b = lastSelectedRef.current) == null ? void 0 : _b.taskId) === task_id) {
|
|
20113
20294
|
return;
|
|
20114
20295
|
}
|
|
20296
|
+
const workflow = workflows.find((w) => w.id === workflow_id);
|
|
20115
20297
|
setExpandedNodes((prev) => {
|
|
20116
20298
|
const next = new Set(prev);
|
|
20117
20299
|
next.add("workflows-root");
|
|
20300
|
+
if (workflow == null ? void 0 : workflow.path) {
|
|
20301
|
+
getParentFolderIds(workflow.path).forEach((id2) => next.add(id2));
|
|
20302
|
+
}
|
|
20118
20303
|
next.add(`workflow-${workflow_id}`);
|
|
20119
20304
|
next.add(`tasks-${workflow_id}`);
|
|
20120
20305
|
if (task_id) {
|
|
@@ -20123,7 +20308,6 @@ function TreeView({ workflows, selection: selection2, onSelect, debugMode = fals
|
|
|
20123
20308
|
return next;
|
|
20124
20309
|
});
|
|
20125
20310
|
if (task_id) {
|
|
20126
|
-
const workflow = workflows.find((w) => w.id === workflow_id);
|
|
20127
20311
|
const task = workflow == null ? void 0 : workflow.tasks.find((t) => t.id === task_id);
|
|
20128
20312
|
if (workflow && task) {
|
|
20129
20313
|
lastSelectedRef.current = { workflowId: workflow_id, taskId: task_id };
|
|
@@ -20132,6 +20316,16 @@ function TreeView({ workflows, selection: selection2, onSelect, debugMode = fals
|
|
|
20132
20316
|
} else {
|
|
20133
20317
|
lastSelectedRef.current = { workflowId: workflow_id };
|
|
20134
20318
|
}
|
|
20319
|
+
setTimeout(() => {
|
|
20320
|
+
var _a2;
|
|
20321
|
+
const currentStepElement = (_a2 = treeContainerRef.current) == null ? void 0 : _a2.querySelector('[data-current-step="true"]');
|
|
20322
|
+
if (currentStepElement) {
|
|
20323
|
+
currentStepElement.scrollIntoView({
|
|
20324
|
+
behavior: "smooth",
|
|
20325
|
+
block: "nearest"
|
|
20326
|
+
});
|
|
20327
|
+
}
|
|
20328
|
+
}, 50);
|
|
20135
20329
|
}, [debugMode, effectiveDebugContext == null ? void 0 : effectiveDebugContext.currentStep, effectiveDebugContext == null ? void 0 : effectiveDebugContext.state.currentStepIndex, workflows, onSelect]);
|
|
20136
20330
|
const toggleNode = (id2) => {
|
|
20137
20331
|
setExpandedNodes((prev) => {
|
|
@@ -20145,30 +20339,46 @@ function TreeView({ workflows, selection: selection2, onSelect, debugMode = fals
|
|
|
20145
20339
|
});
|
|
20146
20340
|
};
|
|
20147
20341
|
const isRootExpanded = expandedNodes.has("workflows-root");
|
|
20148
|
-
|
|
20342
|
+
const totalWorkflowCount = workflows.length;
|
|
20343
|
+
return /* @__PURE__ */ jsx("div", { ref: treeContainerRef, className: `df-tree-view ${debugMode ? "df-tree-view-debug" : ""}`, children: /* @__PURE__ */ jsxs(
|
|
20149
20344
|
TreeNode,
|
|
20150
20345
|
{
|
|
20151
20346
|
label: "Workflows",
|
|
20152
20347
|
icon: /* @__PURE__ */ jsx(Layers, { size: 14 }),
|
|
20153
20348
|
iconColor: TREE_COLORS.workflow,
|
|
20154
20349
|
isExpanded: isRootExpanded,
|
|
20155
|
-
hasChildren:
|
|
20350
|
+
hasChildren: totalWorkflowCount > 0,
|
|
20156
20351
|
level: 0,
|
|
20157
20352
|
onToggle: () => toggleNode("workflows-root"),
|
|
20158
20353
|
onClick: () => toggleNode("workflows-root"),
|
|
20159
|
-
children:
|
|
20160
|
-
|
|
20161
|
-
|
|
20162
|
-
|
|
20163
|
-
|
|
20164
|
-
|
|
20165
|
-
|
|
20166
|
-
|
|
20167
|
-
|
|
20168
|
-
|
|
20169
|
-
|
|
20170
|
-
|
|
20171
|
-
|
|
20354
|
+
children: [
|
|
20355
|
+
sortedRootFolders.map((folder) => /* @__PURE__ */ jsx(
|
|
20356
|
+
FolderNode,
|
|
20357
|
+
{
|
|
20358
|
+
folder,
|
|
20359
|
+
level: 1,
|
|
20360
|
+
selection: selection2,
|
|
20361
|
+
onSelect,
|
|
20362
|
+
expandedNodes,
|
|
20363
|
+
toggleNode,
|
|
20364
|
+
debugMode
|
|
20365
|
+
},
|
|
20366
|
+
folder.fullPath
|
|
20367
|
+
)),
|
|
20368
|
+
rootWorkflows.map((workflow) => /* @__PURE__ */ jsx(
|
|
20369
|
+
WorkflowNode,
|
|
20370
|
+
{
|
|
20371
|
+
workflow,
|
|
20372
|
+
level: 1,
|
|
20373
|
+
selection: selection2,
|
|
20374
|
+
onSelect,
|
|
20375
|
+
expandedNodes,
|
|
20376
|
+
toggleNode,
|
|
20377
|
+
debugMode
|
|
20378
|
+
},
|
|
20379
|
+
workflow.id
|
|
20380
|
+
))
|
|
20381
|
+
]
|
|
20172
20382
|
}
|
|
20173
20383
|
) });
|
|
20174
20384
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goplasmatic/dataflow-ui",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "React visualization library for dataflow-rs workflow engine",
|
|
6
6
|
"author": "Plasmatic Engineering <shankar@goplasmatic.io>",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@goplasmatic/dataflow-wasm": "^2.0.4",
|
|
56
|
-
"@goplasmatic/datalogic-ui": "^4.0.
|
|
56
|
+
"@goplasmatic/datalogic-ui": "^4.0.11",
|
|
57
57
|
"@monaco-editor/react": "^4.7.0",
|
|
58
58
|
"@xyflow/react": "^12.0.0",
|
|
59
59
|
"lucide-react": "^0.462.0"
|
|
@@ -67,6 +67,8 @@
|
|
|
67
67
|
"react-dom": "^18.3.1",
|
|
68
68
|
"typescript": "^5.7.3",
|
|
69
69
|
"vite": "^6.0.7",
|
|
70
|
-
"vite-plugin-dts": "^4.4.0"
|
|
70
|
+
"vite-plugin-dts": "^4.4.0",
|
|
71
|
+
"vite-plugin-top-level-await": "^1.6.0",
|
|
72
|
+
"vite-plugin-wasm": "^3.5.0"
|
|
71
73
|
}
|
|
72
74
|
}
|