@parallelworks/ui 0.38.0 → 0.39.0

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.
Files changed (40) hide show
  1. package/README.md +1 -1
  2. package/dist/ai/components/ChatInput.js +8 -23
  3. package/dist/ai/components/chatDraft.d.ts +7 -0
  4. package/dist/ai/components/chatDraft.js +66 -0
  5. package/dist/editing.d.ts +2 -0
  6. package/dist/editor/Monaco.js +1 -1
  7. package/dist/editor/index.js +1 -1
  8. package/dist/editor/lintContext.js +15 -3
  9. package/dist/graph/DependencyGraph.d.ts +8 -1
  10. package/dist/graph/DependencyGraph.js +433 -204
  11. package/dist/graph/GraphEditor.d.ts +27 -0
  12. package/dist/graph/GraphEditor.js +1543 -0
  13. package/dist/graph/GraphEditorDialogs.d.ts +87 -0
  14. package/dist/graph/GraphEditorDialogs.js +1808 -0
  15. package/dist/graph/InputDialog.js +4 -4
  16. package/dist/graph/InputsEditor.js +2 -2
  17. package/dist/graph/JobSummary.d.ts +3 -0
  18. package/dist/graph/JobSummary.js +34 -21
  19. package/dist/graph/MatrixGroup.d.ts +2 -1
  20. package/dist/graph/MatrixGroup.js +10 -3
  21. package/dist/graph/WorkflowSettingsDialog.d.ts +21 -0
  22. package/dist/graph/WorkflowSettingsDialog.js +670 -0
  23. package/dist/graph/editorFields.js +144 -3
  24. package/dist/graph/editorRows.d.ts +58 -0
  25. package/dist/graph/editorRows.js +460 -0
  26. package/dist/graph/editorStrings.d.ts +1 -0
  27. package/dist/graph/editorStrings.js +1 -0
  28. package/dist/graph/expressionRefs.js +39 -0
  29. package/dist/graph/fieldProblems.js +36 -2
  30. package/dist/graph/graphEditorState.d.ts +191 -0
  31. package/dist/graph/graphEditorState.js +74 -0
  32. package/dist/graph/index.d.ts +3 -0
  33. package/dist/graph/index.js +5 -2
  34. package/dist/graph/inputLayout.js +1 -1
  35. package/dist/graph/inputRefs.js +196 -4
  36. package/dist/graph/refusalText.js +13 -0
  37. package/dist/graph/stepWith.d.ts +38 -0
  38. package/dist/graph/stepWith.js +536 -0
  39. package/dist/styles.css +1 -1
  40. package/package.json +1 -1
@@ -16,15 +16,17 @@ import { toAbsHumanDuration } from "../duration.js";
16
16
  import { initializeValues } from "../form/lib.js";
17
17
  import { AnnotationBanner } from "./AnnotationBanner.js";
18
18
  import { Collapse } from "./Collapse.js";
19
+ import { jobLabel, toggled } from "./util.js";
20
+ import { EDITOR_HANDLE_CLASS, GraphEditorProvider, useGraphEditor, useSelectedEdges } from "./graphEditorState.js";
21
+ import { BoxGrip, EdgeEnds, EdgeHitPath, EmptyGraphEditor, GraphEditorCanvas, NodePorts } from "./editorRows.js";
19
22
  import { emptyColumnsStyle, emptyRowsStyle } from "./gridSpacing.js";
20
23
  import { Reveal } from "./Reveal.js";
21
- import { jobLabel, toggled } from "./util.js";
22
24
  import { Joblist } from "./JobSummary.js";
23
25
  import { MatrixGroupNode, MatrixGroupSummaryItem } from "./MatrixGroup.js";
24
26
  import TreeView from "./TreeView.js";
25
27
  import { isJobRecord } from "./types.js";
26
28
  import cx from "classnames";
27
- import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
29
+ import React, { Suspense, lazy, useCallback, useEffect, useMemo, useRef, useState } from "react";
28
30
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
29
31
  import { flushSync } from "react-dom";
30
32
  import { DateTime } from "luxon";
@@ -362,7 +364,7 @@ function computeGraphLayout(engine, jobs, matrixGroups, layout) {
362
364
  descendantDists
363
365
  };
364
366
  }
