@parallelworks/ui 0.19.0 → 0.19.1

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 (217) hide show
  1. package/dist/{chunks/memoryStore-CC91S5LP.js → ai/adapter/memoryStore.js} +2 -2
  2. package/dist/ai/adapter/mock/index.js +2 -80
  3. package/dist/ai/adapter/mock/mockAdapter.js +81 -0
  4. package/dist/ai/adapter/openai/createOpenAIStream.js +33 -0
  5. package/dist/ai/adapter/openai/errors.js +37 -0
  6. package/dist/ai/adapter/openai/index.js +5 -333
  7. package/dist/ai/adapter/openai/openaiAdapter.js +54 -0
  8. package/dist/ai/adapter/openai/responses.js +108 -0
  9. package/dist/ai/adapter/openai/sse.js +112 -0
  10. package/dist/{chunks/types-Yy0NrerF.js → ai/adapter/types.js} +1 -1
  11. package/dist/ai/agent/classifier.js +12 -0
  12. package/dist/ai/agent/diff.js +223 -0
  13. package/dist/ai/agent/diffHighlight.js +101 -0
  14. package/dist/ai/agent/permissions.js +29 -0
  15. package/dist/ai/agent/toolfmt.js +383 -0
  16. package/dist/ai/agent/transcript.js +127 -0
  17. package/dist/ai/components/AllocationSelector.js +56 -0
  18. package/dist/ai/components/AttachmentManager.js +144 -0
  19. package/dist/ai/components/AttachmentUpload.js +179 -0
  20. package/dist/ai/components/BlockedGroupBanner.js +29 -0
  21. package/dist/ai/components/BranchNavigator.js +57 -0
  22. package/dist/ai/components/ChatEmptyState.js +171 -0
  23. package/dist/ai/components/ChatInput.js +265 -0
  24. package/dist/ai/components/ChatLayout.js +15 -0
  25. package/dist/ai/components/ChatMessage.js +338 -0
  26. package/dist/ai/components/ChatMessageList.js +301 -0
  27. package/dist/ai/components/ChatSidebar.js +345 -0
  28. package/dist/ai/components/ChatThread.js +198 -0
  29. package/dist/ai/components/ComposerChrome.js +50 -0
  30. package/dist/ai/components/ComposerFrame.js +33 -0
  31. package/dist/ai/components/DragOverlay.js +20 -0
  32. package/dist/ai/components/ModelSelector.js +343 -0
  33. package/dist/ai/components/ShareDialog.js +260 -0
  34. package/dist/ai/components/SlashMenu.js +91 -0
  35. package/dist/ai/components/agent/ActivityLine.js +151 -0
  36. package/dist/ai/components/agent/AgentMessageParts.js +721 -0
  37. package/dist/ai/components/agent/PermissionPicker.js +38 -0
  38. package/dist/ai/components/attachmentKeys.js +10 -0
  39. package/dist/ai/core/ChatProvider.js +848 -0
  40. package/dist/ai/core/KeyboardShortcutsProvider.js +9 -0
  41. package/dist/ai/core/chatReducer.js +223 -0
  42. package/dist/ai/core/config.js +45 -0
  43. package/dist/ai/core/events.js +4 -0
  44. package/dist/ai/core/exportConversation.js +42 -0
  45. package/dist/ai/core/greeting.js +38 -0
  46. package/dist/ai/core/parts.js +84 -0
  47. package/dist/ai/core/useDragDrop.js +36 -0
  48. package/dist/ai/core/useKeyboardShortcuts.js +77 -0
  49. package/dist/ai/core/useSSEStream.js +98 -0
  50. package/dist/ai/index.js +43 -6644
  51. package/dist/ai/strings.js +175 -0
  52. package/dist/ai/ui/Avatar.js +35 -0
  53. package/dist/ai/ui/Dropdown.js +110 -0
  54. package/dist/ai/ui/Markdown.js +79 -0
  55. package/dist/ai/ui/Notice.js +77 -0
  56. package/dist/ai/ui/codeBreaks.js +36 -0
  57. package/dist/ai/ui/prose.js +13 -0
  58. package/dist/ai/ui/sidebar.js +148 -0
  59. package/dist/ai/utils.js +23 -0
  60. package/dist/components/AddMenu.js +46 -0
  61. package/dist/components/Avatar.js +46 -0
  62. package/dist/{chunks/BareModal-BtRiQezg.js → components/BareModal.js} +1 -1
  63. package/dist/components/Breadcrumbs.js +83 -0
  64. package/dist/components/Button.js +83 -0
  65. package/dist/components/Callout.js +23 -0
  66. package/dist/components/Card.js +31 -0
  67. package/dist/components/CollapsiblePanel.js +77 -0
  68. package/dist/{chunks/ConfirmModal-aOR4O1QE.js → components/ConfirmModal.js} +4 -9
  69. package/dist/components/CopyToClipboard.js +81 -0
  70. package/dist/{chunks/Markdown-CM-FenNE.js → components/CreateModal.js} +9 -116
  71. package/dist/components/DescriptionList.js +42 -0
  72. package/dist/components/Dropdown.js +349 -0
  73. package/dist/components/EmptyState.js +27 -0
  74. package/dist/components/Filter.js +32 -0
  75. package/dist/components/FilterPill.js +13 -0
  76. package/dist/components/IconButton.js +34 -0
  77. package/dist/{chunks/Indicator-hBSIxOPK.js → components/Indicator.js} +1 -1
  78. package/dist/components/Input.js +88 -0
  79. package/dist/components/LetterBadge.js +13 -0
  80. package/dist/components/Listbox.js +123 -0
  81. package/dist/{chunks/Loader-BXoXTZGc.js → components/Loader.js} +1 -1
  82. package/dist/components/Markdown.js +15 -0
  83. package/dist/components/MultiSelect.js +171 -0
  84. package/dist/components/Overview.js +39 -0
  85. package/dist/components/PageHeader.js +194 -0
  86. package/dist/components/Pagination.js +35 -0
  87. package/dist/{chunks/Provider-CDYurraE.js → components/Provider.js} +1 -1
  88. package/dist/{chunks/RequiredMark-DfLdYbYZ.js → components/RequiredMark.js} +1 -1
  89. package/dist/components/SectionHeader.js +11 -0
  90. package/dist/components/SettingModeOption.js +30 -0
  91. package/dist/components/SettingRow.js +35 -0
  92. package/dist/components/SettingsCard.js +31 -0
  93. package/dist/components/SettingsGroup.js +17 -0
  94. package/dist/components/Sort.js +42 -0
  95. package/dist/components/StatusBadge.js +63 -0
  96. package/dist/components/SwitchToggle.js +59 -0
  97. package/dist/{chunks/SwitchToggleSmall-FcsaYSTs.js → components/SwitchToggleSmall.js} +1 -1
  98. package/dist/{chunks/Table-DU2MW8xz.js → components/Table.js} +3 -3
  99. package/dist/components/Toggle.js +20 -0
  100. package/dist/{chunks/Tooltip-K5HJr4_p.js → components/Tooltip.js} +2 -27
  101. package/dist/{chunks/StatusBadge-C1Blx_cW.js → components/dropdownUtils.js} +2 -63
  102. package/dist/{chunks/focus-D_iZJTW1.js → components/focus.js} +1 -1
  103. package/dist/components/ghostButton.js +7 -0
  104. package/dist/components/inputClasses.js +4 -0
  105. package/dist/components/keys.js +27 -0
  106. package/dist/components/relativeTime.js +32 -0
  107. package/dist/{chunks/useCssIsDark-DHr05dG2.js → components/useCssIsDark.js} +1 -1
  108. package/dist/{chunks/duration-BOv8Phl8.js → duration.js} +1 -1
  109. package/dist/editor/Editor.js +13 -0
  110. package/dist/editor/EditorField.js +53 -0
  111. package/dist/editor/FormCodePanel.js +173 -0
  112. package/dist/{chunks/Monaco-DJ8MW-Dq.js → editor/Monaco.js} +4 -2
  113. package/dist/editor/index.js +6 -1
  114. package/dist/editor/themes.js +91 -0
  115. package/dist/editor/workers.js +29 -0
  116. package/dist/editor/yaml.js +15 -0
  117. package/dist/file-explorer/FileExplorer.js +1402 -0
  118. package/dist/file-explorer/FileExplorerContext.js +370 -0
  119. package/dist/{chunks/CodePreviewEditor-YqQBz1YL.js → file-explorer/FilePreview/CodePreviewEditor.js} +3 -2
  120. package/dist/file-explorer/FilePreview/Inline.js +45 -0
  121. package/dist/file-explorer/FilePreview/StreamedBadge.js +16 -0
  122. package/dist/file-explorer/FilePreview/Viewer.js +526 -0
  123. package/dist/file-explorer/FilePreview/index.js +266 -0
  124. package/dist/file-explorer/FilePreview/parse.js +63 -0
  125. package/dist/file-explorer/FilePreview/previewCache.js +98 -0
  126. package/dist/file-explorer/FilePreview/previewType.js +151 -0
  127. package/dist/file-explorer/FilePreview/readCappedBytes.js +196 -0
  128. package/dist/file-explorer/FilePreview/useObjectBlobUrl.js +95 -0
  129. package/dist/file-explorer/FilePreview/useObjectText.js +109 -0
  130. package/dist/file-explorer/FilePreview/usePresignedUrl.js +73 -0
  131. package/dist/file-explorer/FileRow.js +165 -0
  132. package/dist/file-explorer/FileRowsWindow.js +92 -0
  133. package/dist/file-explorer/FileTable.js +172 -0
  134. package/dist/file-explorer/FileTableHeaders.js +49 -0
  135. package/dist/file-explorer/LoadMoreRow.js +46 -0
  136. package/dist/file-explorer/TreeView.js +376 -0
  137. package/dist/file-explorer/helpers.js +537 -0
  138. package/dist/file-explorer/index.js +7 -5671
  139. package/dist/file-explorer/lib/batch.js +39 -0
  140. package/dist/file-explorer/lib/errors.js +9 -0
  141. package/dist/file-explorer/lib/paging.js +124 -0
  142. package/dist/file-explorer/lib/utils.js +322 -0
  143. package/dist/file-explorer/toasts.js +400 -0
  144. package/dist/file-explorer/treeRows.js +73 -0
  145. package/dist/file-explorer/useNearEnd.js +27 -0
  146. package/dist/form/FieldWrapper.js +54 -0
  147. package/dist/form/Fields/BooleanField.js +51 -0
  148. package/dist/form/Fields/CheckboxGroupField.js +72 -0
  149. package/dist/form/Fields/ColorField.js +33 -0
  150. package/dist/form/Fields/DropdownField.js +49 -0
  151. package/dist/form/Fields/DurationField.js +130 -0
  152. package/dist/form/Fields/LabelField.js +19 -0
  153. package/dist/form/Fields/MultiDropdownField.js +40 -0
  154. package/dist/form/Fields/NumberField.js +51 -0
  155. package/dist/form/Fields/ObjectField.js +54 -0
  156. package/dist/form/Fields/RadioField.js +49 -0
  157. package/dist/form/Fields/RangeField.js +58 -0
  158. package/dist/form/Fields/ResourceOptionIcon.js +21 -0
  159. package/dist/form/Fields/StepField.js +6 -0
  160. package/dist/form/Fields/StringField.js +37 -0
  161. package/dist/form/Fields/TextAreaField.js +31 -0
  162. package/dist/form/Fields/durationValidity.js +11 -0
  163. package/dist/form/Form.js +643 -0
  164. package/dist/form/SpecialTypes/DropdownWrapper.js +30 -0
  165. package/dist/form/SpecialTypes/FormikCustomDropdown.js +122 -0
  166. package/dist/form/SpecialTypes/FormikCustomInput.js +111 -0
  167. package/dist/form/SpecialTypes/FormikMultiSelectDropdown.js +31 -0
  168. package/dist/form/SpecialTypes/FormikNumericInput.js +60 -0
  169. package/dist/form/Wizard/WizardContainer.js +138 -0
  170. package/dist/form/Wizard/WizardNavigation.js +92 -0
  171. package/dist/form/Wizard/WizardStepContent.js +35 -0
  172. package/dist/form/Wizard/WizardStepIndicator.js +108 -0
  173. package/dist/form/Wizard/useWizardState.js +118 -0
  174. package/dist/form/Wizard/utils.js +34 -0
  175. package/dist/form/fieldContext.js +26 -0
  176. package/dist/form/fieldRegistry.js +50 -0
  177. package/dist/form/index.js +17 -2321
  178. package/dist/{chunks/FieldWrapper-Cszwg-4B.js → form/lib.js} +2 -97
  179. package/dist/form/useParsedOpts.js +62 -0
  180. package/dist/form/usePrevious.js +11 -0
  181. package/dist/form/utils/checkShowIfObject.js +14 -0
  182. package/dist/form/utils/duration.js +69 -0
  183. package/dist/form/utils/getParentValue.js +23 -0
  184. package/dist/form/utils/getPlaceholder.js +9 -0
  185. package/dist/form/utils/getValueUsingPath.js +23 -0
  186. package/dist/form/utils/secondaryField.js +22 -0
  187. package/dist/graph/AnnotationBanner.js +141 -0
  188. package/dist/graph/Collapse.js +41 -0
  189. package/dist/graph/DependencyGraph.js +1398 -0
  190. package/dist/graph/JobSummary.js +104 -0
  191. package/dist/graph/MatrixGroup.js +107 -0
  192. package/dist/graph/Reveal.js +57 -0
  193. package/dist/graph/TreeView.js +711 -0
  194. package/dist/graph/index.js +1 -2519
  195. package/dist/graph/types.js +6 -0
  196. package/dist/index.js +46 -599
  197. package/dist/list/ActionToolbar.js +184 -0
  198. package/dist/list/HoverCard.js +73 -0
  199. package/dist/list/ListPage.js +145 -0
  200. package/dist/list/ListPager.js +59 -0
  201. package/dist/list/ListTable.js +163 -0
  202. package/dist/list/ListViewControls.js +372 -0
  203. package/dist/list/RowContextMenu.js +350 -0
  204. package/dist/list/UserHoverCard.js +117 -0
  205. package/dist/list/index.js +11 -2
  206. package/dist/list/listSearch.js +137 -0
  207. package/dist/list/listView.js +210 -0
  208. package/dist/list/textEntry.js +10 -0
  209. package/dist/{chunks/logviewer-BTKuQWzw.js → logviewer/LogViewer.js} +4 -82
  210. package/dist/logviewer/index.js +2 -1
  211. package/dist/logviewer/segments.js +80 -0
  212. package/package.json +1 -1
  213. package/dist/chunks/PageHeader-0kG_iypP.js +0 -692
  214. package/dist/chunks/SwitchToggle-DShNdcao.js +0 -758
  215. package/dist/chunks/editor-Dk1rbV75.js +0 -361
  216. package/dist/chunks/list-CJm875m7.js +0 -1418
  217. /package/dist/{chunks/MarkdownImpl-CyeTVvSt.js → components/MarkdownImpl.js} +0 -0
@@ -0,0 +1,1398 @@
1
+ import { useRunFile, useSlots, useStrings, useWorkflowEngine } from "../components/Provider.js";
2
+ import { ArrowLeftIcon, ArrowRightIcon, DocumentIcon } from "../icons.js";
3
+ import { TooltipInfo } from "../components/Tooltip.js";
4
+ import { BareModal, modalPanelClasses } from "../components/BareModal.js";
5
+ import { Indicator } from "../components/Indicator.js";
6
+ import Loader from "../components/Loader.js";
7
+ import { LogViewer } from "../logviewer/LogViewer.js";
8
+ import { toAbsHumanDuration } from "../duration.js";
9
+ import { AnnotationBanner } from "./AnnotationBanner.js";
10
+ import { Collapse } from "./Collapse.js";
11
+ import { Reveal } from "./Reveal.js";
12
+ import { Joblist } from "./JobSummary.js";
13
+ import { MatrixGroupNode, MatrixGroupSummaryItem } from "./MatrixGroup.js";
14
+ import TreeView from "./TreeView.js";
15
+ import { isJobRecord } from "./types.js";
16
+ import cx from "classnames";
17
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
18
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
19
+ import { flushSync } from "react-dom";
20
+ import { DateTime } from "luxon";
21
+ import { TransformComponent, TransformWrapper } from "react-zoom-pan-pinch";
22
+ //#region src/graph/DependencyGraph.tsx
23
+ function ViewToggle({ viewMode, setViewMode }) {
24
+ return /* @__PURE__ */ jsxs("div", {
25
+ className: "flex border border-[var(--theme-border)] rounded overflow-hidden",
26
+ children: [/* @__PURE__ */ jsx("button", {
27
+ type: "button",
28
+ onClick: () => setViewMode("dag"),
29
+ className: cx("px-3 py-1.5 text-xs font-mono transition-colors", viewMode === "dag" ? "bg-[var(--theme-element)] text-[var(--theme-element-text)]" : "text-[var(--theme-muted-text-color)] hover:text-[var(--theme-app)] hover:bg-[var(--theme-hover)]"),
30
+ children: "DAG"
31
+ }), /* @__PURE__ */ jsx("button", {
32
+ type: "button",
33
+ onClick: () => setViewMode("tree"),
34
+ className: cx("px-3 py-1.5 text-xs font-mono transition-colors", viewMode === "tree" ? "bg-[var(--theme-element)] text-[var(--theme-element-text)]" : "text-[var(--theme-muted-text-color)] hover:text-[var(--theme-app)] hover:bg-[var(--theme-hover)]"),
35
+ children: "Tree"
36
+ })]
37
+ });
38
+ }
39
+ var addCleanupSteps = ({ jobs }) => {
40
+ const clonedJobs = structuredClone(jobs);
41
+ for (const job in jobs) {
42
+ const source = jobs[job];
43
+ const cloned = clonedJobs[job];
44
+ if (!source || !cloned) continue;
45
+ if (Array.isArray(source.steps)) {
46
+ const sourceSteps = source.steps;
47
+ sourceSteps.slice().reverse().forEach((step, index) => {
48
+ if (!step.cleanup) return;
49
+ const linkedName = step.name ? step.name : `Step ${sourceSteps.length - index - 1}`;
50
+ const newStep = {
51
+ ...step,
52
+ name: `POST ${linkedName}`,
53
+ run: step.cleanup,
54
+ linkedStep: `${sourceSteps.length - index - 1}`
55
+ };
56
+ delete newStep.cleanup;
57
+ const clonedStep = cloned.steps[index];
58
+ if (clonedStep) delete clonedStep.cleanup;
59
+ cloned.steps.push(newStep);
60
+ });
61
+ }
62
+ const sourceCleanup = source.cleanup;
63
+ const clonedCleanup = cloned.cleanup;
64
+ if (Array.isArray(sourceCleanup) && clonedCleanup) {
65
+ sourceCleanup.slice().reverse().forEach((step, index) => {
66
+ if (!step.cleanup) return;
67
+ const linkedName = step.name ? step.name : `Cleanup ${sourceCleanup.length - index - 1}`;
68
+ const newStep = {
69
+ ...step,
70
+ name: `POST ${linkedName}`,
71
+ run: step.cleanup,
72
+ linkedStep: `${sourceCleanup.length - index - 1 + cloned.steps.length}`
73
+ };
74
+ delete newStep.cleanup;
75
+ const clonedStep = clonedCleanup[index];
76
+ if (clonedStep) delete clonedStep.cleanup;
77
+ clonedCleanup.push(newStep);
78
+ });
79
+ for (const clonedStep of clonedCleanup) clonedStep.linkedStep = ".";
80
+ cloned.steps = [...cloned.steps, ...clonedCleanup];
81
+ delete cloned.cleanup;
82
+ }
83
+ }
84
+ return clonedJobs;
85
+ };
86
+ var PREVIEW_PADDING = 24;
87
+ function measurePreviewHeight(content, viewRef) {
88
+ const nodes = content?.querySelectorAll("[id^=\"node_\"]");
89
+ const transform = content?.parentElement ? getComputedStyle(content.parentElement).transform : "none";
90
+ if (!content || !nodes?.length || transform === "none") return null;
91
+ if (!viewRef.current) {
92
+ const view = new DOMMatrixReadOnly(transform);
93
+ viewRef.current = {
94
+ scale: view.a,
95
+ offsetY: view.f
96
+ };
97
+ }
98
+ const { scale, offsetY } = viewRef.current;
99
+ let bottom = 0;
100
+ for (const node of nodes) bottom = Math.max(bottom, offsetWithin(node, content).y + node.offsetHeight);
101
+ return Math.ceil(bottom * scale + offsetY + PREVIEW_PADDING);
102
+ }
103
+ function offsetWithin(el, ancestor) {
104
+ let x = 0;
105
+ let y = 0;
106
+ let cur = el;
107
+ while (cur && cur !== ancestor) {
108
+ x += cur.offsetLeft;
109
+ y += cur.offsetTop;
110
+ cur = cur.offsetParent;
111
+ }
112
+ return {
113
+ x,
114
+ y
115
+ };
116
+ }
117
+ function parsePrefixToPath(prefix) {
118
+ const parts = prefix.split("/").filter(Boolean);
119
+ const path = [];
120
+ for (let i = 0; i + 2 < parts.length; i += 3) {
121
+ const name = parts[i + 1];
122
+ const step = parts[i + 2];
123
+ if (parts[i] === "subworkflows" && name && step?.startsWith("step_")) path.push(name, "steps", Number(step.slice(5)), "subworkflow", "jobs");
124
+ }
125
+ return path;
126
+ }
127
+ function computeGraphLayout(engine, jobs, matrixGroups) {
128
+ const visibleJobs = structuredClone(jobs);
129
+ for (const jobName of Object.keys(jobs ?? {})) if (jobs[jobName]?.if === false) delete visibleJobs[jobName];
130
+ engine.collapseMatrixGroups(matrixGroups, visibleJobs);
131
+ const allDeps = {};
132
+ for (const jobName of Object.keys(visibleJobs)) allDeps[jobName] = engine.jobDependencies(jobName, visibleJobs, allDeps);
133
+ const filteredDeps = {};
134
+ for (const jobName of Object.keys(visibleJobs)) {
135
+ const needs = visibleJobs[jobName]?.needs;
136
+ const oldNeeds = Array.isArray(needs) ? needs : [];
137
+ filteredDeps[jobName] = oldNeeds.filter((dep) => {
138
+ return !oldNeeds.some((other) => {
139
+ if (other === dep) return false;
140
+ return allDeps[other]?.has(dep) === true;
141
+ });
142
+ }).sort();
143
+ }
144
+ const depended_on = {};
145
+ Object.keys(visibleJobs).forEach((jobName) => {
146
+ if (filteredDeps[jobName] && Array.isArray(filteredDeps[jobName])) filteredDeps[jobName].forEach((dep) => {
147
+ if (depended_on[dep]) depended_on[dep].push(jobName);
148
+ else depended_on[dep] = [jobName];
149
+ });
150
+ });
151
+ Object.keys(visibleJobs).forEach((jobName) => {
152
+ if (depended_on[jobName]) depended_on[jobName].sort();
153
+ });
154
+ const unfinished = structuredClone(visibleJobs);
155
+ const rootLevel = [];
156
+ const deps = [rootLevel];
157
+ Object.keys(visibleJobs).forEach((jobName) => {
158
+ if (filteredDeps[jobName]?.length === 0) {
159
+ rootLevel.push(jobName);
160
+ delete unfinished[jobName];
161
+ }
162
+ });
163
+ let prevLen;
164
+ do {
165
+ const toAppend = [];
166
+ const unfinishedArr = Object.keys(unfinished);
167
+ prevLen = unfinishedArr.length;
168
+ unfinishedArr.forEach((jobName) => {
169
+ if ((filteredDeps[jobName] ?? []).every((dep) => !unfinishedArr.includes(dep))) {
170
+ toAppend.push(jobName);
171
+ delete unfinished[jobName];
172
+ }
173
+ });
174
+ if (toAppend.length > 0) deps.push(toAppend);
175
+ } while (prevLen !== Object.keys(unfinished).length);
176
+ const finalDeps = [];
177
+ deps.forEach((col) => {
178
+ const toAppend = [];
179
+ while (col.length > 0) {
180
+ const head = col[0];
181
+ if (head === void 0) break;
182
+ const grouped = [head];
183
+ const toRm = [0];
184
+ col.slice(1).forEach((jobName, j) => {
185
+ if (visibleJobs[head]?._matrixGroup || visibleJobs[jobName]?._matrixGroup) return;
186
+ const depsA = depended_on[head];
187
+ const depsB = depended_on[jobName];
188
+ const depsA2 = filteredDeps[head];
189
+ const depsB2 = filteredDeps[jobName];
190
+ if (!depsA && !depsB || depsA && depsB && depsA.length === depsB.length) {
191
+ if (!depsA2 && !depsB2 || depsA2 && depsB2 && depsA2.length === depsB2.length) {
192
+ if ((!depsA || depsA.every((dep, i) => dep === depsB?.[i])) && (!depsA2 || depsA2.every((dep, i) => dep === depsB2?.[i]))) {
193
+ grouped.push(jobName);
194
+ toRm.push(j + 1);
195
+ }
196
+ }
197
+ }
198
+ });
199
+ col.splice(0, col.length, ...col.filter((_, i) => !toRm.includes(i)));
200
+ toAppend.push(grouped);
201
+ }
202
+ finalDeps.push(toAppend);
203
+ });
204
+ while (finalDeps.length > 0) {
205
+ const first = finalDeps[0];
206
+ if (!first) break;
207
+ if (first.length === 0) {
208
+ finalDeps.splice(0, 1);
209
+ continue;
210
+ }
211
+ if (first[0]?.length !== 0) break;
212
+ while (first.length > 0 && first[0]?.length === 0) first.splice(0, 1);
213
+ }
214
+ const ancestorDists = {};
215
+ const descendantDists = {};
216
+ const jobNames = Object.keys(visibleJobs);
217
+ const reverseDeps = {};
218
+ for (const [job, depList] of Object.entries(filteredDeps)) for (const dep of depList) {
219
+ if (!reverseDeps[dep]) reverseDeps[dep] = [];
220
+ reverseDeps[dep].push(job);
221
+ }
222
+ for (const start of jobNames) {
223
+ const aDist = /* @__PURE__ */ new Map();
224
+ const q1 = [[start, 0]];
225
+ while (q1.length > 0) {
226
+ const [job, d] = q1.shift();
227
+ for (const dep of filteredDeps[job] ?? []) if (!aDist.has(dep)) {
228
+ aDist.set(dep, d + 1);
229
+ q1.push([dep, d + 1]);
230
+ }
231
+ }
232
+ ancestorDists[start] = aDist;
233
+ const dDist = /* @__PURE__ */ new Map();
234
+ const q2 = [[start, 0]];
235
+ while (q2.length > 0) {
236
+ const [job, d] = q2.shift();
237
+ for (const child of reverseDeps[job] ?? []) if (!dDist.has(child)) {
238
+ dDist.set(child, d + 1);
239
+ q2.push([child, d + 1]);
240
+ }
241
+ }
242
+ descendantDists[start] = dDist;
243
+ }
244
+ return {
245
+ dependencyCols: finalDeps,
246
+ directDeps: filteredDeps,
247
+ displayJobs: visibleJobs,
248
+ ancestorDists,
249
+ descendantDists
250
+ };
251
+ }
252
+ function SubworkflowWell({ expanded, onSettled, className, children }) {
253
+ return /* @__PURE__ */ jsx(Reveal, {
254
+ open: expanded,
255
+ growWidth: true,
256
+ onSettled,
257
+ children: /* @__PURE__ */ jsx("div", {
258
+ className,
259
+ style: { zoom: .8 },
260
+ children
261
+ })
262
+ });
263
+ }
264
+ var SUBGRAPH_Z_STEP = 100;
265
+ var FIT_ANIMATION_MS = 300;
266
+ function Subgraph({ jobs, pathPrefix, depth, preview, animT, expandedSubworkflows, toggleSubworkflow, openInNewGraph, setSublogOpen, invalidateConnectors, requestFit, stepsOpen, toggleSteps, matrixOpen, toggleMatrix, onReady }) {
267
+ const engine = useWorkflowEngine();
268
+ const wrapperRef = useRef(null);
269
+ const zBase = depth * SUBGRAPH_Z_STEP;
270
+ const [hoveredJob, setHoveredJob] = useState(null);
271
+ const lastHoveredDistancesRef = useRef(null);
272
+ const maxHoveredDistRef = useRef(0);
273
+ const retractPathLensRef = useRef(/* @__PURE__ */ new Map());
274
+ const matrixGroups = useMemo(() => engine.matrixGroups(jobs), [engine, jobs]);
275
+ const { dependencyCols, directDeps, displayJobs, ancestorDists, descendantDists } = useMemo(() => computeGraphLayout(engine, jobs, matrixGroups), [
276
+ engine,
277
+ jobs,
278
+ matrixGroups
279
+ ]);
280
+ const hoveredDistances = useMemo(() => {
281
+ if (!hoveredJob) return null;
282
+ const dist = /* @__PURE__ */ new Map();
283
+ dist.set(hoveredJob, 0);
284
+ const ancestors = ancestorDists[hoveredJob];
285
+ if (ancestors) for (const [k, v] of ancestors) dist.set(k, v);
286
+ const descendants = descendantDists[hoveredJob];
287
+ if (descendants) for (const [k, v] of descendants) dist.set(k, v);
288
+ return dist;
289
+ }, [
290
+ hoveredJob,
291
+ ancestorDists,
292
+ descendantDists
293
+ ]);
294
+ if (hoveredDistances) {
295
+ lastHoveredDistancesRef.current = hoveredDistances;
296
+ maxHoveredDistRef.current = Math.max(...hoveredDistances.values());
297
+ }
298
+ const activeDists = hoveredDistances ?? lastHoveredDistancesRef.current;
299
+ useEffect(() => {
300
+ if (hoveredJob || !lastHoveredDistancesRef.current) return;
301
+ const maxDelay = maxHoveredDistRef.current * animT + animT;
302
+ const timer = setTimeout(() => {
303
+ lastHoveredDistancesRef.current = null;
304
+ maxHoveredDistRef.current = 0;
305
+ invalidateConnectors();
306
+ }, maxDelay * 1e3 + 200);
307
+ return () => clearTimeout(timer);
308
+ }, [
309
+ hoveredJob,
310
+ invalidateConnectors,
311
+ animT
312
+ ]);
313
+ const hoveredRelated = useMemo(() => {
314
+ if (!hoveredDistances) return null;
315
+ return new Set(hoveredDistances.keys());
316
+ }, [hoveredDistances]);
317
+ useEffect(() => {
318
+ const raf = requestAnimationFrame(() => {
319
+ invalidateConnectors();
320
+ onReady?.();
321
+ });
322
+ return () => cancelAnimationFrame(raf);
323
+ }, [
324
+ dependencyCols,
325
+ invalidateConnectors,
326
+ onReady
327
+ ]);
328
+ useEffect(() => {
329
+ const el = wrapperRef.current;
330
+ if (!el) return;
331
+ const ro = new ResizeObserver(() => invalidateConnectors());
332
+ ro.observe(el);
333
+ return () => ro.disconnect();
334
+ }, [invalidateConnectors]);
335
+ const didRevealMountRef = useRef(false);
336
+ useEffect(() => {
337
+ if (!didRevealMountRef.current) {
338
+ didRevealMountRef.current = true;
339
+ return;
340
+ }
341
+ let raf = 0;
342
+ const start = performance.now();
343
+ const tick = () => {
344
+ flushSync(() => invalidateConnectors());
345
+ if (performance.now() - start < 260) raf = requestAnimationFrame(tick);
346
+ };
347
+ raf = requestAnimationFrame(tick);
348
+ return () => cancelAnimationFrame(raf);
349
+ }, [
350
+ stepsOpen,
351
+ matrixOpen,
352
+ expandedSubworkflows,
353
+ invalidateConnectors
354
+ ]);
355
+ const buildBoxLayer = useCallback(() => {
356
+ const isStepsOpen = (j) => stepsOpen.has(pathPrefix + j);
357
+ const onJobClick = (j) => {
358
+ const status = jobs[j]?.status ?? displayJobs[j]?.status;
359
+ if (!isStepsOpen(j) && (status === "skipped" || status === "skipped-failed")) return;
360
+ toggleSteps(pathPrefix + j);
361
+ };
362
+ const expandInline = (j, s) => {
363
+ toggleSubworkflow(`${pathPrefix}${j}:${s}`);
364
+ requestAnimationFrame(invalidateConnectors);
365
+ };
366
+ const openNew = (j, s) => openInNewGraph(pathPrefix, j, s);
367
+ const onStepClick = (j, s) => {
368
+ const stepStatus = jobs[j]?.steps?.[+s]?.status;
369
+ if (stepStatus === "skipped" || stepStatus === "skipped-failed") return;
370
+ if (jobs[j]?.steps[+s]?.subworkflow) expandInline(j, +s);
371
+ else setSublogOpen(engine.stepLogPath(pathPrefix, j, s));
372
+ };
373
+ const onStepLogClick = (j, s) => setSublogOpen(engine.stepLogPath(pathPrefix, j, s));
374
+ const onSubworkflowIcon = (j, s) => openNew(j, s);
375
+ const isSubworkflowExpanded = (j, s) => expandedSubworkflows.has(`${pathPrefix}${j}:${s}`);
376
+ const renderSubworkflow = (j, s) => {
377
+ const subJobs = jobs[j]?.steps?.[s]?.subworkflow?.jobs;
378
+ if (!subJobs) return null;
379
+ return /* @__PURE__ */ jsx(SubworkflowWell, {
380
+ expanded: isSubworkflowExpanded(j, s),
381
+ onSettled: requestFit,
382
+ className: "my-2 rounded-lg border-4 border-(--theme-border)",
383
+ children: /* @__PURE__ */ jsx(NestedSubgraph, {
384
+ jobs: subJobs,
385
+ pathPrefix: `${pathPrefix}subworkflows/${j}/step_${s}/`,
386
+ depth: depth + 1,
387
+ preview,
388
+ animT,
389
+ expandedSubworkflows,
390
+ toggleSubworkflow,
391
+ openInNewGraph,
392
+ setSublogOpen,
393
+ invalidateConnectors,
394
+ requestFit,
395
+ stepsOpen,
396
+ toggleSteps,
397
+ matrixOpen,
398
+ toggleMatrix
399
+ })
400
+ });
401
+ };
402
+ return /* @__PURE__ */ jsx("div", {
403
+ className: "flex relative",
404
+ children: dependencyCols.map((col) => {
405
+ return /* @__PURE__ */ jsx("div", { children: col.map((jobNames) => {
406
+ const matrixGroup = displayJobs[jobNames[0]]?._matrixGroup;
407
+ if (matrixGroup) return /* @__PURE__ */ jsx(MatrixGroupNode, {
408
+ matrixGroup,
409
+ jobNames,
410
+ idPrefix: pathPrefix,
411
+ isExpanded: matrixOpen.has(pathPrefix + matrixGroup.originaljob),
412
+ onToggle: () => toggleMatrix(pathPrefix + matrixGroup.originaljob),
413
+ jobs,
414
+ activeDists,
415
+ onMouseEnter: () => setHoveredJob(jobNames[0]),
416
+ onMouseLeave: () => setHoveredJob(null),
417
+ hoveredRelated,
418
+ animT,
419
+ zBase,
420
+ isStepsOpen,
421
+ onJobClick,
422
+ onStepClick,
423
+ onStepLogClick,
424
+ onSubworkflowIcon,
425
+ isSubworkflowExpanded,
426
+ renderSubworkflow,
427
+ preview
428
+ }, "node_" + jobNames[0]);
429
+ return /* @__PURE__ */ jsxs("div", {
430
+ id: "node_" + pathPrefix + jobNames[0],
431
+ role: "none",
432
+ className: "relative m-24",
433
+ style: { zIndex: (activeDists?.has(jobNames[0]) ? 25 : 1) + zBase },
434
+ onMouseEnter: () => setHoveredJob(jobNames[0]),
435
+ onMouseLeave: () => setHoveredJob(null),
436
+ onFocus: () => setHoveredJob(jobNames[0]),
437
+ onBlur: () => setHoveredJob(null),
438
+ children: [/* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-2xl bg-(--theme-panel-bg)" }), /* @__PURE__ */ jsx("div", {
439
+ className: "relative border-solid shadow py-4 px-8 rounded-xl border-4 whitespace-nowrap bg-(--theme-panel-bg) text-2xl",
440
+ style: {
441
+ opacity: hoveredRelated && !hoveredRelated.has(jobNames[0]) ? .5 : 1,
442
+ transition: `opacity ${animT}s`
443
+ },
444
+ children: /* @__PURE__ */ jsx(Joblist, {
445
+ jobs: displayJobs,
446
+ jobNames,
447
+ onJobClick,
448
+ isStepsOpen,
449
+ onStepClick,
450
+ onStepLogClick,
451
+ onSubworkflowIcon,
452
+ isSubworkflowExpanded,
453
+ renderSubworkflow,
454
+ growWidth: true,
455
+ preview
456
+ })
457
+ })]
458
+ }, "node_" + jobNames[0]);
459
+ }) }, col[0]?.[0]);
460
+ })
461
+ });
462
+ }, [
463
+ dependencyCols,
464
+ displayJobs,
465
+ jobs,
466
+ stepsOpen,
467
+ matrixOpen,
468
+ expandedSubworkflows,
469
+ hoveredRelated,
470
+ activeDists,
471
+ animT,
472
+ zBase,
473
+ preview,
474
+ pathPrefix,
475
+ depth,
476
+ toggleSteps,
477
+ toggleSubworkflow,
478
+ toggleMatrix,
479
+ openInNewGraph,
480
+ setSublogOpen,
481
+ engine,
482
+ invalidateConnectors,
483
+ requestFit
484
+ ]);
485
+ const memoizedBoxLayer = useMemo(() => buildBoxLayer(), [buildBoxLayer]);
486
+ const hasLiveNestedGraph = Array.from(expandedSubworkflows).some((k) => k.startsWith(pathPrefix));
487
+ if (!Array.isArray(dependencyCols) || dependencyCols.length === 0 || !Array.isArray(dependencyCols[0]) || dependencyCols[0].length === 0 || !Array.isArray(dependencyCols[0][0]) || dependencyCols[0][0].length === 0) return null;
488
+ return /* @__PURE__ */ jsxs("div", {
489
+ ref: wrapperRef,
490
+ className: "relative",
491
+ children: [(() => {
492
+ const wrapper = wrapperRef.current;
493
+ const gid = (name) => document.getElementById("node_" + pathPrefix + name);
494
+ const ox = (el) => offsetWithin(el, wrapper).x;
495
+ const nodeConnectorY = (el) => offsetWithin(el, wrapper).y + 37;
496
+ let colGap = 32;
497
+ if (dependencyCols.length >= 2) {
498
+ const col0 = gid(dependencyCols[0]?.[0]?.[0] ?? "");
499
+ const col1 = gid(dependencyCols[1]?.[0]?.[0] ?? "");
500
+ if (col0 && col1) colGap = ox(col1) - (ox(col0) + col0.offsetWidth);
501
+ }
502
+ let minAdy = Number.POSITIVE_INFINITY;
503
+ for (const col of dependencyCols) for (const jobNames of col) for (const dep of directDeps[jobNames[0]] ?? []) {
504
+ const s = gid(dep);
505
+ const e = gid(jobNames[0]);
506
+ if (!s || !e) continue;
507
+ const dy = Math.abs(nodeConnectorY(e) - nodeConnectorY(s));
508
+ if (dy > 0 && dy < minAdy) minAdy = dy;
509
+ }
510
+ const MAX_ARC_R = 16;
511
+ const arcR = Math.min(minAdy === Number.POSITIVE_INFINITY ? MAX_ARC_R : minAdy / 4, colGap / 2, MAX_ARC_R);
512
+ return dependencyCols.map((col) => /* @__PURE__ */ jsx(React.Fragment, { children: col.map((jobNames) => /* @__PURE__ */ jsx(React.Fragment, { children: (directDeps[jobNames[0]] ?? []).map((dep) => {
513
+ const isHighlighted = hoveredRelated?.has(jobNames[0]) && hoveredRelated?.has(dep);
514
+ const baseColor = "var(--theme-border)";
515
+ const depDist = activeDists?.get(dep) ?? 0;
516
+ const targetDist = activeDists?.get(jobNames[0]) ?? 0;
517
+ const hopDelay = animT;
518
+ const closerDist = Math.min(depDist, targetDist);
519
+ const connectorDelay = closerDist * hopDelay;
520
+ const retractDelay = (maxHoveredDistRef.current - closerDist) * hopDelay;
521
+ const drawRightToLeft = targetDist < depDist;
522
+ const start = gid(dep);
523
+ const end = gid(jobNames[0]);
524
+ if (!start || !end) return null;
525
+ const x1 = ox(start) + start.offsetWidth - 2;
526
+ const y1 = nodeConnectorY(start);
527
+ const x2 = ox(end) + 2;
528
+ const y2 = nodeConnectorY(end);
529
+ const dy = y2 - y1;
530
+ const ady = Math.abs(dy);
531
+ let pathD;
532
+ let pathLen;
533
+ let pathDReversed;
534
+ const r = arcR;
535
+ if (ady === 0) {
536
+ pathD = `M ${x1} ${y1} L ${x2} ${y2}`;
537
+ pathDReversed = `M ${x2} ${y2} L ${x1} ${y1}`;
538
+ pathLen = x2 - x1;
539
+ } else {
540
+ const s = dy > 0 ? 1 : -1;
541
+ const ax2end = x2 - (colGap / 2 - r);
542
+ const ax = ax2end - 2 * r;
543
+ const sw1 = dy > 0 ? 1 : 0;
544
+ const sw2 = dy > 0 ? 0 : 1;
545
+ pathD = [
546
+ `M ${x1} ${y1}`,
547
+ `L ${ax} ${y1}`,
548
+ `A ${r} ${r} 0 0 ${sw1} ${ax + r} ${y1 + s * r}`,
549
+ `L ${ax + r} ${y2 - s * r}`,
550
+ `A ${r} ${r} 0 0 ${sw2} ${ax2end} ${y2}`,
551
+ `L ${x2} ${y2}`
552
+ ].join(" ");
553
+ pathDReversed = [
554
+ `M ${x2} ${y2}`,
555
+ `L ${ax2end} ${y2}`,
556
+ `A ${r} ${r} 0 0 ${sw1} ${ax + r} ${y2 - s * r}`,
557
+ `L ${ax + r} ${y1 + s * r}`,
558
+ `A ${r} ${r} 0 0 ${sw2} ${ax} ${y1}`,
559
+ `L ${x1} ${y1}`
560
+ ].join(" ");
561
+ const arcLen = Math.PI * r / 2;
562
+ const vSeg = Math.max(0, ady - 2 * r);
563
+ pathLen = ax - x1 + arcLen + vSeg + arcLen + (x2 - ax2end);
564
+ }
565
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
566
+ /* @__PURE__ */ jsxs("svg", {
567
+ "aria-hidden": "true",
568
+ overflow: "visible",
569
+ className: "absolute pointer-events-none",
570
+ style: { zIndex: (isHighlighted || activeDists?.has(dep) && activeDists?.has(jobNames[0]) ? 30 : 10) + zBase },
571
+ children: [
572
+ /* @__PURE__ */ jsx("rect", {
573
+ x: x1 - 16,
574
+ y: y1 - 16,
575
+ width: 32,
576
+ height: 32,
577
+ fill: "var(--theme-panel-bg)"
578
+ }),
579
+ /* @__PURE__ */ jsx("rect", {
580
+ x: x2 - 16,
581
+ y: y2 - 16,
582
+ width: 32,
583
+ height: 32,
584
+ fill: "var(--theme-panel-bg)"
585
+ }),
586
+ /* @__PURE__ */ jsx("circle", {
587
+ cx: x1,
588
+ cy: y1,
589
+ r: "8",
590
+ fill: isHighlighted ? "#3B82F6" : baseColor,
591
+ style: {
592
+ opacity: hoveredRelated && !isHighlighted ? .3 : 1,
593
+ transition: isHighlighted ? `fill ${animT}s ${depDist * hopDelay}s, opacity 0s` : `fill ${animT}s ${(maxHoveredDistRef.current - depDist) * hopDelay}s, opacity 0s`
594
+ }
595
+ }),
596
+ /* @__PURE__ */ jsx("circle", {
597
+ cx: x2,
598
+ cy: y2,
599
+ r: "8",
600
+ fill: isHighlighted ? "#3B82F6" : baseColor,
601
+ style: {
602
+ opacity: hoveredRelated && !isHighlighted ? .3 : 1,
603
+ transition: isHighlighted ? `fill ${animT}s ${targetDist * hopDelay}s, opacity 0s` : `fill ${animT}s ${(maxHoveredDistRef.current - targetDist) * hopDelay}s, opacity 0s`
604
+ }
605
+ })
606
+ ]
607
+ }),
608
+ /* @__PURE__ */ jsxs("svg", {
609
+ "aria-hidden": "true",
610
+ overflow: "visible",
611
+ className: "absolute pointer-events-none",
612
+ style: { zIndex: (isHighlighted ? 20 : -1) + zBase },
613
+ children: [/* @__PURE__ */ jsx("path", {
614
+ d: pathD,
615
+ stroke: "var(--theme-panel-bg)",
616
+ strokeWidth: "6",
617
+ fill: "transparent"
618
+ }), /* @__PURE__ */ jsx("path", {
619
+ d: pathD,
620
+ stroke: baseColor,
621
+ strokeWidth: "6",
622
+ fill: "transparent",
623
+ style: {
624
+ opacity: hoveredRelated && !isHighlighted ? .3 : 1,
625
+ transition: `opacity 0s`
626
+ }
627
+ })]
628
+ }),
629
+ /* @__PURE__ */ jsxs("svg", {
630
+ "aria-hidden": "true",
631
+ overflow: "visible",
632
+ className: "absolute pointer-events-none",
633
+ style: { zIndex: 20 + zBase },
634
+ children: [/* @__PURE__ */ jsx("path", {
635
+ d: drawRightToLeft ? pathDReversed : pathD,
636
+ fill: "transparent",
637
+ style: {
638
+ stroke: "#3B82F6",
639
+ strokeWidth: 6,
640
+ strokeDasharray: `${pathLen} ${pathLen}`,
641
+ strokeDashoffset: isHighlighted ? 0 : pathLen,
642
+ opacity: isHighlighted ? 1 : 0,
643
+ transition: isHighlighted ? `stroke-dashoffset ${animT}s ease ${connectorDelay}s, opacity 0s ease ${connectorDelay}s` : "stroke-dashoffset 0s, opacity 0s"
644
+ }
645
+ }), (() => {
646
+ const connKey = `${dep}→${jobNames[0]}`;
647
+ if (isHighlighted) retractPathLensRef.current.set(connKey, pathLen);
648
+ const isRetracting = !isHighlighted && !!activeDists?.has(dep) && !!activeDists?.has(jobNames[0]);
649
+ if (!isHighlighted && !isRetracting) return null;
650
+ const frozenLen = retractPathLensRef.current.get(connKey) ?? pathLen;
651
+ return /* @__PURE__ */ jsx("path", {
652
+ d: drawRightToLeft ? pathDReversed : pathD,
653
+ fill: "transparent",
654
+ style: {
655
+ stroke: "#3B82F6",
656
+ strokeWidth: 6,
657
+ strokeDasharray: `${frozenLen} ${frozenLen}`,
658
+ strokeDashoffset: isHighlighted ? 0 : frozenLen,
659
+ opacity: isHighlighted ? 0 : 1,
660
+ transition: isHighlighted ? "stroke-dashoffset 0s, opacity 0s" : `stroke-dashoffset ${animT}s ease ${retractDelay}s, opacity 0s`
661
+ }
662
+ });
663
+ })()]
664
+ })
665
+ ] }, dep);
666
+ }) }, jobNames[0])) }, col[0]?.[0]));
667
+ })(), hasLiveNestedGraph ? buildBoxLayer() : memoizedBoxLayer]
668
+ });
669
+ }
670
+ var NestedSubgraph = Subgraph;
671
+ function SidebarWell({ expanded, children }) {
672
+ return /* @__PURE__ */ jsx(Collapse, {
673
+ expanded,
674
+ releaseOverflow: false,
675
+ children
676
+ });
677
+ }
678
+ function SidebarJobs({ jobs, pathPrefix, preview, stepsOpen, toggleSteps, expandedSubworkflows, toggleSubworkflow, matrixOpen, toggleMatrix, openInNewGraph, setSublogOpen }) {
679
+ const engine = useWorkflowEngine();
680
+ const matrixGroups = useMemo(() => engine.matrixGroups(jobs), [engine, jobs]);
681
+ const { displayJobs } = useMemo(() => computeGraphLayout(engine, jobs, matrixGroups), [
682
+ engine,
683
+ jobs,
684
+ matrixGroups
685
+ ]);
686
+ const isStepsOpen = (j) => stepsOpen.has(pathPrefix + j);
687
+ const onJobClick = (j) => {
688
+ const status = jobs[j]?.status ?? displayJobs[j]?.status;
689
+ if (!isStepsOpen(j) && (status === "skipped" || status === "skipped-failed")) return;
690
+ toggleSteps(pathPrefix + j);
691
+ };
692
+ const expandInline = (j, s) => toggleSubworkflow(`${pathPrefix}${j}:${s}`);
693
+ const openNew = (j, s) => openInNewGraph(pathPrefix, j, s);
694
+ const onStepClick = (j, s) => {
695
+ const stepStatus = jobs[j]?.steps?.[+s]?.status;
696
+ if (stepStatus === "skipped" || stepStatus === "skipped-failed") return;
697
+ if (jobs[j]?.steps[+s]?.subworkflow) expandInline(j, +s);
698
+ else setSublogOpen(engine.stepLogPath(pathPrefix, j, s));
699
+ };
700
+ const onStepLogClick = (j, s) => setSublogOpen(engine.stepLogPath(pathPrefix, j, s));
701
+ const onSubworkflowIcon = (j, s) => openNew(j, s);
702
+ const isSubworkflowExpanded = (j, s) => expandedSubworkflows.has(`${pathPrefix}${j}:${s}`);
703
+ const renderSubworkflow = (j, s) => {
704
+ const subJobs = jobs[j]?.steps?.[s]?.subworkflow?.jobs;
705
+ if (!subJobs) return null;
706
+ return /* @__PURE__ */ jsx(SidebarWell, {
707
+ expanded: isSubworkflowExpanded(j, s),
708
+ children: /* @__PURE__ */ jsx("div", {
709
+ className: "ml-6 mt-0.5 border-l border-(--theme-border) pl-4",
710
+ children: /* @__PURE__ */ jsx(SidebarJobs, {
711
+ jobs: subJobs,
712
+ pathPrefix: `${pathPrefix}subworkflows/${j}/step_${s}/`,
713
+ preview,
714
+ stepsOpen,
715
+ toggleSteps,
716
+ expandedSubworkflows,
717
+ toggleSubworkflow,
718
+ matrixOpen,
719
+ toggleMatrix,
720
+ openInNewGraph,
721
+ setSublogOpen
722
+ })
723
+ })
724
+ });
725
+ };
726
+ const seenMatrixGroups = /* @__PURE__ */ new Set();
727
+ const orderedKeys = Object.keys(jobs).flatMap((jobName) => {
728
+ const originaljob = jobs[jobName]?._matrix?.originaljob;
729
+ if (!originaljob) return [jobName];
730
+ if (seenMatrixGroups.has(originaljob)) return [];
731
+ seenMatrixGroups.add(originaljob);
732
+ return [originaljob];
733
+ });
734
+ return /* @__PURE__ */ jsx(Fragment$1, { children: orderedKeys.map((jobName) => {
735
+ const mg = displayJobs[jobName]?._matrixGroup;
736
+ if (mg) return /* @__PURE__ */ jsx(MatrixGroupSummaryItem, {
737
+ matrixGroup: mg,
738
+ status: displayJobs[jobName]?.status ?? "",
739
+ isExpanded: matrixOpen.has(pathPrefix + mg.originaljob),
740
+ onToggle: () => toggleMatrix(pathPrefix + mg.originaljob),
741
+ jobs,
742
+ isStepsOpen,
743
+ onJobClick,
744
+ onStepClick,
745
+ onStepLogClick,
746
+ onSubworkflowIcon,
747
+ isSubworkflowExpanded,
748
+ renderSubworkflow,
749
+ preview
750
+ }, "matrix-" + pathPrefix + mg.originaljob);
751
+ return /* @__PURE__ */ jsx(Joblist, {
752
+ jobs,
753
+ jobNames: [jobName],
754
+ onJobClick,
755
+ isStepsOpen,
756
+ onStepClick,
757
+ onStepLogClick,
758
+ onSubworkflowIcon,
759
+ isSubworkflowExpanded,
760
+ renderSubworkflow,
761
+ preview
762
+ }, "job-" + pathPrefix + jobName);
763
+ }) });
764
+ }
765
+ var RUN_STATUSES = [
766
+ "started",
767
+ "running",
768
+ "completed",
769
+ "error",
770
+ "failed",
771
+ "canceled",
772
+ "skipped",
773
+ "faulted",
774
+ "canceling",
775
+ "skipped-failed",
776
+ ""
777
+ ];
778
+ function toStatus(value) {
779
+ return RUN_STATUSES.find((status) => status === value) ?? "";
780
+ }
781
+ function walkJobPath(root, path) {
782
+ let cursor = root;
783
+ for (const key of path) {
784
+ if (typeof cursor !== "object" || cursor === null) return {};
785
+ cursor = Object.getOwnPropertyDescriptor(cursor, String(key))?.value;
786
+ }
787
+ return isJobRecord(cursor) ? cursor : {};
788
+ }
789
+ function DependencyGraph({ run, preview = false, initialScale = 1, removeBorder, viewMode = "dag", setViewMode }) {
790
+ const engine = useWorkflowEngine();
791
+ const animT = .125;
792
+ const minScale = .2;
793
+ const maxScale = .6;
794
+ const rootJobs = isJobRecord(run.executedJobs) ? run.executedJobs : {};
795
+ const runNumber = run.number ?? 0;
796
+ const runSlug = run.slug ?? "";
797
+ const workflowName = run.workflowName ?? "";
798
+ const runStatus = toStatus(run.status);
799
+ const [sublogOpen, setSublogOpen] = useState("");
800
+ const [stepsOpen, setStepsOpen] = useState(/* @__PURE__ */ new Set());
801
+ const [expandedSubworkflows, setExpandedSubworkflows] = useState(/* @__PURE__ */ new Set());
802
+ const containerRef = useRef(null);
803
+ const graphContentRef = useRef(null);
804
+ const transformRef = useRef(null);
805
+ const [, forceRerender] = useState(0);
806
+ const invalidateConnectors = useCallback(() => forceRerender((c) => c + 1), []);
807
+ const [windowSize, setWindowSize] = useState(() => ({
808
+ w: typeof window !== "undefined" ? window.innerWidth : 800,
809
+ h: typeof window !== "undefined" ? window.innerHeight : 600
810
+ }));
811
+ useEffect(() => {
812
+ const onResize = () => setWindowSize({
813
+ w: window.innerWidth,
814
+ h: window.innerHeight
815
+ });
816
+ window.addEventListener("resize", onResize);
817
+ return () => window.removeEventListener("resize", onResize);
818
+ }, []);
819
+ const [topOffset, setTopOffset] = useState(null);
820
+ const topObserverRef = useRef(null);
821
+ const setContainerElement = useCallback((el) => {
822
+ containerRef.current = el;
823
+ topObserverRef.current?.disconnect();
824
+ topObserverRef.current = null;
825
+ if (!el || preview) return;
826
+ const measure = () => {
827
+ const cur = containerRef.current;
828
+ if (!cur) return;
829
+ setTopOffset((prev) => {
830
+ const next = cur.getBoundingClientRect().top;
831
+ return prev === next ? prev : next;
832
+ });
833
+ };
834
+ requestAnimationFrame(measure);
835
+ const parent = el.parentElement;
836
+ if (parent) {
837
+ const ro = new ResizeObserver(measure);
838
+ ro.observe(parent);
839
+ topObserverRef.current = ro;
840
+ }
841
+ }, [preview]);
842
+ const containerHeight = topOffset !== null ? Math.max(windowSize.h - topOffset - 16, 160) : null;
843
+ const [previewHeight, setPreviewHeight] = useState(null);
844
+ const previewViewRef = useRef(null);
845
+ useEffect(() => {
846
+ const container = containerRef.current;
847
+ if (!preview || !container) return;
848
+ let raf = 0;
849
+ let attempts = 0;
850
+ const measure = () => {
851
+ cancelAnimationFrame(raf);
852
+ raf = requestAnimationFrame(() => {
853
+ const next = measurePreviewHeight(graphContentRef.current, previewViewRef);
854
+ if (next === null) {
855
+ if (attempts++ < 120) measure();
856
+ return;
857
+ }
858
+ attempts = 0;
859
+ setPreviewHeight((prev) => prev === next ? prev : next);
860
+ });
861
+ };
862
+ measure();
863
+ const observer = new MutationObserver(measure);
864
+ observer.observe(container, {
865
+ childList: true,
866
+ subtree: true
867
+ });
868
+ return () => {
869
+ observer.disconnect();
870
+ cancelAnimationFrame(raf);
871
+ };
872
+ }, [preview]);
873
+ const { dag: dagStrings } = useStrings();
874
+ const slots = useSlots();
875
+ const [executedJobsPath, setExecutedJobsPath] = useState([[]]);
876
+ const [executedJobsPathIdx, setExecutedJobsPathIdx] = useState(0);
877
+ const [expandedJobs, setExpandedJobs] = useState(/* @__PURE__ */ new Set());
878
+ const [matrixOpen, setMatrixOpen] = useState(/* @__PURE__ */ new Set());
879
+ const isRunFinished = !!run.completedAt || run.status === "completed" || run.status === "error" || run.status === "canceled";
880
+ const [now, setNow] = useState(() => DateTime.now());
881
+ useEffect(() => {
882
+ if (isRunFinished) return;
883
+ const timer = setInterval(() => setNow(DateTime.now()), 1e3);
884
+ return () => clearInterval(timer);
885
+ }, [isRunFinished]);
886
+ const jobs = walkJobPath(rootJobs, executedJobsPath[executedJobsPathIdx] ?? []);
887
+ const resetGraph = useCallback(() => {
888
+ setSublogOpen("");
889
+ setStepsOpen(/* @__PURE__ */ new Set());
890
+ setExpandedSubworkflows(/* @__PURE__ */ new Set());
891
+ setMatrixOpen(/* @__PURE__ */ new Set());
892
+ }, []);
893
+ const openInNewGraph = useCallback((prefix, job, step) => {
894
+ resetGraph();
895
+ setExecutedJobsPath((prev) => [[
896
+ ...parsePrefixToPath(prefix),
897
+ job,
898
+ "steps",
899
+ step,
900
+ "subworkflow",
901
+ "jobs"
902
+ ], ...prev.slice(executedJobsPathIdx, 10)]);
903
+ setExecutedJobsPathIdx(0);
904
+ }, [executedJobsPathIdx, resetGraph]);
905
+ const toggleSubworkflow = useCallback((key) => {
906
+ setExpandedSubworkflows((prev) => {
907
+ const next = new Set(prev);
908
+ if (next.has(key)) next.delete(key);
909
+ else next.add(key);
910
+ return next;
911
+ });
912
+ }, []);
913
+ const toggleSteps = useCallback((key) => {
914
+ setStepsOpen((prev) => {
915
+ const next = new Set(prev);
916
+ if (next.has(key)) next.delete(key);
917
+ else next.add(key);
918
+ return next;
919
+ });
920
+ requestAnimationFrame(invalidateConnectors);
921
+ }, [invalidateConnectors]);
922
+ const toggleMatrix = useCallback((key) => {
923
+ setMatrixOpen((prev) => {
924
+ const next = new Set(prev);
925
+ if (next.has(key)) next.delete(key);
926
+ else next.add(key);
927
+ return next;
928
+ });
929
+ requestAnimationFrame(invalidateConnectors);
930
+ }, [invalidateConnectors]);
931
+ const { data: sublogs, isLoading: sublogsLoading, error: sublogsError } = useRunFile(runSlug, sublogOpen || null, preview ? void 0 : { refreshInterval: 2e3 });
932
+ useEffect(() => {
933
+ if (sublogOpen.endsWith("/script-unstable-redacted.sh") && !sublogsLoading && (sublogsError || !sublogs)) setSublogOpen(sublogOpen.replace(/script-unstable-redacted\.sh$/, "script-unstable.sh"));
934
+ }, [
935
+ sublogOpen,
936
+ sublogs,
937
+ sublogsError,
938
+ sublogsLoading
939
+ ]);
940
+ const jobAndStep = sublogOpen.split("/").slice(-3, -1);
941
+ if (jobAndStep.length > 1) jobAndStep[1] = jobAndStep[1].split("_")[1] ?? "";
942
+ const sublogIsStep = jobAndStep.length > 1;
943
+ const logName = jobAndStep.length > 1 ? "Job: " + (jobAndStep[0][0].toUpperCase() + jobAndStep[0].slice(1)).replace(/_/g, " ") + " \nStep: " + (jobs[jobAndStep[0]]?.steps[Number(jobAndStep[1])]?.name || "Step " + jobAndStep[1]) : "";
944
+ const sublogIsSubworkflow = jobAndStep.length > 1 && jobs[jobAndStep[0]]?.steps[Number(jobAndStep[1])]?.subworkflow;
945
+ const matrixGroups = useMemo(() => engine.matrixGroups(jobs), [engine, jobs]);
946
+ const { dependencyCols } = useMemo(() => computeGraphLayout(engine, jobs, matrixGroups), [
947
+ engine,
948
+ jobs,
949
+ matrixGroups
950
+ ]);
951
+ const fitGraph = useCallback((animationMs = 0) => {
952
+ const container = containerRef.current;
953
+ const content = graphContentRef.current;
954
+ const transform = transformRef.current;
955
+ if (!container || !content || !transform) return;
956
+ const topNodes = content.querySelectorAll(":scope > div > div > div > div[id^=\"node_\"]");
957
+ const nodes = topNodes.length > 0 ? topNodes : content.querySelectorAll("[id^=\"node_\"]");
958
+ if (nodes.length === 0) return;
959
+ let minX = Number.POSITIVE_INFINITY;
960
+ let minY = Number.POSITIVE_INFINITY;
961
+ let maxX = 0;
962
+ let maxY = 0;
963
+ for (const node of nodes) {
964
+ const el = node;
965
+ const pos = offsetWithin(el, content);
966
+ minX = Math.min(minX, pos.x);
967
+ minY = Math.min(minY, pos.y);
968
+ maxX = Math.max(maxX, pos.x + el.offsetWidth);
969
+ maxY = Math.max(maxY, pos.y + el.offsetHeight);
970
+ }
971
+ const graphW = maxX - minX;
972
+ const graphH = maxY - minY;
973
+ const containerW = container.clientWidth;
974
+ const containerH = container.clientHeight;
975
+ if (graphW === 0 || graphH === 0) return;
976
+ const margin = Math.min(containerW, containerH) * .05;
977
+ const scaleW = (containerW - margin * 2) / graphW;
978
+ const scaleH = (containerH - margin * 2) / graphH;
979
+ const scale = Math.min(Math.max(Math.min(scaleW, scaleH), minScale), maxScale);
980
+ const offsetX = margin - minX * scale;
981
+ const offsetY = margin - minY * scale;
982
+ if (animationMs > 0) {
983
+ transform.setTransform(offsetX, offsetY, scale, animationMs, "easeOut");
984
+ return;
985
+ }
986
+ const wrapperEl = container.querySelector(".react-transform-component");
987
+ if (wrapperEl) wrapperEl.style.transform = `translate(${offsetX}px, ${offsetY}px) scale(${scale})`;
988
+ requestAnimationFrame(() => {
989
+ transform.setTransform(offsetX, offsetY, scale, 0);
990
+ invalidateConnectors();
991
+ });
992
+ }, [invalidateConnectors]);
993
+ const animatedFit = useCallback(() => fitGraph(FIT_ANIMATION_MS), [fitGraph]);
994
+ useEffect(() => {
995
+ if (preview) return;
996
+ const raf = requestAnimationFrame(() => fitGraph());
997
+ return () => cancelAnimationFrame(raf);
998
+ }, [
999
+ preview,
1000
+ fitGraph,
1001
+ windowSize,
1002
+ executedJobsPathIdx
1003
+ ]);
1004
+ const currentPath = executedJobsPath[executedJobsPathIdx] ?? [];
1005
+ const nestingDepth = currentPath.length / 5;
1006
+ let pathAppend = "";
1007
+ for (let i = 0; i < nestingDepth; i++) pathAppend += `subworkflows/${currentPath[5 * i]}/step_${currentPath[5 * i + 2]?.toString()}/`;
1008
+ let generalLogPath = "logs.out";
1009
+ if (nestingDepth > 0) {
1010
+ let parentPrefix = "";
1011
+ for (let i = 0; i < nestingDepth - 1; i++) parentPrefix += `subworkflows/${currentPath[5 * i]}/step_${currentPath[5 * i + 2]?.toString()}/`;
1012
+ const lastJob = String(currentPath[5 * (nestingDepth - 1)] ?? "");
1013
+ const lastStep = currentPath[5 * (nestingDepth - 1) + 2] ?? "";
1014
+ generalLogPath = engine.stepLogPath(parentPrefix, lastJob, lastStep);
1015
+ }
1016
+ const missingLogText = sublogIsSubworkflow ? "Subworkflows generally have no logs/scripts at this level.\nClick on the \"Open in new graph\" icon on the subworkflow step to open the subworkflow graph." : sublogsError ? dagStrings.logLoadFailed : (sublogOpen === generalLogPath ? dagStrings.statusReason(run.statusReason) : void 0) ?? dagStrings.noLogFound;
1017
+ const currentSublog = sublogOpen === "" ? "" : preview ? engine.toYaml(jobs[jobAndStep[0]]?.steps[Number(jobAndStep[1])]) : sublogsError || !sublogs ? missingLogText : sublogs;
1018
+ const expansionKeys = useMemo(() => engine.expansionKeys(jobs, pathAppend), [
1019
+ engine,
1020
+ jobs,
1021
+ pathAppend
1022
+ ]);
1023
+ const hasExpandable = expansionKeys.subworkflowKeys.length > 0 || expansionKeys.matrixKeys.length > 0;
1024
+ const allExpanded = hasExpandable && expansionKeys.subworkflowKeys.every((k) => expandedSubworkflows.has(k)) && expansionKeys.matrixKeys.every((k) => matrixOpen.has(k));
1025
+ const toggleExpandAll = () => {
1026
+ if (allExpanded) {
1027
+ setExpandedSubworkflows(/* @__PURE__ */ new Set());
1028
+ setStepsOpen(/* @__PURE__ */ new Set());
1029
+ setMatrixOpen(/* @__PURE__ */ new Set());
1030
+ } else {
1031
+ setExpandedSubworkflows(new Set(expansionKeys.subworkflowKeys));
1032
+ setStepsOpen(new Set(expansionKeys.jobKeys));
1033
+ setMatrixOpen(new Set(expansionKeys.matrixKeys));
1034
+ }
1035
+ requestAnimationFrame(() => {
1036
+ invalidateConnectors();
1037
+ fitGraph();
1038
+ });
1039
+ };
1040
+ const nextStep = useCallback((backward) => {
1041
+ const sublogSplit = sublogOpen.split("/");
1042
+ let job = sublogSplit[sublogSplit.length - 3] ?? "";
1043
+ let step = parseInt(sublogSplit[sublogSplit.length - 2].slice(5), 10);
1044
+ const jobArr = Object.keys(jobs);
1045
+ let idx = jobArr.indexOf(job);
1046
+ if (backward) {
1047
+ if (0 < step) step -= 1;
1048
+ else {
1049
+ if (idx === 0) idx = jobArr.length;
1050
+ job = jobArr[idx - 1] ?? "";
1051
+ step = (jobs[job]?.steps.length ?? 0) - 1;
1052
+ }
1053
+ } else if ((jobs[job]?.steps.length ?? 0) - 1 > step) step += 1;
1054
+ else {
1055
+ if (idx === jobArr.length - 1) idx = -1;
1056
+ job = jobArr[idx + 1] ?? "";
1057
+ step = 0;
1058
+ }
1059
+ setSublogOpen(sublogSplit.slice(0, -3).join("/") + `/${job}/step_${step}/` + sublogSplit.slice(-1)[0]);
1060
+ }, [jobs, sublogOpen]);
1061
+ const handleKeyDown = useCallback((e) => {
1062
+ if (e.key === "ArrowRight") nextStep(false);
1063
+ else if (e.key === "ArrowLeft") nextStep(true);
1064
+ }, [nextStep]);
1065
+ useEffect(() => {
1066
+ if (!sublogIsStep) return;
1067
+ window.addEventListener("keydown", handleKeyDown);
1068
+ return () => window.removeEventListener("keydown", handleKeyDown);
1069
+ }, [handleKeyDown, sublogIsStep]);
1070
+ const summarySidebar = useMemo(() => /* @__PURE__ */ jsx(SidebarJobs, {
1071
+ jobs,
1072
+ pathPrefix: pathAppend,
1073
+ preview,
1074
+ stepsOpen,
1075
+ toggleSteps,
1076
+ expandedSubworkflows,
1077
+ toggleSubworkflow,
1078
+ matrixOpen,
1079
+ toggleMatrix,
1080
+ openInNewGraph,
1081
+ setSublogOpen
1082
+ }), [
1083
+ jobs,
1084
+ pathAppend,
1085
+ preview,
1086
+ stepsOpen,
1087
+ toggleSteps,
1088
+ expandedSubworkflows,
1089
+ toggleSubworkflow,
1090
+ matrixOpen,
1091
+ toggleMatrix,
1092
+ openInNewGraph
1093
+ ]);
1094
+ if (!Array.isArray(dependencyCols) || dependencyCols.length === 0 || !Array.isArray(dependencyCols[0]) || dependencyCols[0].length === 0 || !Array.isArray(dependencyCols[0][0]) || dependencyCols[0][0].length === 0) return;
1095
+ const startDt = run.createdAt ? DateTime.fromISO(run.createdAt) : now;
1096
+ const endDt = run.completedAt ? DateTime.fromISO(run.completedAt) : now;
1097
+ const runtime = toAbsHumanDuration(startDt, endDt);
1098
+ const additionalBottomLeftBarComponents = sublogIsStep ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
1099
+ /* @__PURE__ */ jsxs("button", {
1100
+ type: "button",
1101
+ className: "btn btn-neutral",
1102
+ onClick: () => nextStep(true),
1103
+ children: [/* @__PURE__ */ jsx(ArrowLeftIcon, {}), /* @__PURE__ */ jsx("span", { children: "Prev" })]
1104
+ }),
1105
+ /* @__PURE__ */ jsxs("button", {
1106
+ type: "button",
1107
+ className: "btn btn-neutral",
1108
+ onClick: () => nextStep(false),
1109
+ children: [/* @__PURE__ */ jsx(ArrowRightIcon, {}), /* @__PURE__ */ jsx("span", { children: "Next" })]
1110
+ }),
1111
+ /* @__PURE__ */ jsx("span", {
1112
+ className: "pt-0.5 whitespace-pre-wrap text-xs",
1113
+ children: logName
1114
+ })
1115
+ ] }) : void 0;
1116
+ const additionalBottomRightBarComponents = (!preview || sublogIsSubworkflow) && sublogOpen && sublogIsStep ? /* @__PURE__ */ jsx("button", {
1117
+ type: "button",
1118
+ className: "btn btn-neutral",
1119
+ onClick: () => {
1120
+ if (sublogIsSubworkflow) {
1121
+ openInNewGraph(pathAppend, jobAndStep[0], parseInt(jobAndStep[1], 10));
1122
+ setSublogOpen("");
1123
+ } else setSublogOpen(sublogOpen.split("/").slice(-1)[0] === "logs.out" ? [...sublogOpen.split("/").slice(0, -1), "script-unstable-redacted.sh"].join("/") : [...sublogOpen.split("/").slice(0, -1), "logs.out"].join("/"));
1124
+ },
1125
+ children: /* @__PURE__ */ jsx("span", { children: sublogIsSubworkflow ? "Open Subworkflow" : sublogOpen.split("/").slice(-1)[0] === "logs.out" ? "Show Script" : "Show Logs" })
1126
+ }) : void 0;
1127
+ return /* @__PURE__ */ jsxs("div", {
1128
+ className: "flex flex-col lg:flex-row w-full",
1129
+ children: [
1130
+ !preview && /* @__PURE__ */ jsxs("div", {
1131
+ className: "hidden lg:block lg:w-[280px] flex-shrink-0 lg:mr-1",
1132
+ children: [
1133
+ slots.runSessions && /* @__PURE__ */ jsx("div", {
1134
+ className: "mb-4 overflow-y-auto",
1135
+ children: slots.runSessions({
1136
+ runId: run.id ?? "",
1137
+ runNumber,
1138
+ runActive: engine.isRunActive(run.status),
1139
+ links: run.links
1140
+ })
1141
+ }),
1142
+ /* @__PURE__ */ jsxs("div", {
1143
+ className: "font-bold flex items-center gap-x-2",
1144
+ children: ["Summary", /* @__PURE__ */ jsx("button", {
1145
+ type: "button",
1146
+ "aria-label": "General log",
1147
+ onClick: () => setSublogOpen(generalLogPath),
1148
+ className: "link",
1149
+ children: /* @__PURE__ */ jsx(DocumentIcon, { className: "w-4 h-4" })
1150
+ })]
1151
+ }),
1152
+ /* @__PURE__ */ jsx("div", {
1153
+ className: "mx-4 mb-4 overflow-y-auto",
1154
+ children: summarySidebar
1155
+ })
1156
+ ]
1157
+ }),
1158
+ /* @__PURE__ */ jsxs("div", {
1159
+ className: cx(preview ? "w-full" : "flex-1 min-w-0 mt-2", "h-full flex flex-col gap-y-2"),
1160
+ children: [
1161
+ !preview && /* @__PURE__ */ jsxs("div", {
1162
+ className: "panel w-full border rounded-lg px-4 py-2 flex flex-wrap items-center gap-x-16 gap-y-2",
1163
+ children: [
1164
+ /* @__PURE__ */ jsxs("div", {
1165
+ className: "flex flex-col",
1166
+ children: [/* @__PURE__ */ jsx("div", {
1167
+ className: "text-xs",
1168
+ children: dagStrings.workflow
1169
+ }), /* @__PURE__ */ jsx("div", {
1170
+ className: "text-sm font-semibold flex items-center gap-x-1",
1171
+ children: workflowName
1172
+ })]
1173
+ }),
1174
+ /* @__PURE__ */ jsxs("div", {
1175
+ className: "flex flex-col",
1176
+ children: [/* @__PURE__ */ jsx("div", {
1177
+ className: "text-xs",
1178
+ children: dagStrings.run
1179
+ }), /* @__PURE__ */ jsx("div", {
1180
+ className: "text-sm font-semibold flex items-center gap-x-1",
1181
+ children: runNumber
1182
+ })]
1183
+ }),
1184
+ /* @__PURE__ */ jsxs("div", {
1185
+ className: "flex flex-col",
1186
+ children: [/* @__PURE__ */ jsx("div", {
1187
+ className: "text-xs",
1188
+ children: dagStrings.status
1189
+ }), /* @__PURE__ */ jsx("div", {
1190
+ className: "text-sm font-semibold flex items-center gap-x-1",
1191
+ children: /* @__PURE__ */ jsxs(TooltipInfo, {
1192
+ className: "flex items-center gap-x-1",
1193
+ text: dagStrings.statusReason(run.statusReason) ?? "",
1194
+ children: [/* @__PURE__ */ jsx(Indicator, { status: runStatus }), dagStrings.statusLabel(runStatus)]
1195
+ })
1196
+ })]
1197
+ }),
1198
+ /* @__PURE__ */ jsxs("div", {
1199
+ className: "flex flex-col",
1200
+ children: [/* @__PURE__ */ jsx("div", {
1201
+ className: "text-xs",
1202
+ children: dagStrings.runtime
1203
+ }), /* @__PURE__ */ jsx("div", {
1204
+ className: "text-sm font-semibold flex items-center gap-x-1",
1205
+ children: runtime
1206
+ })]
1207
+ }),
1208
+ /* @__PURE__ */ jsxs("div", {
1209
+ className: "flex flex-col",
1210
+ children: [/* @__PURE__ */ jsx("div", {
1211
+ className: "text-xs",
1212
+ children: dagStrings.submitted
1213
+ }), /* @__PURE__ */ jsx("div", {
1214
+ className: "text-sm font-semibold flex items-center gap-x-1",
1215
+ children: startDt ? startDt.toLocaleString(DateTime.DATETIME_SHORT) : ""
1216
+ })]
1217
+ }),
1218
+ /* @__PURE__ */ jsx("div", { className: "flex-1" }),
1219
+ setViewMode && /* @__PURE__ */ jsx(ViewToggle, {
1220
+ viewMode,
1221
+ setViewMode: (mode) => {
1222
+ if (mode === "dag") {
1223
+ setStepsOpen(/* @__PURE__ */ new Set());
1224
+ requestAnimationFrame(fitGraph);
1225
+ }
1226
+ setViewMode?.(mode);
1227
+ }
1228
+ })
1229
+ ]
1230
+ }),
1231
+ !preview && /* @__PURE__ */ jsx(AnnotationBanner, { executedJobs: jobs }),
1232
+ viewMode === "tree" && !preview ? /* @__PURE__ */ jsx("div", {
1233
+ className: "panel w-full border",
1234
+ children: /* @__PURE__ */ jsx(TreeView, {
1235
+ jobs,
1236
+ workflowName,
1237
+ runNumber,
1238
+ slug: runSlug,
1239
+ expandedJobs,
1240
+ setExpandedJobs
1241
+ })
1242
+ }) : /* @__PURE__ */ jsx("div", {
1243
+ ref: setContainerElement,
1244
+ className: "panel w-full " + (removeBorder ? "border-none" : "border") + " overflow-hidden relative",
1245
+ style: {
1246
+ height: containerHeight !== null ? `${containerHeight}px` : previewHeight !== null ? `${Math.min(previewHeight, windowSize.h * .62)}px` : "62vh",
1247
+ minHeight: previewHeight !== null ? 0 : 160
1248
+ },
1249
+ children: /* @__PURE__ */ jsx(TransformWrapper, {
1250
+ ref: transformRef,
1251
+ minScale,
1252
+ maxScale,
1253
+ initialScale: maxScale,
1254
+ doubleClick: { disabled: true },
1255
+ limitToBounds: false,
1256
+ onInit: () => {
1257
+ setTimeout(invalidateConnectors, 0);
1258
+ },
1259
+ onPanning: invalidateConnectors,
1260
+ onPinch: invalidateConnectors,
1261
+ onTransform: invalidateConnectors,
1262
+ onWheel: invalidateConnectors,
1263
+ onZoom: invalidateConnectors,
1264
+ children: () => /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
1265
+ className: "absolute z-10 top-2 right-2 flex flex-col items-end gap-y-1",
1266
+ children: [
1267
+ hasExpandable && /* @__PURE__ */ jsx("button", {
1268
+ type: "button",
1269
+ className: "btn btn-info cursor-pointer h-6",
1270
+ onClick: toggleExpandAll,
1271
+ children: allExpanded ? dagStrings.collapseAll : dagStrings.expandAll
1272
+ }),
1273
+ /* @__PURE__ */ jsx("button", {
1274
+ type: "button",
1275
+ className: "btn btn-info w-21 cursor-pointer h-6",
1276
+ "aria-label": "Reset view",
1277
+ onClick: () => {
1278
+ if ((executedJobsPath[executedJobsPathIdx]?.length ?? 0) > 0) {
1279
+ resetGraph();
1280
+ setExecutedJobsPath([[], ...executedJobsPath.slice(executedJobsPathIdx, 10)]);
1281
+ setExecutedJobsPathIdx(0);
1282
+ } else fitGraph(FIT_ANIMATION_MS);
1283
+ },
1284
+ children: "Reset View"
1285
+ }),
1286
+ executedJobsPath.length > 1 && /* @__PURE__ */ jsxs("div", {
1287
+ className: "flex",
1288
+ children: [/* @__PURE__ */ jsx("button", {
1289
+ type: "button",
1290
+ onClick: () => {
1291
+ if (executedJobsPath.length > executedJobsPathIdx + 1) {
1292
+ resetGraph();
1293
+ setExecutedJobsPathIdx(executedJobsPathIdx + 1);
1294
+ }
1295
+ },
1296
+ className: "btn btn-info mr-1 cursor-pointer w-10 h-6",
1297
+ children: "<"
1298
+ }), /* @__PURE__ */ jsx("button", {
1299
+ type: "button",
1300
+ onClick: () => {
1301
+ if (executedJobsPathIdx - 1 >= 0) {
1302
+ resetGraph();
1303
+ setExecutedJobsPathIdx(executedJobsPathIdx - 1);
1304
+ }
1305
+ },
1306
+ className: "btn btn-info cursor-pointer w-10 h-6",
1307
+ children: ">"
1308
+ })]
1309
+ })
1310
+ ]
1311
+ }), /* @__PURE__ */ jsx(TransformComponent, { children: /* @__PURE__ */ jsx("div", {
1312
+ ref: graphContentRef,
1313
+ className: "flex relative mr-[8000px]",
1314
+ style: { minHeight: 4e3 },
1315
+ children: /* @__PURE__ */ jsx(Subgraph, {
1316
+ jobs,
1317
+ pathPrefix: pathAppend,
1318
+ depth: 0,
1319
+ preview,
1320
+ animT,
1321
+ expandedSubworkflows,
1322
+ toggleSubworkflow,
1323
+ openInNewGraph,
1324
+ setSublogOpen,
1325
+ invalidateConnectors,
1326
+ requestFit: animatedFit,
1327
+ stepsOpen,
1328
+ toggleSteps,
1329
+ matrixOpen,
1330
+ toggleMatrix,
1331
+ onReady: fitGraph
1332
+ }, `sg-${executedJobsPathIdx}-${pathAppend}`)
1333
+ }) })] })
1334
+ })
1335
+ })
1336
+ ]
1337
+ }),
1338
+ /* @__PURE__ */ jsx(BareModal, {
1339
+ open: sublogOpen !== "",
1340
+ onClose: () => setSublogOpen(""),
1341
+ align: "center",
1342
+ className: cx(modalPanelClasses, "max-h-full overflow-y-auto p-4 sm:p-6"),
1343
+ children: /* @__PURE__ */ jsx("div", {
1344
+ className: "flex w-full",
1345
+ children: sublogsLoading && (!sublogs || !runNumber) ? /* @__PURE__ */ jsx(Loader, {
1346
+ text: dagStrings.loadingLogs,
1347
+ size: 70,
1348
+ textSize: 18
1349
+ }) : preview ? /* @__PURE__ */ jsxs("div", { children: [slots.yamlViewer?.({
1350
+ value: currentSublog,
1351
+ height: window.innerHeight / 1.4,
1352
+ width: window.innerWidth / 1.4,
1353
+ contentKey: "editor_" + sublogOpen
1354
+ }) ?? /* @__PURE__ */ jsx("pre", {
1355
+ className: "h-full overflow-auto text-xs p-4",
1356
+ style: {
1357
+ height: window.innerHeight / 1.4,
1358
+ width: window.innerWidth / 1.4
1359
+ },
1360
+ children: currentSublog
1361
+ }), /* @__PURE__ */ jsxs("div", {
1362
+ className: "flex theme-panel select-none items-center gap-x-2 p-2",
1363
+ children: [/* @__PURE__ */ jsx("div", {
1364
+ className: "w-full flex flex-row gap-x-2 items-center",
1365
+ children: /* @__PURE__ */ jsx("div", {
1366
+ className: "flex gap-x-2 w-full",
1367
+ children: additionalBottomLeftBarComponents
1368
+ })
1369
+ }), additionalBottomRightBarComponents]
1370
+ })] }) : /* @__PURE__ */ jsx(LogViewer, {
1371
+ log: currentSublog?.length > 1e3 ? currentSublog.slice(-1e5) : currentSublog,
1372
+ height: windowSize.h / 1.2,
1373
+ width: windowSize.w / 1.2,
1374
+ hideExpand: true,
1375
+ additionalBottomLeftBarComponents,
1376
+ additionalBottomRightBarComponents,
1377
+ enableWorkflowCommands: true
1378
+ })
1379
+ })
1380
+ })
1381
+ ]
1382
+ });
1383
+ }
1384
+ function DependencyGraphPreview(inputs) {
1385
+ const ymlJobs = inputs.yml?.["jobs"];
1386
+ return /* @__PURE__ */ jsx(DependencyGraph, {
1387
+ run: {
1388
+ executedJobs: addCleanupSteps({ jobs: isJobRecord(ymlJobs) ? ymlJobs : {} }),
1389
+ number: 0,
1390
+ workflowName: ""
1391
+ },
1392
+ preview: true,
1393
+ initialScale: inputs.initialScale || 1,
1394
+ removeBorder: inputs.removeBorder
1395
+ });
1396
+ }
1397
+ //#endregion
1398
+ export { DependencyGraphPreview, DependencyGraph as default };