365
- function jobHandlers({ engine, jobs, displayJobs, pathPrefix, stepsOpen, toggleSteps, expandedSubworkflows, toggleSubworkflow, openInNewGraph, setSublogOpen, renderWell }) {
367
+ function jobHandlers({ engine, jobs, displayJobs, pathPrefix, stepsOpen, toggleSteps, expandedSubworkflows, toggleSubworkflow, openInNewGraph, setSublogOpen, renderWell, editStep }) {
366
368
  const isStepsOpen = (j) => stepsOpen.has(pathPrefix + j);
367
369
  const subworkflowKey = (j, s) => `${pathPrefix}${j}:${s}`;
368
370
  const openLog = (j, s) => setSublogOpen(engine.stepLogPath(pathPrefix, j, s));
@@ -376,6 +378,10 @@ function jobHandlers({ engine, jobs, displayJobs, pathPrefix, stepsOpen, toggleS
376
378
  onStepClick: (j, s) => {
377
379
  const step = jobs[j]?.steps?.[s];
378
380
  if (step?.status === "skipped" || step?.status === "skipped-failed") return;
381
+ if (editStep && !step?.linkedStep) {
382
+ editStep(jobs[j]?._matrix?.originaljob ?? j, s);
383
+ return;
384
+ }
379
385
  if (step?.subworkflow) toggleSubworkflow(subworkflowKey(j, s));
380
386
  else openLog(j, s);
381
387
  },
@@ -401,6 +407,7 @@ function SubworkflowWell({ expanded, onSettled, className, children }) {
401
407
  });
402
408
  }
403
409
  var SUBGRAPH_Z_STEP = 100;
410
+ var GraphEditorOverlays = lazy(() => import("./GraphEditor.js"));
404
411
  var CONNECTOR_COLOR = "var(--theme-border)";
405
412
  var HIGHLIGHT_COLOR = "var(--theme-element)";
406
413
  var FIT_ANIMATION_MS = 300;
@@ -422,10 +429,18 @@ function sizesSettled(els) {
422
429
  function sizingTransitions(el) {
423
430
  return el.getAnimations({ subtree: true }).filter((animation) => animation instanceof CSSTransition && (animation.transitionProperty === "grid-template-rows" || animation.transitionProperty === "width"));
424
431
  }
432
+ function intoView(start, end, low, high) {
433
+ if (end - start > high - low) return 0;
434
+ if (start < low) return low - start;
435
+ return end > high ? high - end : 0;
436
+ }
437
+ var HOLD_MS = 2e3;
425
438
  var ZOOM_STEP = .25;
426
- function Subgraph({ jobs, pathPrefix, depth, preview, animT, expandedSubworkflows, toggleSubworkflow, openInNewGraph, setSublogOpen, invalidateConnectors, requestFit, stepsOpen, toggleSteps, matrixOpen, toggleMatrix, onReady, layout }) {
439
+ function Subgraph({ jobs, pathPrefix, depth, preview, animT, expandedSubworkflows, toggleSubworkflow, openInNewGraph, setSublogOpen, invalidateConnectors, requestFit, stepsOpen, toggleSteps, matrixOpen, toggleMatrix, onReady, layout, editable = false }) {
427
440
  const engine = useWorkflowEngine();
428
441
  const wrapperRef = useRef(null);
442
+ const editorApi = useGraphEditor();
443
+ const selectedEdges = useSelectedEdges(editable ? editorApi : null);
429
444
  const zBase = depth * SUBGRAPH_Z_STEP;
430
445
  const [hoveredJob, setHoveredJob] = useState(null);
431
446
  const lastHoveredDistancesRef = useRef(null);
@@ -443,6 +458,23 @@ function Subgraph({ jobs, pathPrefix, depth, preview, animT, expandedSubworkflow
443
458
  matrixGroups,
444
459
  placed
445
460
  ]);
461
+ useEffect(() => {
462
+ const wrapper = wrapperRef.current;
463
+ if (!editable || !editorApi || !wrapper) return;
464
+ editorApi.registerGrid({
465
+ wrapper,
466
+ cols: dependencyCols,
467
+ rows: rowSlots,
468
+ columns: colSlots
469
+ });
470
+ return () => editorApi.registerGrid(null);
471
+ }, [
472
+ editable,
473
+ editorApi,
474
+ dependencyCols,
475
+ rowSlots,
476
+ colSlots
477
+ ]);
446
478
  const hoveredDistances = useMemo(() => {
447
479
  if (!hoveredJob) return null;
448
480
  const dist = /* @__PURE__ */ new Map();
@@ -533,6 +565,7 @@ function Subgraph({ jobs, pathPrefix, depth, preview, animT, expandedSubworkflow
533
565
  },
534
566
  openInNewGraph,
535
567
  setSublogOpen,
568
+ editStep: editable && editorApi ? (j, s) => editorApi.editStep(j, s) : void 0,
536
569
  renderWell: (subJobs, subPrefix, expanded) => /* @__PURE__ */ jsx(SubworkflowWell, {
537
570
  expanded,
538
571
  onSettled: requestFit,
@@ -581,6 +614,7 @@ function Subgraph({ jobs, pathPrefix, depth, preview, animT, expandedSubworkflow
581
614
  zBase,
582
615
  handlers,
583
616
  preview,
617
+ editable,
584
618
  rowsAbove
585
619
  }, `node_${first}`);
586
620
  return /* @__PURE__ */ jsxs("div", {
@@ -595,19 +629,20 @@ function Subgraph({ jobs, pathPrefix, depth, preview, animT, expandedSubworkflow
595
629
  onMouseLeave: () => setHoveredJob(null),
596
630
  onFocus: () => setHoveredJob(first),
597
631
  onBlur: () => setHoveredJob(null),
598
- children: [/* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-2xl bg-(--theme-panel-bg)" }), /* @__PURE__ */ jsx("div", {
632
+ children: [/* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-2xl bg-(--theme-panel-bg)" }), /* @__PURE__ */ jsxs("div", {
599
633
  className: "relative border-solid shadow py-4 px-8 rounded-xl border-4 whitespace-nowrap bg-(--theme-panel-bg) text-2xl",
600
634
  style: {
601
635
  opacity: hoveredRelated && !hoveredRelated.has(first) ? .5 : 1,
602
636
  transition: `opacity ${animT}s`
603
637
  },
604
- children: /* @__PURE__ */ jsx(Joblist, {
638
+ children: [editable && jobNames.length > 1 && /* @__PURE__ */ jsx(BoxGrip, { jobs: jobNames }), /* @__PURE__ */ jsx(Joblist, {
605
639
  jobs: displayJobs,
606
640
  jobNames,
607
641
  ...handlers,
608
642
  growWidth: true,
609
- preview
610
- })
643
+ preview,
644
+ editable
645
+ })]
611
646
  })]
612
647
  }, `node_${first}`);
613
648
  })
@@ -637,7 +672,9 @@ function Subgraph({ jobs, pathPrefix, depth, preview, animT, expandedSubworkflow
637
672
  setSublogOpen,
638
673
  engine,
639
674
  invalidateConnectors,
640
- requestFit
675
+ requestFit,
676
+ editable,
677
+ editorApi
641
678
  ]);
642
679
  const memoizedBoxLayer = useMemo(() => buildBoxLayer(), [buildBoxLayer]);
643
680
  const hasLiveNestedGraph = Array.from(expandedSubworkflows).some((k) => k.startsWith(pathPrefix));
@@ -645,191 +682,259 @@ function Subgraph({ jobs, pathPrefix, depth, preview, animT, expandedSubworkflow
645
682
  return /* @__PURE__ */ jsxs("div", {
646
683
  ref: wrapperRef,
647
684
  className: "relative",
648
- children: [(() => {
649
- const wrapper = wrapperRef.current;
650
- const gid = (name) => document.getElementById(`node_${pathPrefix}${name}`);
651
- const ox = (el) => offsetWithin(el, wrapper).x;
652
- const nodeConnectorY = (el) => offsetWithin(el, wrapper).y + 37;
653
- const depsOf = (head) => directDeps[head] ?? [];
654
- let colGap = 32;
655
- if (dependencyCols.length >= 2) {
656
- const col0 = gid(dependencyCols[0]?.[0]?.[0] ?? "");
657
- const col1 = gid(dependencyCols[1]?.[0]?.[0] ?? "");
658
- if (col0 && col1) colGap = ox(col1) - (ox(col0) + col0.offsetWidth);
659
- }
660
- if (colSlots.some((column, c) => column !== c)) for (let c = 1; c < dependencyCols.length; c++) {
661
- const left = gid(dependencyCols[c - 1]?.[0]?.[0] ?? "");
662
- const right = gid(dependencyCols[c]?.[0]?.[0] ?? "");
663
- if (left && right) colGap = Math.min(colGap, ox(right) - (ox(left) + left.offsetWidth));
664
- }
665
- let minAdy = Number.POSITIVE_INFINITY;
666
- for (const col of dependencyCols) for (const jobNames of col) {
667
- const first = jobNames[0];
668
- if (first === void 0) continue;
669
- for (const dep of depsOf(first)) {
670
- const s = gid(dep);
671
- const e = gid(first);
672
- if (!s || !e) continue;
673
- const dy = Math.abs(nodeConnectorY(e) - nodeConnectorY(s));
674
- if (dy > 0 && dy < minAdy) minAdy = dy;
685
+ children: [
686
+ (() => {
687
+ const wrapper = wrapperRef.current;
688
+ const gid = (name) => document.getElementById(`node_${pathPrefix}${name}`);
689
+ const ox = (el) => offsetWithin(el, wrapper).x;
690
+ const nodeConnectorY = (el) => offsetWithin(el, wrapper).y + 37;
691
+ const depsOf = (head) => directDeps[head] ?? [];
692
+ let colGap = 32;
693
+ if (dependencyCols.length >= 2) {
694
+ const col0 = gid(dependencyCols[0]?.[0]?.[0] ?? "");
695
+ const col1 = gid(dependencyCols[1]?.[0]?.[0] ?? "");
696
+ if (col0 && col1) colGap = ox(col1) - (ox(col0) + col0.offsetWidth);
675
697
  }
676
- }
677
- const MAX_ARC_R = 16;
678
- const arcR = Math.min(minAdy === Number.POSITIVE_INFINITY ? MAX_ARC_R : minAdy / 4, colGap / 2, MAX_ARC_R);
679
- return dependencyCols.map((col) => /* @__PURE__ */ jsx(React.Fragment, { children: col.map(([first]) => first === void 0 ? null : /* @__PURE__ */ jsx(React.Fragment, { children: depsOf(first).map((dep) => {
680
- const isHighlighted = hoveredRelated?.has(first) && hoveredRelated?.has(dep);
681
- const depDist = activeDists?.get(dep) ?? 0;
682
- const targetDist = activeDists?.get(first) ?? 0;
683
- const closerDist = Math.min(depDist, targetDist);
684
- const connectorDelay = closerDist * animT;
685
- const retractDelay = (maxHoveredDistRef.current - closerDist) * animT;
686
- const drawRightToLeft = targetDist < depDist;
687
- const start = gid(dep);
688
- const end = gid(first);
689
- if (!start || !end) return null;
690
- const x1 = ox(start) + start.offsetWidth - 2;
691
- const y1 = nodeConnectorY(start);
692
- const x2 = ox(end) + 2;
693
- const y2 = nodeConnectorY(end);
694
- const dy = y2 - y1;
695
- const ady = Math.abs(dy);
696
- let pathD;
697
- let pathLen;
698
- let pathDReversed;
699
- const r = arcR;
700
- if (ady === 0) {
701
- pathD = `M ${x1} ${y1} L ${x2} ${y2}`;
702
- pathDReversed = `M ${x2} ${y2} L ${x1} ${y1}`;
703
- pathLen = x2 - x1;
704
- } else {
705
- const s = dy > 0 ? 1 : -1;
706
- const ax2end = x2 - (colGap / 2 - r);
707
- const ax = ax2end - 2 * r;
708
- const sw1 = dy > 0 ? 1 : 0;
709
- const sw2 = dy > 0 ? 0 : 1;
710
- pathD = [
711
- `M ${x1} ${y1}`,
712
- `L ${ax} ${y1}`,
713
- `A ${r} ${r} 0 0 ${sw1} ${ax + r} ${y1 + s * r}`,
714
- `L ${ax + r} ${y2 - s * r}`,
715
- `A ${r} ${r} 0 0 ${sw2} ${ax2end} ${y2}`,
716
- `L ${x2} ${y2}`
717
- ].join(" ");
718
- pathDReversed = [
719
- `M ${x2} ${y2}`,
720
- `L ${ax2end} ${y2}`,
721
- `A ${r} ${r} 0 0 ${sw1} ${ax + r} ${y2 - s * r}`,
722
- `L ${ax + r} ${y1 + s * r}`,
723
- `A ${r} ${r} 0 0 ${sw2} ${ax} ${y1}`,
724
- `L ${x1} ${y1}`
725
- ].join(" ");
726
- const arcLen = Math.PI * r / 2;
727
- const vSeg = Math.max(0, ady - 2 * r);
728
- pathLen = ax - x1 + arcLen + vSeg + arcLen + (x2 - ax2end);
698
+ if (colSlots.some((column, c) => column !== c)) for (let c = 1; c < dependencyCols.length; c++) {
699
+ const left = gid(dependencyCols[c - 1]?.[0]?.[0] ?? "");
700
+ const right = gid(dependencyCols[c]?.[0]?.[0] ?? "");
701
+ if (left && right) colGap = Math.min(colGap, ox(right) - (ox(left) + left.offsetWidth));
729
702
  }
730
- return /* @__PURE__ */ jsxs(React.Fragment, { children: [
731
- /* @__PURE__ */ jsxs("svg", {
732
- "aria-hidden": "true",
733
- overflow: "visible",
734
- className: "absolute pointer-events-none",
735
- style: { zIndex: (isHighlighted || activeDists?.has(dep) && activeDists?.has(first) ? 30 : 10) + zBase },
736
- children: [
737
- /* @__PURE__ */ jsx("rect", {
738
- x: x1 - 16,
739
- y: y1 - 16,
740
- width: 32,
741
- height: 32,
742
- fill: "var(--theme-panel-bg)"
743
- }),
744
- /* @__PURE__ */ jsx("rect", {
745
- x: x2 - 16,
746
- y: y2 - 16,
747
- width: 32,
748
- height: 32,
749
- fill: "var(--theme-panel-bg)"
750
- }),
751
- /* @__PURE__ */ jsx("circle", {
752
- cx: x1,
753
- cy: y1,
754
- r: "8",
755
- fill: isHighlighted ? HIGHLIGHT_COLOR : CONNECTOR_COLOR,
756
- style: {
757
- opacity: hoveredRelated && !isHighlighted ? .3 : 1,
758
- transition: isHighlighted ? `fill ${animT}s ${depDist * animT}s, opacity 0s` : `fill ${animT}s ${(maxHoveredDistRef.current - depDist) * animT}s, opacity 0s`
759
- }
760
- }),
761
- /* @__PURE__ */ jsx("circle", {
762
- cx: x2,
763
- cy: y2,
764
- r: "8",
765
- fill: isHighlighted ? HIGHLIGHT_COLOR : CONNECTOR_COLOR,
766
- style: {
767
- opacity: hoveredRelated && !isHighlighted ? .3 : 1,
768
- transition: isHighlighted ? `fill ${animT}s ${targetDist * animT}s, opacity 0s` : `fill ${animT}s ${(maxHoveredDistRef.current - targetDist) * animT}s, opacity 0s`
769
- }
770
- })
771
- ]
772
- }),
773
- /* @__PURE__ */ jsxs("svg", {
774
- "aria-hidden": "true",
775
- overflow: "visible",
776
- className: "absolute pointer-events-none",
777
- style: { zIndex: (isHighlighted ? 20 : -1) + zBase },
778
- children: [/* @__PURE__ */ jsx("path", {
703
+ let minAdy = Number.POSITIVE_INFINITY;
704
+ for (const col of dependencyCols) for (const jobNames of col) {
705
+ const first = jobNames[0];
706
+ if (first === void 0) continue;
707
+ for (const dep of depsOf(first)) {
708
+ const s = gid(dep);
709
+ const e = gid(first);
710
+ if (!s || !e) continue;
711
+ const dy = Math.abs(nodeConnectorY(e) - nodeConnectorY(s));
712
+ if (dy > 0 && dy < minAdy) minAdy = dy;
713
+ }
714
+ }
715
+ const MAX_ARC_R = 16;
716
+ const arcR = Math.min(minAdy === Number.POSITIVE_INFINITY ? MAX_ARC_R : minAdy / 4, colGap / 2, MAX_ARC_R);
717
+ const ports = (jobNames) => {
718
+ const [head] = jobNames;
719
+ const el = head === void 0 ? null : gid(head);
720
+ if (head === void 0 || !el) return null;
721
+ const y = nodeConnectorY(el);
722
+ const wiredIn = depsOf(head).some((dep) => gid(dep));
723
+ const wiredOut = dependencyCols.some((c) => c.some(([other = ""]) => depsOf(other).includes(head) && !!gid(other)));
724
+ return /* @__PURE__ */ jsx(NodePorts, {
725
+ jobs: jobNames,
726
+ left: {
727
+ x: ox(el) + 2,
728
+ y
729
+ },
730
+ right: {
731
+ x: ox(el) + el.offsetWidth - 2,
732
+ y
733
+ },
734
+ drawLeft: !wiredIn,
735
+ drawRight: !wiredOut,
736
+ zIndex: 40 + zBase
737
+ });
738
+ };
739
+ const hits = [];
740
+ const connectors = dependencyCols.map((col) => /* @__PURE__ */ jsx(React.Fragment, { children: col.map((jobNames) => {
741
+ const first = jobNames[0];
742
+ if (first === void 0) return null;
743
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [editable && ports(jobNames), depsOf(first).map((dep) => {
744
+ const isHighlighted = hoveredRelated?.has(first) && hoveredRelated?.has(dep);
745
+ const depDist = activeDists?.get(dep) ?? 0;
746
+ const targetDist = activeDists?.get(first) ?? 0;
747
+ const closerDist = Math.min(depDist, targetDist);
748
+ const connectorDelay = closerDist * animT;
749
+ const retractDelay = (maxHoveredDistRef.current - closerDist) * animT;
750
+ const drawRightToLeft = targetDist < depDist;
751
+ const start = gid(dep);
752
+ const end = gid(first);
753
+ if (!start || !end) return null;
754
+ const x1 = ox(start) + start.offsetWidth - 2;
755
+ const y1 = nodeConnectorY(start);
756
+ const x2 = ox(end) + 2;
757
+ const y2 = nodeConnectorY(end);
758
+ const selected = selectedEdges.some((edge) => edge.from === dep && edge.to === first);
759
+ const dy = y2 - y1;
760
+ const ady = Math.abs(dy);
761
+ let pathD;
762
+ let pathLen;
763
+ let pathDReversed;
764
+ const r = arcR;
765
+ if (ady === 0) {
766
+ pathD = `M ${x1} ${y1} L ${x2} ${y2}`;
767
+ pathDReversed = `M ${x2} ${y2} L ${x1} ${y1}`;
768
+ pathLen = x2 - x1;
769
+ } else {
770
+ const s = dy > 0 ? 1 : -1;
771
+ const ax2end = x2 - (colGap / 2 - r);
772
+ const ax = ax2end - 2 * r;
773
+ const sw1 = dy > 0 ? 1 : 0;
774
+ const sw2 = dy > 0 ? 0 : 1;
775
+ pathD = [
776
+ `M ${x1} ${y1}`,
777
+ `L ${ax} ${y1}`,
778
+ `A ${r} ${r} 0 0 ${sw1} ${ax + r} ${y1 + s * r}`,
779
+ `L ${ax + r} ${y2 - s * r}`,
780
+ `A ${r} ${r} 0 0 ${sw2} ${ax2end} ${y2}`,
781
+ `L ${x2} ${y2}`
782
+ ].join(" ");
783
+ pathDReversed = [
784
+ `M ${x2} ${y2}`,
785
+ `L ${ax2end} ${y2}`,
786
+ `A ${r} ${r} 0 0 ${sw1} ${ax + r} ${y2 - s * r}`,
787
+ `L ${ax + r} ${y1 + s * r}`,
788
+ `A ${r} ${r} 0 0 ${sw2} ${ax} ${y1}`,
789
+ `L ${x1} ${y1}`
790
+ ].join(" ");
791
+ const arcLen = Math.PI * r / 2;
792
+ const vSeg = Math.max(0, ady - 2 * r);
793
+ pathLen = ax - x1 + arcLen + vSeg + arcLen + (x2 - ax2end);
794
+ }
795
+ if (editable) hits.push(/* @__PURE__ */ jsxs(React.Fragment, { children: [selected && /* @__PURE__ */ jsx("path", {
779
796
  d: pathD,
780
- stroke: "var(--theme-panel-bg)",
781
- strokeWidth: "6",
797
+ stroke: "var(--theme-element)",
798
+ strokeWidth: "10",
782
799
  fill: "transparent"
783
- }), /* @__PURE__ */ jsx("path", {
800
+ }), /* @__PURE__ */ jsx(EdgeHitPath, {
784
801
  d: pathD,
785
- stroke: CONNECTOR_COLOR,
786
- strokeWidth: "6",
787
- fill: "transparent",
788
- style: {
789
- opacity: hoveredRelated && !isHighlighted ? .3 : 1,
790
- transition: `opacity 0s`
802
+ edge: {
803
+ from: dep,
804
+ to: first
791
805
  }
792
- })]
793
- }),
794
- /* @__PURE__ */ jsxs("svg", {
795
- "aria-hidden": "true",
796
- overflow: "visible",
797
- className: "absolute pointer-events-none",
798
- style: { zIndex: 20 + zBase },
799
- children: [/* @__PURE__ */ jsx("path", {
800
- d: drawRightToLeft ? pathDReversed : pathD,
801
- fill: "transparent",
802
- style: {
803
- stroke: HIGHLIGHT_COLOR,
804
- strokeWidth: 6,
805
- strokeDasharray: `${pathLen} ${pathLen}`,
806
- strokeDashoffset: isHighlighted ? 0 : pathLen,
807
- opacity: isHighlighted ? 1 : 0,
808
- transition: isHighlighted ? `stroke-dashoffset ${animT}s ease ${connectorDelay}s, opacity 0s ease ${connectorDelay}s` : "stroke-dashoffset 0s, opacity 0s"
809
- }
810
- }), (() => {
811
- const connKey = `${dep}→${first}`;
812
- if (isHighlighted) retractPathLensRef.current.set(connKey, pathLen);
813
- const isRetracting = !isHighlighted && !!activeDists?.has(dep) && !!activeDists?.has(first);
814
- if (!isHighlighted && !isRetracting) return null;
815
- const frozenLen = retractPathLensRef.current.get(connKey) ?? pathLen;
816
- return /* @__PURE__ */ jsx("path", {
817
- d: drawRightToLeft ? pathDReversed : pathD,
818
- fill: "transparent",
819
- style: {
820
- stroke: HIGHLIGHT_COLOR,
821
- strokeWidth: 6,
822
- strokeDasharray: `${frozenLen} ${frozenLen}`,
823
- strokeDashoffset: isHighlighted ? 0 : frozenLen,
824
- opacity: isHighlighted ? 0 : 1,
825
- transition: isHighlighted ? "stroke-dashoffset 0s, opacity 0s" : `stroke-dashoffset ${animT}s ease ${retractDelay}s, opacity 0s`
826
- }
827
- });
828
- })()]
829
- })
830
- ] }, dep);
831
- }) }, first)) }, col[0]?.[0]));
832
- })(), hasLiveNestedGraph ? buildBoxLayer() : memoizedBoxLayer]
806
+ })] }, `${dep}→${first}`));
807
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
808
+ /* @__PURE__ */ jsxs("svg", {
809
+ "aria-hidden": "true",
810
+ overflow: "visible",
811
+ className: "absolute pointer-events-none",
812
+ style: { zIndex: (isHighlighted || activeDists?.has(dep) && activeDists?.has(first) ? 30 : 10) + zBase },
813
+ children: [
814
+ /* @__PURE__ */ jsx("rect", {
815
+ x: x1 - 16,
816
+ y: y1 - 16,
817
+ width: 32,
818
+ height: 32,
819
+ fill: "var(--theme-panel-bg)"
820
+ }),
821
+ /* @__PURE__ */ jsx("rect", {
822
+ x: x2 - 16,
823
+ y: y2 - 16,
824
+ width: 32,
825
+ height: 32,
826
+ fill: "var(--theme-panel-bg)"
827
+ }),
828
+ /* @__PURE__ */ jsx("circle", {
829
+ cx: x1,
830
+ cy: y1,
831
+ r: "8",
832
+ fill: isHighlighted ? HIGHLIGHT_COLOR : CONNECTOR_COLOR,
833
+ style: {
834
+ opacity: hoveredRelated && !isHighlighted ? .3 : 1,
835
+ transition: isHighlighted ? `fill ${animT}s ${depDist * animT}s, opacity 0s` : `fill ${animT}s ${(maxHoveredDistRef.current - depDist) * animT}s, opacity 0s`
836
+ }
837
+ }),
838
+ /* @__PURE__ */ jsx("circle", {
839
+ cx: x2,
840
+ cy: y2,
841
+ r: "8",
842
+ fill: isHighlighted ? HIGHLIGHT_COLOR : CONNECTOR_COLOR,
843
+ style: {
844
+ opacity: hoveredRelated && !isHighlighted ? .3 : 1,
845
+ transition: isHighlighted ? `fill ${animT}s ${targetDist * animT}s, opacity 0s` : `fill ${animT}s ${(maxHoveredDistRef.current - targetDist) * animT}s, opacity 0s`
846
+ }
847
+ })
848
+ ]
849
+ }),
850
+ /* @__PURE__ */ jsxs("svg", {
851
+ "aria-hidden": "true",
852
+ overflow: "visible",
853
+ className: "absolute pointer-events-none",
854
+ style: { zIndex: (isHighlighted ? 20 : -1) + zBase },
855
+ children: [/* @__PURE__ */ jsx("path", {
856
+ d: pathD,
857
+ stroke: "var(--theme-panel-bg)",
858
+ strokeWidth: "6",
859
+ fill: "transparent"
860
+ }), /* @__PURE__ */ jsx("path", {
861
+ d: pathD,
862
+ stroke: CONNECTOR_COLOR,
863
+ strokeWidth: "6",
864
+ fill: "transparent",
865
+ style: {
866
+ opacity: hoveredRelated && !isHighlighted ? .3 : 1,
867
+ transition: `opacity 0s`
868
+ }
869
+ })]
870
+ }),
871
+ /* @__PURE__ */ jsxs("svg", {
872
+ "aria-hidden": "true",
873
+ overflow: "visible",
874
+ className: "absolute pointer-events-none",
875
+ style: { zIndex: 20 + zBase },
876
+ children: [/* @__PURE__ */ jsx("path", {
877
+ d: drawRightToLeft ? pathDReversed : pathD,
878
+ fill: "transparent",
879
+ style: {
880
+ stroke: HIGHLIGHT_COLOR,
881
+ strokeWidth: 6,
882
+ strokeDasharray: `${pathLen} ${pathLen}`,
883
+ strokeDashoffset: isHighlighted ? 0 : pathLen,
884
+ opacity: isHighlighted ? 1 : 0,
885
+ transition: isHighlighted ? `stroke-dashoffset ${animT}s ease ${connectorDelay}s, opacity 0s ease ${connectorDelay}s` : "stroke-dashoffset 0s, opacity 0s"
886
+ }
887
+ }), (() => {
888
+ const connKey = `${dep}→${first}`;
889
+ if (isHighlighted) retractPathLensRef.current.set(connKey, pathLen);
890
+ const isRetracting = !isHighlighted && !!activeDists?.has(dep) && !!activeDists?.has(first);
891
+ if (!isHighlighted && !isRetracting) return null;
892
+ const frozenLen = retractPathLensRef.current.get(connKey) ?? pathLen;
893
+ return /* @__PURE__ */ jsx("path", {
894
+ d: drawRightToLeft ? pathDReversed : pathD,
895
+ fill: "transparent",
896
+ style: {
897
+ stroke: HIGHLIGHT_COLOR,
898
+ strokeWidth: 6,
899
+ strokeDasharray: `${frozenLen} ${frozenLen}`,
900
+ strokeDashoffset: isHighlighted ? 0 : frozenLen,
901
+ opacity: isHighlighted ? 0 : 1,
902
+ transition: isHighlighted ? "stroke-dashoffset 0s, opacity 0s" : `stroke-dashoffset ${animT}s ease ${retractDelay}s, opacity 0s`
903
+ }
904
+ });
905
+ })()]
906
+ }),
907
+ editable && selected && /* @__PURE__ */ jsx(EdgeEnds, {
908
+ edge: {
909
+ from: dep,
910
+ to: first
911
+ },
912
+ start: {
913
+ x: x1,
914
+ y: y1
915
+ },
916
+ end: {
917
+ x: x2,
918
+ y: y2
919
+ },
920
+ zIndex: 45 + zBase
921
+ })
922
+ ] }, dep);
923
+ })] }, first);
924
+ }) }, col[0]?.[0]));
925
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [connectors, editable && /* @__PURE__ */ jsx("svg", {
926
+ role: "none",
927
+ overflow: "visible",
928
+ className: "absolute pointer-events-none",
929
+ width: wrapper?.offsetWidth,
930
+ height: wrapper?.offsetHeight,
931
+ style: { zIndex: 1 + zBase },
932
+ children: hits
933
+ })] });
934
+ })(),
935
+ hasLiveNestedGraph ? buildBoxLayer() : memoizedBoxLayer,
936
+ editable && /* @__PURE__ */ jsx(GraphEditorCanvas, {})
937
+ ]
833
938
  });
834
939
  }
835
940
  var NestedSubgraph = Subgraph;
@@ -918,7 +1023,7 @@ function walkJobPath(root, path) {
918
1023
  }
919
1024
  return isJobRecord(cursor) ? cursor : {};
920
1025
  }
921
- function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag", setViewMode, layout, yamlJobs, fixedHeight }) {
1026
+ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag", setViewMode, layout, editor, yamlJobs, inputs, workflow, fixedHeight }) {
922
1027
  const engine = useWorkflowEngine();
923
1028
  const animT = .125;
924
1029
  const minScale = .2;
@@ -950,8 +1055,10 @@ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag",
950
1055
  }, []);
951
1056
  const [topOffset, setTopOffset] = useState(null);
952
1057
  const topObserverRef = useRef(null);
1058
+ const [containerEl, setContainerEl] = useState(null);
953
1059
  const setContainerElement = useCallback((el) => {
954
1060
  containerRef.current = el;
1061
+ setContainerEl(el);
955
1062
  topObserverRef.current?.disconnect();
956
1063
  topObserverRef.current = null;
957
1064
  if (!el || preview) return;
@@ -1025,6 +1132,20 @@ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag",
1025
1132
  yamlJobs,
1026
1133
  jobs
1027
1134
  ]);
1135
+ const [editorApi, setEditorApi] = useState(null);
1136
+ const editorOverlays = editor ? /* @__PURE__ */ jsx(Suspense, {
1137
+ fallback: null,
1138
+ children: /* @__PURE__ */ jsx(GraphEditorOverlays, {
1139
+ editor,
1140
+ jobs,
1141
+ yamlJobs,
1142
+ inputs,
1143
+ workflow,
1144
+ layout: placed,
1145
+ container: containerEl,
1146
+ onApi: setEditorApi
1147
+ })
1148
+ }) : null;
1028
1149
  const resetGraph = useCallback(() => {
1029
1150
  setSublogOpen("");
1030
1151
  setStepsOpen(/* @__PURE__ */ new Set());
@@ -1077,6 +1198,26 @@ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag",
1077
1198
  jobs,
1078
1199
  matrixGroups
1079
1200
  ]);
1201
+ const held = useRef(null);
1202
+ const viewed = useRef(false);
1203
+ const nodeOf = useCallback((job) => {
1204
+ const row = [...containerRef.current?.querySelectorAll("[data-dag-job]") ?? []].find((el) => el.dataset["dagJob"] === job);
1205
+ return row?.closest("[id^=\"node_\"]") ?? row ?? null;
1206
+ }, []);
1207
+ const hold = useCallback((job) => {
1208
+ const content = graphContentRef.current;
1209
+ const transform = transformRef.current;
1210
+ const node = nodeOf(job);
1211
+ if (!content || !transform || !node) return;
1212
+ const { positionX, positionY, scale } = transform.instance.state;
1213
+ const pos = offsetWithin(node, content);
1214
+ held.current = {
1215
+ job,
1216
+ x: positionX + pos.x * scale,
1217
+ y: positionY + pos.y * scale,
1218
+ at: performance.now()
1219
+ };
1220
+ }, [nodeOf]);
1080
1221
  const fitGraph = useCallback((animationMs = 0) => {
1081
1222
  const container = containerRef.current;
1082
1223
  const content = graphContentRef.current;
@@ -1100,8 +1241,26 @@ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag",
1100
1241
  const graphH = maxY - minY;
1101
1242
  const containerW = container.clientWidth;
1102
1243
  const containerH = container.clientHeight;
1103
- if (graphW === 0 || graphH === 0) return;
1104
1244
  const margin = Math.min(containerW, containerH) * .05;
1245
+ const pending = held.current;
1246
+ held.current = null;
1247
+ const node = pending && performance.now() - pending.at < HOLD_MS ? nodeOf(pending.job) : null;
1248
+ const viewing = editor && viewed.current && animationMs === 0;
1249
+ if (pending && node || viewing) {
1250
+ const { positionX, positionY, scale: current } = transform.instance.state;
1251
+ const pos = pending && node ? offsetWithin(node, content) : null;
1252
+ const x = pending && pos ? pending.x - pos.x * current : positionX;
1253
+ const y = pending && pos ? pending.y - pos.y * current : positionY;
1254
+ transform.setTransform(x, y, current, 0);
1255
+ const dx = intoView(x + minX * current, x + maxX * current, margin, containerW - margin);
1256
+ const dy = intoView(y + minY * current, y + maxY * current, margin, containerH - margin);
1257
+ requestAnimationFrame(() => {
1258
+ if (dx !== 0 || dy !== 0) transform.setTransform(x + dx, y + dy, current, FIT_ANIMATION_MS, "easeOut");
1259
+ invalidateConnectors();
1260
+ });
1261
+ return;
1262
+ }
1263
+ if (graphW === 0 || graphH === 0) return;
1105
1264
  const scaleW = (containerW - margin * 2) / graphW;
1106
1265
  const scaleH = (containerH - margin * 2) / graphH;
1107
1266
  const scale = Math.min(Math.max(Math.min(scaleW, scaleH), minScale), maxScale);
@@ -1117,7 +1276,11 @@ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag",
1117
1276
  transform.setTransform(offsetX, offsetY, scale, 0);
1118
1277
  invalidateConnectors();
1119
1278
  });
1120
- }, [invalidateConnectors]);
1279
+ }, [
1280
+ invalidateConnectors,
1281
+ nodeOf,
1282
+ editor
1283
+ ]);
1121
1284
  const fitWhenSettled = useCallback(async () => {
1122
1285
  const container = containerRef.current;
1123
1286
  const content = graphContentRef.current;
@@ -1142,15 +1305,55 @@ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag",
1142
1305
  preview,
1143
1306
  fixedHeight
1144
1307
  ]);
1308
+ const centerOn = useCallback((el) => {
1309
+ const container = containerRef.current;
1310
+ const content = graphContentRef.current;
1311
+ const transform = transformRef.current;
1312
+ if (!container || !content || !transform) return;
1313
+ const { scale } = transform.instance.state;
1314
+ const pos = offsetWithin(el, content);
1315
+ transform.setTransform(container.clientWidth / 2 - (pos.x + el.offsetWidth / 2) * scale, container.clientHeight / 2 - (pos.y + el.offsetHeight / 2) * scale, scale, FIT_ANIMATION_MS, "easeOut");
1316
+ }, []);
1317
+ const registerShow = editor?.registerShow;
1318
+ useEffect(() => {
1319
+ if (!editorApi) return;
1320
+ editorApi.registerView({ hold });
1321
+ return () => editorApi.registerView(null);
1322
+ }, [editorApi, hold]);
1323
+ useEffect(() => {
1324
+ if (!registerShow || !editorApi) return;
1325
+ return registerShow(({ job, step }) => {
1326
+ const row = [...containerRef.current?.querySelectorAll("[data-dag-job]") ?? []].find((el) => el.dataset["dagJob"] === job);
1327
+ if (job === void 0 || !row) return false;
1328
+ if (step !== void 0) setStepsOpen((prev) => new Set(prev).add(job));
1329
+ editorApi.focus(step === void 0 ? { job } : {
1330
+ job,
1331
+ step
1332
+ });
1333
+ requestAnimationFrame(() => centerOn(row.closest("[id^=\"node_\"]") ?? row));
1334
+ if (step === void 0) editorApi.editJob(job);
1335
+ else editorApi.editStep(job, step);
1336
+ return true;
1337
+ });
1338
+ }, [
1339
+ registerShow,
1340
+ editorApi,
1341
+ centerOn
1342
+ ]);
1145
1343
  const [zoomedAll, setZoomedAll] = useState("in");
1146
1344
  const onTransform = useCallback((_, { scale }) => {
1147
1345
  setZoomedAll(scale >= .599 ? "in" : scale <= .201 ? "out" : null);
1148
1346
  invalidateConnectors();
1149
1347
  }, [invalidateConnectors]);
1348
+ const moved = useCallback(() => {
1349
+ viewed.current = true;
1350
+ invalidateConnectors();
1351
+ }, [invalidateConnectors]);
1150
1352
  const zoomBy = useCallback((step) => {
1151
1353
  const container = containerRef.current;
1152
1354
  const transform = transformRef.current;
1153
1355
  if (!container || !transform) return;
1356
+ viewed.current = true;
1154
1357
  const { positionX, positionY, scale } = transform.instance.state;
1155
1358
  const next = Math.min(maxScale, Math.max(minScale, scale * Math.exp(step)));
1156
1359
  const x = container.clientWidth / 2;
@@ -1191,7 +1394,10 @@ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag",
1191
1394
  resetGraph();
1192
1395
  setExecutedJobsPath([[], ...executedJobsPath.slice(executedJobsPathIdx, 10)]);
1193
1396
  setExecutedJobsPathIdx(0);
1194
- } else fitGraph(FIT_ANIMATION_MS);
1397
+ } else {
1398
+ viewed.current = false;
1399
+ fitGraph(FIT_ANIMATION_MS);
1400
+ }
1195
1401
  };
1196
1402
  const toggleExpandAll = () => {
1197
1403
  flushSync(() => {
@@ -1261,7 +1467,15 @@ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag",
1261
1467
  toggleMatrix,
1262
1468
  openInNewGraph
1263
1469
  ]);
1264
- if (!dependencyCols[0]?.length) return;
1470
+ if (!dependencyCols[0]?.length) return editor ? /* @__PURE__ */ jsx(GraphEditorProvider, {
1471
+ value: editorApi,
1472
+ children: /* @__PURE__ */ jsx(EmptyGraphEditor, {
1473
+ containerRef: setContainerElement,
1474
+ overlays: editorOverlays,
1475
+ height: fixedHeight ?? "240px",
1476
+ removeBorder
1477
+ })
1478
+ }) : void 0;
1265
1479
  const startDt = run.createdAt ? DateTime.fromISO(run.createdAt) : now;
1266
1480
  const endDt = run.completedAt ? DateTime.fromISO(run.completedAt) : now;
1267
1481
  const runtime = toAbsHumanDuration(startDt, endDt);
@@ -1294,7 +1508,7 @@ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag",
1294
1508
  },
1295
1509
  children: /* @__PURE__ */ jsx("span", { children: sublogIsSubworkflow ? "Open Subworkflow" : sublogShowsLog ? "Show Script" : "Show Logs" })
1296
1510
  }) : void 0;
1297
- return /* @__PURE__ */ jsxs("div", {
1511
+ const graph = /* @__PURE__ */ jsxs("div", {
1298
1512
  className: "flex flex-col lg:flex-row w-full",
1299
1513
  children: [
1300
1514
  !preview && /* @__PURE__ */ jsxs("div", {
@@ -1407,27 +1621,29 @@ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag",
1407
1621
  expandedJobs,
1408
1622
  setExpandedJobs
1409
1623
  })
1410
- }) : /* @__PURE__ */ jsx("div", {
1624
+ }) : /* @__PURE__ */ jsxs("div", {
1411
1625
  ref: setContainerElement,
1412
- className: cx("panel w-full overflow-hidden relative", removeBorder ? "border-none" : "border"),
1626
+ tabIndex: editor ? -1 : void 0,
1627
+ className: cx("panel w-full overflow-hidden relative", removeBorder ? "border-none" : "border", editor && "outline-none"),
1413
1628
  style: {
1414
1629
  height: fixedHeight ?? (containerHeight !== null ? `${containerHeight}px` : previewHeight !== null ? `${Math.min(previewHeight, windowSize.h * .62)}px` : "62vh"),
1415
1630
  minHeight: previewHeight !== null && !fixedHeight ? 0 : 160
1416
1631
  },
1417
- children: /* @__PURE__ */ jsx(TransformWrapper, {
1632
+ children: [/* @__PURE__ */ jsx(TransformWrapper, {
1418
1633
  ref: transformRef,
1419
1634
  minScale,
1420
1635
  maxScale,
1421
1636
  initialScale: maxScale,
1637
+ ...editor ? { panning: { excluded: [EDITOR_HANDLE_CLASS] } } : {},
1422
1638
  doubleClick: { disabled: true },
1423
1639
  limitToBounds: false,
1424
1640
  onInit: () => {
1425
1641
  setTimeout(invalidateConnectors, 0);
1426
1642
  },
1427
- onPanning: invalidateConnectors,
1428
- onPinch: invalidateConnectors,
1643
+ onPanning: moved,
1644
+ onPinch: moved,
1429
1645
  onTransform,
1430
- onWheel: invalidateConnectors,
1646
+ onWheel: moved,
1431
1647
  onZoom: invalidateConnectors,
1432
1648
  children: () => /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
1433
1649
  className: "absolute z-10 top-2 right-2 flex max-w-[calc(100%-1rem)] flex-col items-end gap-y-1",
@@ -1471,10 +1687,11 @@ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag",
1471
1687
  matrixOpen,
1472
1688
  toggleMatrix,
1473
1689
  onReady: fitGraph,
1474
- layout: placed
1690
+ layout: placed,
1691
+ editable: !!editorApi && currentPath.length === 0
1475
1692
  }, `sg-${executedJobsPathIdx}-${pathAppend}`)
1476
1693
  }) })] })
1477
- })
1694
+ }), editorOverlays]
1478
1695
  })
1479
1696
  ]
1480
1697
  }),
@@ -1523,6 +1740,10 @@ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag",
1523
1740
  })
1524
1741
  ]
1525
1742
  });
1743
+ return /* @__PURE__ */ jsx(GraphEditorProvider, {
1744
+ value: editorApi,
1745
+ children: graph
1746
+ });
1526
1747
  }
1527
1748
  var isSlot = (value) => typeof value === "number" && Number.isInteger(value) && value >= 0;
1528
1749
  /** Each job's place on the graph from its `position`; none when no job has one. */
@@ -1617,14 +1838,19 @@ function DependencyGraphPreview(inputs) {
1617
1838
  const execute = isJobRecord(on) ? on["execute"] : void 0;
1618
1839
  const executeInputs = isJobRecord(execute) ? execute["inputs"] : void 0;
1619
1840
  const workflowInputs = isJobRecord(executeInputs) ? executeInputs : void 0;
1841
+ const editable = !!inputs.editor;
1620
1842
  const executedJobs = useMemo(() => {
1621
- const withCleanup = addCleanupSteps({ jobs });
1843
+ const withCleanup = addCleanupSteps({ jobs: editable ? Object.fromEntries(Object.entries(jobs).map(([name, job]) => {
1844
+ const { if: condition, ...rest } = job;
1845
+ return [name, condition === false ? rest : job];
1846
+ })) : jobs });
1622
1847
  const evaluated = ready ? withRunExpressions(engine, withCleanup, workflowInputs) : withCleanup;
1623
1848
  return editing ? editing.expandMatrixJobs(evaluated) : evaluated;
1624
1849
  }, [
1625
1850
  engine,
1626
1851
  editing,
1627
1852
  ready,
1853
+ editable,
1628
1854
  jobs,
1629
1855
  workflowInputs
1630
1856
  ]);
@@ -1637,9 +1863,12 @@ function DependencyGraphPreview(inputs) {
1637
1863
  preview: true,
1638
1864
  removeBorder: inputs.removeBorder,
1639
1865
  layout: inputs.layout,
1866
+ editor: inputs.editor,
1640
1867
  yamlJobs: jobs,
1868
+ inputs: workflowInputs,
1869
+ workflow: inputs.yml,
1641
1870
  fixedHeight: inputs.height
1642
1871
  });
1643
1872
  }
1644
1873
  //#endregion
1645
- export { DependencyGraphPreview, addCleanupSteps, computeGraphLayout, DependencyGraph as default };
1874
+ export { DependencyGraphPreview, addCleanupSteps, computeGraphLayout, DependencyGraph as default, intoView };