@parallelworks/ui 0.18.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 (349) hide show
  1. package/README.md +86 -6
  2. package/dist/ai/adapter/memoryStore.d.ts +8 -0
  3. package/dist/ai/adapter/memoryStore.js +152 -0
  4. package/dist/ai/adapter/mock/index.d.ts +2 -0
  5. package/dist/ai/adapter/mock/index.js +3 -0
  6. package/dist/ai/adapter/mock/mockAdapter.d.ts +4 -0
  7. package/dist/ai/adapter/mock/mockAdapter.js +81 -0
  8. package/dist/ai/adapter/openai/createOpenAIStream.d.ts +9 -0
  9. package/dist/ai/adapter/openai/createOpenAIStream.js +33 -0
  10. package/dist/ai/adapter/openai/errors.d.ts +3 -0
  11. package/dist/ai/adapter/openai/errors.js +37 -0
  12. package/dist/ai/adapter/openai/index.d.ts +6 -0
  13. package/dist/ai/adapter/openai/index.js +6 -0
  14. package/dist/ai/adapter/openai/openaiAdapter.d.ts +9 -0
  15. package/dist/ai/adapter/openai/openaiAdapter.js +54 -0
  16. package/dist/ai/adapter/openai/responses.d.ts +4 -0
  17. package/dist/ai/adapter/openai/responses.js +108 -0
  18. package/dist/ai/adapter/openai/sse.d.ts +24 -0
  19. package/dist/ai/adapter/openai/sse.js +112 -0
  20. package/dist/ai/adapter/openai/wire.d.ts +40 -0
  21. package/dist/ai/adapter/types.d.ts +131 -0
  22. package/dist/ai/adapter/types.js +11 -0
  23. package/dist/ai/agent/classifier.d.ts +2 -0
  24. package/dist/ai/agent/classifier.js +12 -0
  25. package/dist/ai/agent/diff.d.ts +35 -0
  26. package/dist/ai/agent/diff.js +223 -0
  27. package/dist/ai/agent/diffHighlight.d.ts +6 -0
  28. package/dist/ai/agent/diffHighlight.js +101 -0
  29. package/dist/ai/agent/index.d.ts +6 -0
  30. package/dist/ai/agent/permissions.d.ts +11 -0
  31. package/dist/ai/agent/permissions.js +29 -0
  32. package/dist/ai/agent/toolfmt.d.ts +18 -0
  33. package/dist/ai/agent/toolfmt.js +383 -0
  34. package/dist/ai/agent/transcript.d.ts +38 -0
  35. package/dist/ai/agent/transcript.js +127 -0
  36. package/dist/ai/ai.css +325 -0
  37. package/dist/ai/components/AllocationSelector.d.ts +4 -0
  38. package/dist/ai/components/AllocationSelector.js +56 -0
  39. package/dist/ai/components/AttachmentManager.d.ts +1 -0
  40. package/dist/ai/components/AttachmentManager.js +144 -0
  41. package/dist/ai/components/AttachmentUpload.d.ts +24 -0
  42. package/dist/ai/components/AttachmentUpload.js +179 -0
  43. package/dist/ai/components/BlockedGroupBanner.d.ts +1 -0
  44. package/dist/ai/components/BlockedGroupBanner.js +29 -0
  45. package/dist/ai/components/BranchNavigator.d.ts +8 -0
  46. package/dist/ai/components/BranchNavigator.js +57 -0
  47. package/dist/ai/components/ChatEmptyState.d.ts +3 -0
  48. package/dist/ai/components/ChatEmptyState.js +171 -0
  49. package/dist/ai/components/ChatInput.d.ts +45 -0
  50. package/dist/ai/components/ChatInput.js +265 -0
  51. package/dist/ai/components/ChatLayout.d.ts +5 -0
  52. package/dist/ai/components/ChatLayout.js +15 -0
  53. package/dist/ai/components/ChatMessage.d.ts +24 -0
  54. package/dist/ai/components/ChatMessage.js +338 -0
  55. package/dist/ai/components/ChatMessageList.d.ts +35 -0
  56. package/dist/ai/components/ChatMessageList.js +301 -0
  57. package/dist/ai/components/ChatSidebar.d.ts +5 -0
  58. package/dist/ai/components/ChatSidebar.js +345 -0
  59. package/dist/ai/components/ChatThread.d.ts +3 -0
  60. package/dist/ai/components/ChatThread.js +198 -0
  61. package/dist/ai/components/ComposerChrome.d.ts +18 -0
  62. package/dist/ai/components/ComposerChrome.js +50 -0
  63. package/dist/ai/components/ComposerFrame.d.ts +16 -0
  64. package/dist/ai/components/ComposerFrame.js +33 -0
  65. package/dist/ai/components/DragOverlay.d.ts +5 -0
  66. package/dist/ai/components/DragOverlay.js +20 -0
  67. package/dist/ai/components/ModelSelector.d.ts +6 -0
  68. package/dist/ai/components/ModelSelector.js +343 -0
  69. package/dist/ai/components/ShareDialog.d.ts +11 -0
  70. package/dist/ai/components/ShareDialog.js +260 -0
  71. package/dist/ai/components/SlashMenu.d.ts +28 -0
  72. package/dist/ai/components/SlashMenu.js +91 -0
  73. package/dist/ai/components/agent/ActivityLine.d.ts +21 -0
  74. package/dist/ai/components/agent/ActivityLine.js +151 -0
  75. package/dist/ai/components/agent/AgentMessageParts.d.ts +54 -0
  76. package/dist/ai/components/agent/AgentMessageParts.js +721 -0
  77. package/dist/ai/components/agent/PermissionPicker.d.ts +11 -0
  78. package/dist/ai/components/agent/PermissionPicker.js +38 -0
  79. package/dist/ai/components/attachmentKeys.d.ts +4 -0
  80. package/dist/ai/components/attachmentKeys.js +10 -0
  81. package/dist/ai/core/ChatProvider.d.ts +66 -0
  82. package/dist/ai/core/ChatProvider.js +848 -0
  83. package/dist/ai/core/KeyboardShortcutsProvider.d.ts +5 -0
  84. package/dist/ai/core/KeyboardShortcutsProvider.js +9 -0
  85. package/dist/ai/core/chatReducer.d.ts +114 -0
  86. package/dist/ai/core/chatReducer.js +223 -0
  87. package/dist/ai/core/config.d.ts +47 -0
  88. package/dist/ai/core/config.js +45 -0
  89. package/dist/ai/core/events.d.ts +1 -0
  90. package/dist/ai/core/events.js +4 -0
  91. package/dist/ai/core/exportConversation.d.ts +4 -0
  92. package/dist/ai/core/exportConversation.js +42 -0
  93. package/dist/ai/core/greeting.d.ts +3 -0
  94. package/dist/ai/core/greeting.js +38 -0
  95. package/dist/ai/core/parts.d.ts +11 -0
  96. package/dist/ai/core/parts.js +84 -0
  97. package/dist/ai/core/useDragDrop.d.ts +14 -0
  98. package/dist/ai/core/useDragDrop.js +36 -0
  99. package/dist/ai/core/useKeyboardShortcuts.d.ts +12 -0
  100. package/dist/ai/core/useKeyboardShortcuts.js +77 -0
  101. package/dist/ai/core/useSSEStream.d.ts +34 -0
  102. package/dist/ai/core/useSSEStream.js +98 -0
  103. package/dist/ai/index.d.ts +40 -0
  104. package/dist/ai/index.js +44 -0
  105. package/dist/ai/strings.d.ts +167 -0
  106. package/dist/ai/strings.js +175 -0
  107. package/dist/ai/types.d.ts +236 -0
  108. package/dist/ai/ui/Avatar.d.ts +8 -0
  109. package/dist/ai/ui/Avatar.js +35 -0
  110. package/dist/ai/ui/Dropdown.d.ts +31 -0
  111. package/dist/ai/ui/Dropdown.js +110 -0
  112. package/dist/ai/ui/Markdown.d.ts +8 -0
  113. package/dist/ai/ui/Markdown.js +79 -0
  114. package/dist/ai/ui/Notice.d.ts +21 -0
  115. package/dist/ai/ui/Notice.js +77 -0
  116. package/dist/ai/ui/buttons.d.ts +1 -0
  117. package/dist/ai/ui/codeBreaks.d.ts +16 -0
  118. package/dist/ai/ui/codeBreaks.js +36 -0
  119. package/dist/ai/ui/prose.d.ts +1 -0
  120. package/dist/ai/ui/prose.js +13 -0
  121. package/dist/ai/ui/sidebar.d.ts +81 -0
  122. package/dist/ai/ui/sidebar.js +148 -0
  123. package/dist/ai/utils.d.ts +4 -0
  124. package/dist/ai/utils.js +23 -0
  125. package/dist/assets/{editor.worker-CuBRIwi-.js → editor.worker-BmHUIVG4.js} +194 -163
  126. package/dist/assets/{json.worker-8ytS02vz.js → json.worker-D8bJ92nw.js} +384 -338
  127. package/dist/assets/{yaml.worker-DJC-VUcv.js → yaml.worker-C4gyGAko.js} +15657 -13823
  128. package/dist/components/AddMenu.js +46 -0
  129. package/dist/components/Avatar.d.ts +1 -1
  130. package/dist/components/Avatar.js +46 -0
  131. package/dist/{chunks/BareModal-BtRiQezg.js → components/BareModal.js} +1 -1
  132. package/dist/components/Breadcrumbs.d.ts +1 -1
  133. package/dist/components/Breadcrumbs.js +83 -0
  134. package/dist/components/Button.js +83 -0
  135. package/dist/components/Callout.js +23 -0
  136. package/dist/components/Card.js +31 -0
  137. package/dist/components/CollapsiblePanel.js +77 -0
  138. package/dist/components/ConfirmModal.js +74 -0
  139. package/dist/components/CopyToClipboard.js +81 -0
  140. package/dist/{chunks/CreateModal-BY6DLmur.js → components/CreateModal.js} +10 -189
  141. package/dist/components/DescriptionList.js +42 -0
  142. package/dist/components/Dropdown.d.ts +1 -1
  143. package/dist/components/Dropdown.js +349 -0
  144. package/dist/components/EmptyState.d.ts +1 -1
  145. package/dist/components/EmptyState.js +27 -0
  146. package/dist/components/Filter.js +32 -0
  147. package/dist/components/FilterPill.js +13 -0
  148. package/dist/components/IconButton.js +34 -0
  149. package/dist/components/Indicator.d.ts +2 -2
  150. package/dist/{chunks/Indicator-hBSIxOPK.js → components/Indicator.js} +1 -1
  151. package/dist/components/Input.d.ts +1 -1
  152. package/dist/components/Input.js +88 -0
  153. package/dist/components/LetterBadge.d.ts +1 -1
  154. package/dist/components/LetterBadge.js +13 -0
  155. package/dist/components/Listbox.js +123 -0
  156. package/dist/{chunks/Loader-BXoXTZGc.js → components/Loader.js} +1 -1
  157. package/dist/components/Markdown.js +15 -0
  158. package/dist/components/MarkdownImpl.d.ts +1 -1
  159. package/dist/components/MultiSelect.js +171 -0
  160. package/dist/components/Overview.js +39 -0
  161. package/dist/components/PageHeader.js +194 -0
  162. package/dist/components/Pagination.js +35 -0
  163. package/dist/components/Provider.d.ts +16 -65
  164. package/dist/{chunks/Provider-Ghua9PkN.js → components/Provider.js} +54 -20
  165. package/dist/{chunks/RequiredMark-DfLdYbYZ.js → components/RequiredMark.js} +1 -1
  166. package/dist/components/SectionHeader.js +11 -0
  167. package/dist/components/SettingModeOption.js +30 -0
  168. package/dist/components/SettingRow.d.ts +1 -1
  169. package/dist/components/SettingRow.js +35 -0
  170. package/dist/components/SettingsCard.js +31 -0
  171. package/dist/components/SettingsGroup.d.ts +1 -1
  172. package/dist/components/SettingsGroup.js +17 -0
  173. package/dist/components/Sort.js +42 -0
  174. package/dist/components/StatusBadge.js +63 -0
  175. package/dist/components/SwitchToggle.js +59 -0
  176. package/dist/{chunks/SwitchToggleSmall-FcsaYSTs.js → components/SwitchToggleSmall.js} +1 -1
  177. package/dist/{chunks/Table-4OHjdKdO.js → components/Table.js} +7 -5
  178. package/dist/components/Toggle.js +20 -0
  179. package/dist/{chunks/Tooltip-Crxo_eU_.js → components/Tooltip.js} +6 -4
  180. package/dist/{chunks/StatusBadge-C1Blx_cW.js → components/dropdownUtils.js} +2 -63
  181. package/dist/components/focus.d.ts +2 -0
  182. package/dist/components/focus.js +7 -0
  183. package/dist/components/ghostButton.js +7 -0
  184. package/dist/components/inputClasses.js +4 -0
  185. package/dist/components/keys.d.ts +12 -0
  186. package/dist/components/keys.js +27 -0
  187. package/dist/components/relativeTime.js +32 -0
  188. package/dist/{chunks/useCssIsDark-DHr05dG2.js → components/useCssIsDark.js} +1 -1
  189. package/dist/duration.d.ts +3 -0
  190. package/dist/duration.js +17 -0
  191. package/dist/editor/Editor.js +13 -0
  192. package/dist/editor/EditorField.js +53 -0
  193. package/dist/editor/FormCodePanel.js +173 -0
  194. package/dist/editor/Monaco.d.ts +1 -20
  195. package/dist/editor/Monaco.js +112 -0
  196. package/dist/editor/index.d.ts +2 -1
  197. package/dist/editor/index.js +7 -2
  198. package/dist/editor/themes.js +91 -0
  199. package/dist/editor/workers.js +29 -0
  200. package/dist/editor/yaml.d.ts +5 -0
  201. package/dist/editor/yaml.js +15 -0
  202. package/dist/engine.d.ts +98 -0
  203. package/dist/file-explorer/FileExplorer.d.ts +11 -2
  204. package/dist/file-explorer/FileExplorer.js +1402 -0
  205. package/dist/file-explorer/FileExplorerContext.d.ts +1 -1
  206. package/dist/file-explorer/FileExplorerContext.js +370 -0
  207. package/dist/file-explorer/FilePreview/CodePreviewEditor.d.ts +1 -1
  208. package/dist/{chunks/CodePreviewEditor-4lx3kE-w.js → file-explorer/FilePreview/CodePreviewEditor.js} +3 -2
  209. package/dist/file-explorer/FilePreview/Inline.js +45 -0
  210. package/dist/file-explorer/FilePreview/StreamedBadge.js +16 -0
  211. package/dist/file-explorer/FilePreview/Viewer.js +526 -0
  212. package/dist/file-explorer/FilePreview/index.js +266 -0
  213. package/dist/file-explorer/FilePreview/parse.js +63 -0
  214. package/dist/file-explorer/FilePreview/previewCache.js +98 -0
  215. package/dist/file-explorer/FilePreview/previewType.d.ts +1 -1
  216. package/dist/file-explorer/FilePreview/previewType.js +151 -0
  217. package/dist/file-explorer/FilePreview/readCappedBytes.js +196 -0
  218. package/dist/file-explorer/FilePreview/useObjectBlobUrl.js +95 -0
  219. package/dist/file-explorer/FilePreview/useObjectText.js +109 -0
  220. package/dist/file-explorer/FilePreview/usePresignedUrl.js +73 -0
  221. package/dist/file-explorer/FileRow.d.ts +1 -1
  222. package/dist/file-explorer/FileRow.js +165 -0
  223. package/dist/file-explorer/FileRowsWindow.js +92 -0
  224. package/dist/file-explorer/FileTable.js +172 -0
  225. package/dist/file-explorer/FileTableHeaders.js +49 -0
  226. package/dist/file-explorer/LoadMoreRow.js +46 -0
  227. package/dist/file-explorer/TreeView.js +376 -0
  228. package/dist/file-explorer/helpers.d.ts +4 -4
  229. package/dist/file-explorer/helpers.js +537 -0
  230. package/dist/file-explorer/index.d.ts +3 -3
  231. package/dist/file-explorer/index.js +7 -5705
  232. package/dist/file-explorer/lib/batch.js +39 -0
  233. package/dist/file-explorer/lib/errors.js +9 -0
  234. package/dist/file-explorer/lib/paging.js +124 -0
  235. package/dist/file-explorer/lib/types.d.ts +6 -1
  236. package/dist/file-explorer/lib/utils.d.ts +1 -3
  237. package/dist/file-explorer/lib/utils.js +322 -0
  238. package/dist/file-explorer/toasts.js +400 -0
  239. package/dist/file-explorer/treeRows.js +73 -0
  240. package/dist/file-explorer/useNearEnd.d.ts +1 -1
  241. package/dist/file-explorer/useNearEnd.js +27 -0
  242. package/dist/form/FieldWrapper.d.ts +2 -2
  243. package/dist/form/FieldWrapper.js +54 -0
  244. package/dist/form/Fields/BooleanField.js +51 -0
  245. package/dist/form/Fields/CheckboxGroupField.js +72 -0
  246. package/dist/form/Fields/ColorField.js +33 -0
  247. package/dist/form/Fields/DropdownField.js +49 -0
  248. package/dist/form/Fields/DurationField.js +130 -0
  249. package/dist/form/Fields/LabelField.js +19 -0
  250. package/dist/form/Fields/MultiDropdownField.js +40 -0
  251. package/dist/form/Fields/NumberField.js +51 -0
  252. package/dist/form/Fields/ObjectField.js +54 -0
  253. package/dist/form/Fields/RadioField.js +49 -0
  254. package/dist/form/Fields/RangeField.js +58 -0
  255. package/dist/form/Fields/ResourceOptionIcon.d.ts +1 -1
  256. package/dist/form/Fields/ResourceOptionIcon.js +21 -0
  257. package/dist/form/Fields/StepField.js +6 -0
  258. package/dist/form/Fields/StringField.js +37 -0
  259. package/dist/form/Fields/TextAreaField.js +31 -0
  260. package/dist/form/Fields/durationValidity.js +11 -0
  261. package/dist/form/Form.d.ts +4 -21
  262. package/dist/form/Form.js +643 -0
  263. package/dist/form/SpecialTypes/DropdownWrapper.js +30 -0
  264. package/dist/form/SpecialTypes/FormikCustomDropdown.js +122 -0
  265. package/dist/form/SpecialTypes/FormikCustomInput.js +111 -0
  266. package/dist/form/SpecialTypes/FormikMultiSelectDropdown.js +31 -0
  267. package/dist/form/SpecialTypes/FormikNumericInput.js +60 -0
  268. package/dist/form/Wizard/WizardContainer.js +138 -0
  269. package/dist/form/Wizard/WizardNavigation.js +92 -0
  270. package/dist/form/Wizard/WizardStepContent.js +35 -0
  271. package/dist/form/Wizard/WizardStepIndicator.js +108 -0
  272. package/dist/form/Wizard/useWizardState.js +118 -0
  273. package/dist/form/Wizard/utils.js +34 -0
  274. package/dist/form/fieldContext.js +26 -0
  275. package/dist/form/fieldRegistry.js +50 -0
  276. package/dist/form/index.d.ts +11 -12
  277. package/dist/form/index.js +18 -2445
  278. package/dist/form/lib.d.ts +6 -1
  279. package/dist/{chunks/FieldWrapper-Bef01EWO.js → form/lib.js} +5 -104
  280. package/dist/form/types/fieldComponentTypes.d.ts +3 -2
  281. package/dist/form/types/fieldTypes.d.ts +6 -20
  282. package/dist/form/useParsedOpts.d.ts +1 -1
  283. package/dist/form/useParsedOpts.js +62 -0
  284. package/dist/form/usePrevious.js +11 -0
  285. package/dist/form/utils/checkShowIfObject.js +14 -0
  286. package/dist/form/utils/{slurmDuration.d.ts → duration.d.ts} +2 -2
  287. package/dist/form/utils/duration.js +69 -0
  288. package/dist/form/utils/getParentValue.js +23 -0
  289. package/dist/form/utils/getPlaceholder.js +9 -0
  290. package/dist/form/utils/getValueUsingPath.js +23 -0
  291. package/dist/form/utils/secondaryField.js +22 -0
  292. package/dist/graph/AnnotationBanner.js +141 -0
  293. package/dist/graph/Collapse.js +41 -0
  294. package/dist/{workflow/index.js → graph/DependencyGraph.js} +76 -1163
  295. package/dist/graph/JobSummary.js +104 -0
  296. package/dist/{workflow → graph}/MatrixGroup.d.ts +2 -2
  297. package/dist/graph/MatrixGroup.js +107 -0
  298. package/dist/graph/Reveal.js +57 -0
  299. package/dist/graph/TreeView.js +711 -0
  300. package/dist/{workflow → graph}/index.d.ts +1 -1
  301. package/dist/graph/index.js +2 -0
  302. package/dist/{workflow → graph}/types.d.ts +3 -3
  303. package/dist/graph/types.js +6 -0
  304. package/dist/icons.d.ts +20 -21
  305. package/dist/index.d.ts +38 -40
  306. package/dist/index.js +47 -599
  307. package/dist/list/ActionToolbar.js +184 -0
  308. package/dist/list/HoverCard.js +73 -0
  309. package/dist/list/ListPage.js +145 -0
  310. package/dist/list/ListPager.js +59 -0
  311. package/dist/list/ListTable.d.ts +1 -1
  312. package/dist/list/ListTable.js +163 -0
  313. package/dist/list/ListViewControls.d.ts +2 -2
  314. package/dist/list/ListViewControls.js +372 -0
  315. package/dist/list/RowContextMenu.js +350 -0
  316. package/dist/list/UserHoverCard.js +117 -0
  317. package/dist/list/index.d.ts +9 -9
  318. package/dist/list/index.js +11 -2
  319. package/dist/list/listSearch.js +137 -0
  320. package/dist/list/listView.js +210 -0
  321. package/dist/list/textEntry.js +10 -0
  322. package/dist/logviewer/LogViewer.d.ts +1 -1
  323. package/dist/logviewer/LogViewer.js +750 -0
  324. package/dist/logviewer/index.d.ts +1 -0
  325. package/dist/logviewer/index.js +3 -742
  326. package/dist/logviewer/segments.d.ts +9 -0
  327. package/dist/logviewer/segments.js +80 -0
  328. package/dist/styles/fonts.css +4 -4
  329. package/dist/styles/theme.css +6 -4
  330. package/dist/styles.css +1 -1
  331. package/dist/theme/index.js +2 -2
  332. package/package.json +38 -14
  333. package/LICENSE +0 -202
  334. package/dist/chunks/Monaco-C1POOdVv.js +0 -1046
  335. package/dist/chunks/PageHeader-l49p1nM9.js +0 -690
  336. package/dist/chunks/SwitchToggle-CHPP8uZQ.js +0 -755
  337. package/dist/chunks/editor-9_uVtUyy.js +0 -347
  338. package/dist/chunks/list-qrojZZNv.js +0 -1411
  339. package/dist/editor/usesGitlab.d.ts +0 -97
  340. package/dist/form/Fields/BucketReadOnlyField.d.ts +0 -7
  341. package/dist/form/types/workflow-schema.d.ts +0 -1145
  342. package/dist/form/utils/pwResolver.d.ts +0 -18
  343. package/dist/{chunks/MarkdownImpl-Bq4QhrtA.js → components/MarkdownImpl.js} +1 -1
  344. /package/dist/{workflow → graph}/AnnotationBanner.d.ts +0 -0
  345. /package/dist/{workflow → graph}/Collapse.d.ts +0 -0
  346. /package/dist/{workflow → graph}/DependencyGraph.d.ts +0 -0
  347. /package/dist/{workflow → graph}/JobSummary.d.ts +0 -0
  348. /package/dist/{workflow → graph}/Reveal.d.ts +0 -0
  349. /package/dist/{workflow → graph}/TreeView.d.ts +0 -0
@@ -1,1136 +1,25 @@
1
- import { c as useRunFile, l as useSlots, r as useNavigation, u as useStrings } from "../chunks/Provider-Ghua9PkN.js";
2
- import { ArrowLeftIcon, ArrowRightIcon, ChevronDownIcon, ChevronRightIcon, DocumentIcon, ErrorIcon, InfoIcon, LoaderIcon, OpenInNewGraphIcon, WarningIcon } from "../icons.js";
3
- import { n as TOOLTIP_ID, r as TooltipInfo } from "../chunks/Tooltip-Crxo_eU_.js";
4
- import { t as Loader } from "../chunks/Loader-BXoXTZGc.js";
5
- import { n as modalPanelClasses, t as BareModal } from "../chunks/BareModal-BtRiQezg.js";
6
- import { t as Indicator } from "../chunks/Indicator-hBSIxOPK.js";
7
- import { LogViewer } from "../logviewer/index.js";
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";
8
16
  import cx from "classnames";
9
- import React, { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
17
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
10
18
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
11
19
  import { flushSync } from "react-dom";
12
20
  import { DateTime } from "luxon";
13
- import { buildSubworkflowLogPath, collapseMatrixGroupsInLayout, collectExpansionKeys, computeMatrixAggStatus, detectMatrixGroups, dumpYaml, getAllDeps, getStepLabel, isRunActive, toAbsHumanDuration } from "@parallelworks/workflow-parser";
14
21
  import { TransformComponent, TransformWrapper } from "react-zoom-pan-pinch";
15
- import { AnimatePresence, motion } from "framer-motion";
16
- //#region src/workflow/AnnotationBanner.tsx
17
- var typeOrder = {
18
- error: 0,
19
- warning: 1,
20
- notice: 2
21
- };
22
- var typeConfig = {
23
- error: {
24
- icon: ErrorIcon,
25
- label: "Error",
26
- accentClass: "text-red-500",
27
- bgClass: "bg-red-500/5 dark:bg-red-500/10",
28
- countBg: "bg-red-500/15 text-red-700 dark:text-red-400"
29
- },
30
- warning: {
31
- icon: WarningIcon,
32
- label: "Warning",
33
- accentClass: "text-yellow-500",
34
- bgClass: "bg-yellow-500/5 dark:bg-yellow-500/10",
35
- countBg: "bg-yellow-500/15 text-yellow-700 dark:text-yellow-400"
36
- },
37
- notice: {
38
- icon: InfoIcon,
39
- label: "Notice",
40
- accentClass: "text-blue-500",
41
- bgClass: "bg-blue-500/5 dark:bg-blue-500/10",
42
- countBg: "bg-blue-500/15 text-blue-700 dark:text-blue-400"
43
- }
44
- };
45
- function collectAnnotations(executedJobs, parentPath) {
46
- const annotations = [];
47
- for (const [jobName, job] of Object.entries(executedJobs)) {
48
- const fullJobName = parentPath ? `${parentPath} > ${jobName}` : jobName;
49
- const steps = job?.steps;
50
- if (!Array.isArray(steps)) continue;
51
- for (let stepIndex = 0; stepIndex < steps.length; stepIndex++) {
52
- const step = steps[stepIndex];
53
- const stepAnnotations = step?.annotations;
54
- if (Array.isArray(stepAnnotations)) {
55
- for (const annotation of stepAnnotations) if (annotation?.message) annotations.push({
56
- type: annotation.type || "notice",
57
- message: annotation.message,
58
- title: annotation.title,
59
- file: annotation.file,
60
- line: annotation.line,
61
- jobName: fullJobName,
62
- stepName: step?.name,
63
- stepIndex
64
- });
65
- }
66
- const subJobs = step?.subworkflow?.jobs;
67
- if (subJobs && typeof subJobs === "object") annotations.push(...collectAnnotations(subJobs, fullJobName));
68
- }
69
- }
70
- annotations.sort((a, b) => (typeOrder[a.type] ?? 2) - (typeOrder[b.type] ?? 2));
71
- return annotations;
72
- }
73
- function AnnotationBanner({ executedJobs }) {
74
- const [expanded, setExpanded] = useState(false);
75
- if (!executedJobs) return null;
76
- const annotations = collectAnnotations(executedJobs);
77
- if (annotations.length === 0) return null;
78
- const counts = {
79
- error: 0,
80
- warning: 0,
81
- notice: 0
82
- };
83
- for (const a of annotations) counts[a.type]++;
84
- const ChevronIcon = expanded ? ChevronDownIcon : ChevronRightIcon;
85
- return /* @__PURE__ */ jsxs("div", {
86
- className: cx("rounded-lg border border-[var(--theme-border)] overflow-hidden", "bg-[var(--theme-panel-bg)]"),
87
- children: [/* @__PURE__ */ jsxs("button", {
88
- type: "button",
89
- onClick: () => setExpanded((prev) => !prev),
90
- className: cx("w-full flex items-center gap-3 px-4 py-2.5 text-left", "hover:bg-[var(--theme-hover)] transition-colors", expanded && "border-b border-[var(--theme-border)]"),
91
- children: [
92
- /* @__PURE__ */ jsx(ChevronIcon, { className: "w-3.5 h-3.5 text-[var(--theme-muted-text-color)] shrink-0" }),
93
- /* @__PURE__ */ jsx("span", {
94
- className: "text-sm font-medium text-[var(--theme-panel-color)]",
95
- children: "Annotations"
96
- }),
97
- /* @__PURE__ */ jsx("div", {
98
- className: "flex items-center gap-1.5",
99
- children: [
100
- "error",
101
- "warning",
102
- "notice"
103
- ].map((type) => {
104
- if (!counts[type]) return null;
105
- const config = typeConfig[type];
106
- const Icon = config.icon;
107
- return /* @__PURE__ */ jsxs("span", {
108
- className: cx("inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium", config.countBg),
109
- children: [/* @__PURE__ */ jsx(Icon, { className: "w-3 h-3" }), counts[type]]
110
- }, type);
111
- })
112
- })
113
- ]
114
- }), expanded && /* @__PURE__ */ jsx("div", {
115
- className: "divide-y divide-[var(--theme-border)] max-h-[400px] overflow-y-auto",
116
- children: annotations.map((annotation, idx) => {
117
- const config = typeConfig[annotation.type] || typeConfig.notice;
118
- const Icon = config.icon;
119
- return /* @__PURE__ */ jsxs("div", {
120
- className: cx("flex items-start gap-2.5 px-4 py-2 text-sm", config.bgClass),
121
- children: [/* @__PURE__ */ jsx(Icon, { className: cx("w-4 h-4 mt-0.5 shrink-0", config.accentClass) }), /* @__PURE__ */ jsxs("div", {
122
- className: "flex-1 min-w-0",
123
- children: [
124
- /* @__PURE__ */ jsxs("span", {
125
- className: cx("font-medium", config.accentClass),
126
- children: [annotation.title || config.label, ":"]
127
- }),
128
- " ",
129
- /* @__PURE__ */ jsx("span", {
130
- className: "text-[var(--theme-panel-color)]",
131
- children: annotation.message
132
- }),
133
- /* @__PURE__ */ jsxs("span", {
134
- className: "text-[var(--theme-muted-text-color)] ml-2 text-xs",
135
- children: [
136
- "in ",
137
- annotation.jobName,
138
- " →",
139
- " ",
140
- annotation.stepName || `step ${annotation.stepIndex}`
141
- ]
142
- })
143
- ]
144
- })]
145
- }, idx);
146
- })
147
- })]
148
- });
149
- }
150
- //#endregion
151
- //#region src/workflow/Collapse.tsx
152
- function Collapse({ expanded, children, releaseOverflow = true }) {
153
- const [overflowVisible, setOverflowVisible] = useState(false);
154
- return /* @__PURE__ */ jsx(AnimatePresence, {
155
- initial: false,
156
- children: expanded && /* @__PURE__ */ jsx(motion.div, {
157
- initial: {
158
- height: 0,
159
- opacity: 0
160
- },
161
- animate: {
162
- height: "auto",
163
- opacity: 1
164
- },
165
- exit: {
166
- height: 0,
167
- opacity: 0
168
- },
169
- transition: {
170
- duration: .2,
171
- ease: [
172
- .25,
173
- .1,
174
- .25,
175
- 1
176
- ]
177
- },
178
- style: { overflow: overflowVisible ? "visible" : "hidden" },
179
- onAnimationStart: () => setOverflowVisible(false),
180
- onAnimationComplete: () => {
181
- if (expanded && releaseOverflow) setOverflowVisible(true);
182
- },
183
- children
184
- }, "collapse")
185
- });
186
- }
187
- //#endregion
188
- //#region src/workflow/Reveal.tsx
189
- var EASE = "cubic-bezier(0.25, 0.1, 0.25, 1)";
190
- var ROWS_TRANSITION = `grid-template-rows 200ms ${EASE}`;
191
- var FULL_TRANSITION = `${ROWS_TRANSITION}, width 200ms ${EASE}`;
192
- function Reveal({ open, children, growWidth = false, onSettled }) {
193
- const contentRef = useRef(null);
194
- const [width, setWidth] = useState(0);
195
- const [prevOpen, setPrevOpen] = useState(open);
196
- const [clipped, setClipped] = useState(!open);
197
- const [toggling, setToggling] = useState(false);
198
- const [renderChildren, setRenderChildren] = useState(open);
199
- if (open && !renderChildren) setRenderChildren(true);
200
- if (prevOpen !== open) {
201
- setPrevOpen(open);
202
- setToggling(true);
203
- setClipped(true);
204
- }
205
- useLayoutEffect(() => {
206
- if (!growWidth || !renderChildren) return;
207
- const el = contentRef.current;
208
- if (!el) return;
209
- const measure = () => setWidth(el.offsetWidth);
210
- measure();
211
- const ro = new ResizeObserver(measure);
212
- ro.observe(el);
213
- return () => ro.disconnect();
214
- }, [growWidth, renderChildren]);
215
- return /* @__PURE__ */ jsx("div", {
216
- className: "grid",
217
- style: {
218
- gridTemplateRows: open ? "1fr" : "0fr",
219
- overflow: clipped ? "hidden" : "visible",
220
- ...growWidth ? {
221
- width: open ? width : 0,
222
- transition: toggling ? FULL_TRANSITION : ROWS_TRANSITION
223
- } : { transition: ROWS_TRANSITION }
224
- },
225
- onTransitionEnd: (e) => {
226
- if (e.target === e.currentTarget && e.propertyName === "grid-template-rows") {
227
- setToggling(false);
228
- setClipped(!open);
229
- if (!open) setRenderChildren(false);
230
- onSettled?.();
231
- }
232
- },
233
- children: /* @__PURE__ */ jsx("div", {
234
- ref: contentRef,
235
- className: "min-h-0",
236
- ...growWidth ? { style: { width: "max-content" } } : {},
237
- children: renderChildren ? children : null
238
- })
239
- });
240
- }
241
- //#endregion
242
- //#region src/workflow/JobSummary.tsx
243
- function UsesIcon({ uses, subworkflow }) {
244
- const navigation = useNavigation();
245
- const slots = useSlots();
246
- const { jobActions } = useStrings();
247
- if (!uses || !slots.workflowIcon) return null;
248
- const open = () => {
249
- if (uses.startsWith("workflow/")) navigation.toWorkflow(uses.split("/")[1] ?? "");
250
- else if (subworkflow.sourceUrl) navigation.openExternal(subworkflow.sourceUrl);
251
- else if (uses.startsWith("github/")) {
252
- const [ownerRepo, ref] = uses.replace("github/", "").split("@");
253
- navigation.openExternal("https://github.com/" + ownerRepo + "/tree/" + ref);
254
- } else navigation.toMarketItem(uses.split("/")[1] ?? "");
255
- };
256
- return /* @__PURE__ */ jsx("button", {
257
- type: "button",
258
- "aria-label": jobActions.openOriginalWorkflow,
259
- "data-tooltip-id": TOOLTIP_ID,
260
- "data-tooltip-content": jobActions.openOriginalWorkflow,
261
- onClick: open,
262
- className: "cursor-pointer",
263
- children: slots.workflowIcon({
264
- image: subworkflow.image,
265
- type: subworkflow.type,
266
- className: "ml-1.5 h-[1.2em] w-[1.2em]"
267
- })
268
- });
269
- }
270
- function Joblist(inputs) {
271
- const { jobActions } = useStrings();
272
- return inputs.jobNames.map((jobName) => {
273
- if (inputs.jobs[jobName]?._matrixGroup) return null;
274
- const jobStatus = inputs.jobs[jobName]?.status;
275
- const matrixMeta = inputs.jobs[jobName]?._matrix;
276
- const jobLabel = matrixMeta?.originaljob ? `${matrixMeta.originaljob[0].toUpperCase() + matrixMeta.originaljob.slice(1)} (${(matrixMeta.index ?? 0) + 1}/${matrixMeta.totalingroup ?? "?"})` : (jobName[0].toUpperCase() + jobName.slice(1)).replace(/_/g, " ");
277
- return /* @__PURE__ */ jsxs("div", {
278
- className: "py-1 select-none",
279
- children: [/* @__PURE__ */ jsxs("button", {
280
- type: "button",
281
- "aria-expanded": inputs.isStepsOpen(jobName),
282
- onClick: () => inputs.onJobClick(jobName),
283
- className: cx("flex items-center gap-x-1.5 w-full text-left", jobStatus === "skipped" || jobStatus === "skipped-failed" ? "cursor-default" : "cursor-pointer"),
284
- children: [!inputs.preview && /* @__PURE__ */ jsx(Indicator, { status: inputs.jobs[jobName]?.status }), /* @__PURE__ */ jsx("div", {
285
- className: "font-medium",
286
- children: jobLabel
287
- })]
288
- }), /* @__PURE__ */ jsx(Reveal, {
289
- open: inputs.isStepsOpen(jobName),
290
- growWidth: inputs.growWidth ?? false,
291
- children: /* @__PURE__ */ jsx("div", {
292
- className: "flex flex-col gap-y-0.5 mt-0.5",
293
- children: (inputs.jobs[jobName]?.steps || []).map((step, i) => {
294
- const stepLabel = getStepLabel(step, i);
295
- const subJobs = step.subworkflow?.jobs;
296
- const canExpand = !!inputs.renderSubworkflow && !!subJobs && Object.keys(subJobs).length > 0;
297
- return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
298
- className: cx("flex items-center", step.status === "skipped" || step.status === "skipped-failed" ? "cursor-default" : "cursor-pointer"),
299
- children: [/* @__PURE__ */ jsxs("button", {
300
- type: "button",
301
- onClick: () => inputs.onStepClick(jobName, i),
302
- className: cx(inputs.preview ? "ml-1.5" : "ml-4", "gap-x-1.5 flex items-center text-left"),
303
- "data-testid": stepLabel,
304
- children: [inputs.preview ? "↳" : /* @__PURE__ */ jsx(Indicator, { status: step.status }), /* @__PURE__ */ jsx("div", { children: stepLabel })]
305
- }), step.subworkflow && /* @__PURE__ */ jsxs(Fragment$1, { children: [
306
- inputs.onStepLogClick && /* @__PURE__ */ jsx("button", {
307
- type: "button",
308
- "aria-label": jobActions.openSubworkflowLogs,
309
- "data-tooltip-id": "global-tooltip",
310
- "data-tooltip-content": jobActions.openSubworkflowLogs,
311
- onClick: (e) => {
312
- e.stopPropagation();
313
- inputs.onStepLogClick?.(jobName, i);
314
- },
315
- className: "link ml-1",
316
- children: /* @__PURE__ */ jsx(DocumentIcon, { className: "w-[1.2em] h-[1.2em]" })
317
- }),
318
- /* @__PURE__ */ jsx(UsesIcon, {
319
- uses: step.uses,
320
- subworkflow: step.subworkflow
321
- }),
322
- inputs.onSubworkflowIcon && /* @__PURE__ */ jsx("button", {
323
- type: "button",
324
- "aria-label": jobActions.openInNewGraph,
325
- "data-tooltip-id": "global-tooltip",
326
- "data-tooltip-content": jobActions.openInNewGraph,
327
- onClick: (e) => {
328
- e.stopPropagation();
329
- inputs.onSubworkflowIcon?.(jobName, i);
330
- },
331
- className: "link ml-1",
332
- children: /* @__PURE__ */ jsx(OpenInNewGraphIcon, { className: "w-[1.2em] h-[1.2em]" })
333
- })
334
- ] })]
335
- }), canExpand && inputs.renderSubworkflow?.(jobName, i)] }, "step-" + jobName + "-" + i);
336
- })
337
- })
338
- })]
339
- }, "job-" + jobName);
340
- });
341
- }
342
- //#endregion
343
- //#region src/workflow/TreeView.tsx
344
- function getStepDuration(step) {
345
- if (!step.startedAt) return "";
346
- return toAbsHumanDuration(DateTime.fromISO(step.startedAt), step.completedAt ? DateTime.fromISO(step.completedAt) : DateTime.now());
347
- }
348
- function getJobDuration(job) {
349
- if (!job.startedAt) return "";
350
- return toAbsHumanDuration(DateTime.fromISO(job.startedAt), job.completedAt ? DateTime.fromISO(job.completedAt) : DateTime.now());
351
- }
352
- function formatJobLabel(jobName, job) {
353
- const matrix = job?._matrix;
354
- if (matrix?.originaljob) return `${matrix.originaljob[0].toUpperCase() + matrix.originaljob.slice(1)} (${(matrix.index ?? 0) + 1}/${matrix.totalingroup ?? "?"})`;
355
- return (jobName[0].toUpperCase() + jobName.slice(1)).replace(/_/g, " ");
356
- }
357
- function InlineLogViewer({ slug, jobName, stepIndex }) {
358
- const { data: logs, isLoading, error } = useRunFile(slug, buildSubworkflowLogPath("", jobName, stepIndex), { refreshInterval: 2e3 });
359
- if (isLoading && !logs) return /* @__PURE__ */ jsxs("div", {
360
- className: "flex items-center gap-2 px-5 py-4 text-sm text-[var(--theme-muted-text-color)]",
361
- children: [/* @__PURE__ */ jsx(LoaderIcon, { className: "w-4 h-4" }), /* @__PURE__ */ jsx("span", { children: "Loading logs..." })]
362
- });
363
- if (error || !logs) return /* @__PURE__ */ jsx("div", {
364
- className: "px-5 py-4 text-sm text-[var(--theme-muted-text-color)] italic",
365
- children: "No logs available"
366
- });
367
- return /* @__PURE__ */ jsx("div", {
368
- className: "border-t border-[var(--theme-border)] ",
369
- children: /* @__PURE__ */ jsx(LogViewer, {
370
- log: logs,
371
- width: "100%",
372
- height: "unset",
373
- inline: true,
374
- contentClassName: "bg-[var(--theme-app-bg)]",
375
- enableWorkflowCommands: true
376
- })
377
- });
378
- }
379
- function JobRow({ jobName, job, isExpanded, onToggle, dependencies, workflowName, runNumber, slug, expandedJobs, onJobToggle, expandedSteps, onStepToggle }) {
380
- const hasSteps = job.steps && job.steps.length > 0;
381
- const duration = getJobDuration(job);
382
- const panelId = useId();
383
- return /* @__PURE__ */ jsxs("div", {
384
- className: "border-b border-[var(--theme-border)] last:border-b-0",
385
- children: [/* @__PURE__ */ jsxs("button", {
386
- type: "button",
387
- "aria-expanded": isExpanded,
388
- "aria-controls": hasSteps ? panelId : void 0,
389
- className: cx("flex items-center gap-3 px-5 py-3 transition-colors w-full text-left", job.status === "skipped" || job.status === "skipped-failed" ? "cursor-default" : "cursor-pointer", isExpanded && "bg-[var(--theme-panel-bg)]"),
390
- onClick: onToggle,
391
- children: [
392
- /* @__PURE__ */ jsx(motion.div, {
393
- animate: { rotate: isExpanded ? 90 : 0 },
394
- transition: { duration: .15 },
395
- className: cx("flex-shrink-0", hasSteps ? "text-[var(--theme-muted-text-color)]" : "invisible"),
396
- children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: cx("w-4 h-4", isExpanded && "text-[var(--theme-element)]") })
397
- }),
398
- /* @__PURE__ */ jsx(Indicator, { status: job.status }),
399
- /* @__PURE__ */ jsxs("div", {
400
- className: "flex flex-col gap-1 min-w-0 flex-1",
401
- children: [/* @__PURE__ */ jsx("span", {
402
- className: "font-mono text-sm text-[var(--theme-app)] truncate",
403
- children: formatJobLabel(jobName, job)
404
- }), dependencies && dependencies.length > 0 && /* @__PURE__ */ jsxs("div", {
405
- className: "flex items-center gap-1.5",
406
- children: [/* @__PURE__ */ jsx("span", {
407
- className: "text-[10px] text-[var(--theme-muted-text-color)]",
408
- children: "depends on:"
409
- }), dependencies.map((dep) => /* @__PURE__ */ jsx("span", {
410
- className: "text-[10px] px-1.5 py-0.5 rounded bg-[var(--theme-muted-panel-bg)] text-[var(--theme-muted-text-color)]",
411
- children: formatJobLabel(dep)
412
- }, dep))]
413
- })]
414
- }),
415
- /* @__PURE__ */ jsx("div", { className: "flex-1" }),
416
- duration && /* @__PURE__ */ jsx("span", {
417
- className: "font-mono text-xs text-[var(--theme-muted-text-color)] flex-shrink-0",
418
- children: duration
419
- })
420
- ]
421
- }), /* @__PURE__ */ jsx(AnimatePresence, {
422
- initial: false,
423
- children: isExpanded && hasSteps && /* @__PURE__ */ jsx(motion.div, {
424
- initial: {
425
- height: 0,
426
- opacity: 0
427
- },
428
- animate: {
429
- height: "auto",
430
- opacity: 1
431
- },
432
- exit: {
433
- height: 0,
434
- opacity: 0
435
- },
436
- transition: {
437
- duration: .2,
438
- ease: [
439
- .25,
440
- .1,
441
- .25,
442
- 1
443
- ]
444
- },
445
- className: "overflow-hidden",
446
- id: panelId,
447
- children: /* @__PURE__ */ jsx("div", {
448
- className: "ml-[38px] border-l-2 border-[var(--theme-border)]",
449
- children: job.steps.map((step, index) => {
450
- const stepKey = `${jobName}:${index}`;
451
- return /* @__PURE__ */ jsx(StepRow, {
452
- step,
453
- index,
454
- isExpanded: expandedSteps.has(stepKey),
455
- onToggle: () => onStepToggle(stepKey),
456
- workflowName,
457
- runNumber,
458
- slug,
459
- jobName,
460
- expandedJobs,
461
- onJobToggle,
462
- expandedSteps,
463
- onStepToggle
464
- }, index);
465
- })
466
- })
467
- })
468
- })]
469
- });
470
- }
471
- function StepRow({ step, index, isExpanded, onToggle, workflowName, runNumber, slug, jobName, expandedJobs, onJobToggle, expandedSteps, onStepToggle, depth = 0 }) {
472
- const slots = useSlots();
473
- const duration = getStepDuration(step);
474
- const label = getStepLabel(step, index);
475
- const isSubworkflow = !!step.subworkflow;
476
- const panelId = useId();
477
- const subworkflowJobs = step.subworkflow?.jobs ?? null;
478
- const subworkflowJobNames = subworkflowJobs ? Object.keys(subworkflowJobs).filter((name) => subworkflowJobs[name]?.if !== false) : [];
479
- const subworkflowDependenciesMap = useMemo(() => {
480
- if (!subworkflowJobs) return {};
481
- const map = {};
482
- for (const subJobName of subworkflowJobNames) {
483
- const job = subworkflowJobs[subJobName];
484
- if (job?.needs && Array.isArray(job.needs) && job.needs.length > 0) map[subJobName] = job.needs;
485
- }
486
- return map;
487
- }, [subworkflowJobs, subworkflowJobNames]);
488
- return /* @__PURE__ */ jsxs("div", { children: [
489
- /* @__PURE__ */ jsxs("button", {
490
- type: "button",
491
- "aria-expanded": isExpanded,
492
- "aria-controls": panelId,
493
- className: cx("flex items-center gap-3 px-5 py-2.5 cursor-pointer transition-colors w-full text-left", isExpanded ? "" : "hover:bg-[var(--theme-hover)]"),
494
- onClick: onToggle,
495
- children: [
496
- /* @__PURE__ */ jsx(motion.div, {
497
- animate: { rotate: isExpanded ? 90 : 0 },
498
- transition: { duration: .15 },
499
- className: "flex-shrink-0 text-[var(--theme-muted-text-color)]",
500
- children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: cx("w-4 h-4", isExpanded && "text-[var(--theme-element)]") })
501
- }),
502
- /* @__PURE__ */ jsx(Indicator, { status: step.status }),
503
- isSubworkflow && slots.workflowIcon?.({
504
- image: step.subworkflow?.image,
505
- type: step.subworkflow?.type,
506
- className: "h-4 w-4 flex-shrink-0"
507
- }),
508
- /* @__PURE__ */ jsxs("span", {
509
- className: "font-mono text-sm text-[var(--theme-app)] truncate flex-1",
510
- children: [label, isSubworkflow && /* @__PURE__ */ jsx("span", {
511
- className: "ml-2 text-xs text-[var(--theme-muted-text-color)]",
512
- children: "(subworkflow)"
513
- })]
514
- }),
515
- duration && /* @__PURE__ */ jsx("span", {
516
- className: "font-mono text-xs text-[var(--theme-muted-text-color)] flex-shrink-0",
517
- children: duration
518
- })
519
- ]
520
- }),
521
- /* @__PURE__ */ jsx(AnimatePresence, {
522
- initial: false,
523
- children: isExpanded && !isSubworkflow && /* @__PURE__ */ jsx(motion.div, {
524
- initial: {
525
- height: 0,
526
- opacity: 0
527
- },
528
- animate: {
529
- height: "auto",
530
- opacity: 1
531
- },
532
- exit: {
533
- height: 0,
534
- opacity: 0
535
- },
536
- transition: {
537
- duration: .2,
538
- ease: [
539
- .25,
540
- .1,
541
- .25,
542
- 1
543
- ]
544
- },
545
- className: "overflow-hidden",
546
- id: panelId,
547
- children: /* @__PURE__ */ jsx(InlineLogViewer, {
548
- slug,
549
- jobName,
550
- stepIndex: index
551
- })
552
- })
553
- }),
554
- /* @__PURE__ */ jsx(AnimatePresence, {
555
- initial: false,
556
- children: isExpanded && isSubworkflow && subworkflowJobNames.length > 0 && /* @__PURE__ */ jsx(motion.div, {
557
- initial: {
558
- height: 0,
559
- opacity: 0
560
- },
561
- animate: {
562
- height: "auto",
563
- opacity: 1
564
- },
565
- exit: {
566
- height: 0,
567
- opacity: 0
568
- },
569
- transition: {
570
- duration: .2,
571
- ease: [
572
- .25,
573
- .1,
574
- .25,
575
- 1
576
- ]
577
- },
578
- className: "overflow-hidden",
579
- id: panelId,
580
- children: /* @__PURE__ */ jsx("div", {
581
- className: "ml-[38px] border-l-2 border-[var(--theme-border)]",
582
- children: subworkflowJobNames.map((subJobName) => {
583
- const subJob = subworkflowJobs?.[subJobName];
584
- if (!subJob) return null;
585
- const subJobKey = `${jobName}:${index}:${subJobName}`;
586
- return /* @__PURE__ */ jsx(SubworkflowJobRow, {
587
- jobName: subJobName,
588
- job: subJob,
589
- isExpanded: expandedJobs.has(subJobKey),
590
- onToggle: () => onJobToggle(subJobKey),
591
- dependencies: subworkflowDependenciesMap[subJobName],
592
- slug,
593
- parentJobName: jobName,
594
- parentStepIndex: index,
595
- expandedJobs,
596
- onJobToggle,
597
- expandedSteps,
598
- onStepToggle,
599
- depth: depth + 1
600
- }, subJobName);
601
- })
602
- })
603
- })
604
- })
605
- ] });
606
- }
607
- function SubworkflowJobRow({ jobName, job, isExpanded, onToggle, dependencies, slug, parentJobName, parentStepIndex, subworkflowPrefix, expandedJobs, onJobToggle, expandedSteps, onStepToggle, depth }) {
608
- const hasSteps = job.steps && job.steps.length > 0;
609
- const duration = getJobDuration(job);
610
- const nestedSubworkflowPrefix = `${subworkflowPrefix || ""}subworkflows/${parentJobName}/step_${parentStepIndex}/`;
611
- const panelId = useId();
612
- return /* @__PURE__ */ jsxs("div", {
613
- className: "border-b border-[var(--theme-border)] last:border-b-0",
614
- children: [/* @__PURE__ */ jsxs("button", {
615
- type: "button",
616
- "aria-expanded": isExpanded,
617
- "aria-controls": hasSteps ? panelId : void 0,
618
- className: cx("flex items-center gap-3 px-5 py-3 transition-colors w-full text-left", job.status === "skipped" || job.status === "skipped-failed" ? "cursor-default" : "cursor-pointer", isExpanded && "bg-[var(--theme-panel-bg)]"),
619
- onClick: onToggle,
620
- children: [
621
- /* @__PURE__ */ jsx(motion.div, {
622
- animate: { rotate: isExpanded ? 90 : 0 },
623
- transition: { duration: .15 },
624
- className: cx("flex-shrink-0", hasSteps ? "text-[var(--theme-muted-text-color)]" : "invisible"),
625
- children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: cx("w-4 h-4", isExpanded && "text-[var(--theme-element)]") })
626
- }),
627
- /* @__PURE__ */ jsx(Indicator, { status: job.status }),
628
- /* @__PURE__ */ jsxs("div", {
629
- className: "flex flex-col gap-1 min-w-0 flex-1",
630
- children: [/* @__PURE__ */ jsx("span", {
631
- className: "font-mono text-sm text-[var(--theme-app)] truncate",
632
- children: formatJobLabel(jobName, job)
633
- }), dependencies && dependencies.length > 0 && /* @__PURE__ */ jsxs("div", {
634
- className: "flex items-center gap-1.5",
635
- children: [/* @__PURE__ */ jsx("span", {
636
- className: "text-[10px] text-[var(--theme-muted-text-color)]",
637
- children: "depends on:"
638
- }), dependencies.map((dep) => /* @__PURE__ */ jsx("span", {
639
- className: "text-[10px] px-1.5 py-0.5 rounded bg-[var(--theme-muted-panel-bg)] text-[var(--theme-muted-text-color)]",
640
- children: formatJobLabel(dep)
641
- }, dep))]
642
- })]
643
- }),
644
- /* @__PURE__ */ jsx("div", { className: "flex-1" }),
645
- duration && /* @__PURE__ */ jsx("span", {
646
- className: "font-mono text-xs text-[var(--theme-muted-text-color)] flex-shrink-0",
647
- children: duration
648
- })
649
- ]
650
- }), /* @__PURE__ */ jsx(AnimatePresence, {
651
- initial: false,
652
- children: isExpanded && hasSteps && /* @__PURE__ */ jsx(motion.div, {
653
- initial: {
654
- height: 0,
655
- opacity: 0
656
- },
657
- animate: {
658
- height: "auto",
659
- opacity: 1
660
- },
661
- exit: {
662
- height: 0,
663
- opacity: 0
664
- },
665
- transition: {
666
- duration: .2,
667
- ease: [
668
- .25,
669
- .1,
670
- .25,
671
- 1
672
- ]
673
- },
674
- className: "overflow-hidden",
675
- id: panelId,
676
- children: /* @__PURE__ */ jsx("div", {
677
- className: "ml-[38px] border-l-2 border-[var(--theme-border)]",
678
- children: job.steps.map((step, index) => {
679
- const stepKey = `${nestedSubworkflowPrefix}${jobName}:${index}`;
680
- return /* @__PURE__ */ jsx(SubworkflowStepRow, {
681
- step,
682
- index,
683
- isExpanded: expandedSteps.has(stepKey),
684
- onToggle: () => onStepToggle(stepKey),
685
- slug,
686
- subworkflowPrefix: nestedSubworkflowPrefix,
687
- jobName,
688
- expandedJobs,
689
- onJobToggle,
690
- expandedSteps,
691
- onStepToggle,
692
- depth
693
- }, index);
694
- })
695
- })
696
- })
697
- })]
698
- });
699
- }
700
- function SubworkflowStepRow({ step, index, isExpanded, onToggle, slug, subworkflowPrefix, jobName, expandedJobs, onJobToggle, expandedSteps, onStepToggle, depth }) {
701
- const slots = useSlots();
702
- const duration = getStepDuration(step);
703
- const label = getStepLabel(step, index);
704
- const isSubworkflow = !!step.subworkflow;
705
- const panelId = useId();
706
- const subworkflowJobs = step.subworkflow?.jobs ?? null;
707
- const subworkflowJobNames = subworkflowJobs ? Object.keys(subworkflowJobs).filter((name) => subworkflowJobs[name]?.if !== false) : [];
708
- const subworkflowDependenciesMap = useMemo(() => {
709
- if (!subworkflowJobs) return {};
710
- const map = {};
711
- for (const subJobName of subworkflowJobNames) {
712
- const job = subworkflowJobs[subJobName];
713
- if (job?.needs && Array.isArray(job.needs) && job.needs.length > 0) map[subJobName] = job.needs;
714
- }
715
- return map;
716
- }, [subworkflowJobs, subworkflowJobNames]);
717
- return /* @__PURE__ */ jsxs("div", { children: [
718
- /* @__PURE__ */ jsxs("button", {
719
- type: "button",
720
- "aria-expanded": isExpanded,
721
- "aria-controls": panelId,
722
- className: cx("flex items-center gap-3 px-5 py-2.5 cursor-pointer transition-colors w-full text-left", isExpanded ? "" : "hover:bg-[var(--theme-hover)]"),
723
- onClick: onToggle,
724
- children: [
725
- /* @__PURE__ */ jsx(motion.div, {
726
- animate: { rotate: isExpanded ? 90 : 0 },
727
- transition: { duration: .15 },
728
- className: "flex-shrink-0 text-[var(--theme-muted-text-color)]",
729
- children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: cx("w-4 h-4", isExpanded && "text-[var(--theme-element)]") })
730
- }),
731
- /* @__PURE__ */ jsx(Indicator, { status: step.status }),
732
- isSubworkflow && slots.workflowIcon?.({
733
- image: step.subworkflow?.image,
734
- type: step.subworkflow?.type,
735
- className: "h-4 w-4 flex-shrink-0"
736
- }),
737
- /* @__PURE__ */ jsxs("span", {
738
- className: "font-mono text-sm text-[var(--theme-app)] truncate flex-1",
739
- children: [label, isSubworkflow && /* @__PURE__ */ jsx("span", {
740
- className: "ml-2 text-xs text-[var(--theme-muted-text-color)]",
741
- children: "(subworkflow)"
742
- })]
743
- }),
744
- duration && /* @__PURE__ */ jsx("span", {
745
- className: "font-mono text-xs text-[var(--theme-muted-text-color)] flex-shrink-0",
746
- children: duration
747
- })
748
- ]
749
- }),
750
- /* @__PURE__ */ jsx(AnimatePresence, {
751
- initial: false,
752
- children: isExpanded && !isSubworkflow && /* @__PURE__ */ jsx(motion.div, {
753
- initial: {
754
- height: 0,
755
- opacity: 0
756
- },
757
- animate: {
758
- height: "auto",
759
- opacity: 1
760
- },
761
- exit: {
762
- height: 0,
763
- opacity: 0
764
- },
765
- transition: {
766
- duration: .2,
767
- ease: [
768
- .25,
769
- .1,
770
- .25,
771
- 1
772
- ]
773
- },
774
- className: "overflow-hidden",
775
- id: panelId,
776
- children: /* @__PURE__ */ jsx(SubworkflowInlineLogViewer, {
777
- slug,
778
- subworkflowPrefix,
779
- jobName,
780
- stepIndex: index
781
- })
782
- })
783
- }),
784
- /* @__PURE__ */ jsx(AnimatePresence, {
785
- initial: false,
786
- children: isExpanded && isSubworkflow && subworkflowJobNames.length > 0 && /* @__PURE__ */ jsx(motion.div, {
787
- initial: {
788
- height: 0,
789
- opacity: 0
790
- },
791
- animate: {
792
- height: "auto",
793
- opacity: 1
794
- },
795
- exit: {
796
- height: 0,
797
- opacity: 0
798
- },
799
- transition: {
800
- duration: .2,
801
- ease: [
802
- .25,
803
- .1,
804
- .25,
805
- 1
806
- ]
807
- },
808
- className: "overflow-hidden",
809
- id: panelId,
810
- children: /* @__PURE__ */ jsx("div", {
811
- className: "ml-[38px] border-l-2 border-[var(--theme-border)]",
812
- children: subworkflowJobNames.map((subJobName) => {
813
- const subJob = subworkflowJobs?.[subJobName];
814
- if (!subJob) return null;
815
- const subJobKey = `${subworkflowPrefix}${jobName}:${index}:${subJobName}`;
816
- return /* @__PURE__ */ jsx(SubworkflowJobRow, {
817
- jobName: subJobName,
818
- job: subJob,
819
- isExpanded: expandedJobs.has(subJobKey),
820
- onToggle: () => onJobToggle(subJobKey),
821
- dependencies: subworkflowDependenciesMap[subJobName],
822
- slug,
823
- parentJobName: jobName,
824
- parentStepIndex: index,
825
- subworkflowPrefix,
826
- expandedJobs,
827
- onJobToggle,
828
- expandedSteps,
829
- onStepToggle,
830
- depth: depth + 1
831
- }, subJobName);
832
- })
833
- })
834
- })
835
- })
836
- ] });
837
- }
838
- function SubworkflowInlineLogViewer({ slug, subworkflowPrefix, jobName, stepIndex }) {
839
- const { data: logs, isLoading, error } = useRunFile(slug, buildSubworkflowLogPath(subworkflowPrefix, jobName, stepIndex), { refreshInterval: 2e3 });
840
- if (isLoading && !logs) return /* @__PURE__ */ jsxs("div", {
841
- className: "flex items-center gap-2 px-5 py-4 text-sm text-[var(--theme-muted-text-color)]",
842
- children: [/* @__PURE__ */ jsx(LoaderIcon, { className: "w-4 h-4" }), /* @__PURE__ */ jsx("span", { children: "Loading logs..." })]
843
- });
844
- if (error || !logs) return /* @__PURE__ */ jsx("div", {
845
- className: "px-5 py-4 text-sm text-[var(--theme-muted-text-color)] italic",
846
- children: "No logs available"
847
- });
848
- return /* @__PURE__ */ jsx("div", {
849
- className: "border-t border-[var(--theme-border)]",
850
- children: /* @__PURE__ */ jsx(LogViewer, {
851
- log: logs,
852
- width: "100%",
853
- height: "unset",
854
- inline: true,
855
- contentClassName: "bg-[var(--theme-app-bg)]",
856
- enableWorkflowCommands: true
857
- })
858
- });
859
- }
860
- function TreeView({ jobs, workflowName, runNumber, slug, expandedJobs, setExpandedJobs }) {
861
- const visibleJobNames = useMemo(() => {
862
- return Object.keys(jobs).filter((jobName) => jobs[jobName]?.if !== false);
863
- }, [jobs]);
864
- const [expandedSteps, setExpandedSteps] = useState(/* @__PURE__ */ new Set());
865
- const [expandedSubworkflowJobs, setExpandedSubworkflowJobs] = useState(/* @__PURE__ */ new Set());
866
- const toggleJob = useCallback((jobName) => {
867
- const newExpanded = new Set(expandedJobs);
868
- if (newExpanded.has(jobName)) newExpanded.delete(jobName);
869
- else {
870
- const status = jobs[jobName]?.status;
871
- if (status === "skipped" || status === "skipped-failed") return;
872
- newExpanded.add(jobName);
873
- }
874
- setExpandedJobs(newExpanded);
875
- }, [
876
- expandedJobs,
877
- setExpandedJobs,
878
- jobs
879
- ]);
880
- const toggleSubworkflowJob = useCallback((jobKey) => {
881
- setExpandedSubworkflowJobs((prev) => {
882
- const newSet = new Set(prev);
883
- if (newSet.has(jobKey)) newSet.delete(jobKey);
884
- else newSet.add(jobKey);
885
- return newSet;
886
- });
887
- }, []);
888
- const toggleStep = useCallback((stepKey) => {
889
- setExpandedSteps((prev) => {
890
- const newSet = new Set(prev);
891
- if (newSet.has(stepKey)) newSet.delete(stepKey);
892
- else newSet.add(stepKey);
893
- return newSet;
894
- });
895
- }, []);
896
- const expandAll = useCallback(() => {
897
- setExpandedJobs(new Set(visibleJobNames));
898
- }, [visibleJobNames, setExpandedJobs]);
899
- const collapseAll = useCallback(() => {
900
- setExpandedJobs(/* @__PURE__ */ new Set());
901
- setExpandedSteps(/* @__PURE__ */ new Set());
902
- setExpandedSubworkflowJobs(/* @__PURE__ */ new Set());
903
- }, [setExpandedJobs]);
904
- const allExpanded = expandedJobs.size === visibleJobNames.length;
905
- const dependenciesMap = useMemo(() => {
906
- const map = {};
907
- for (const jobName of visibleJobNames) {
908
- const job = jobs[jobName];
909
- if (job?.needs && Array.isArray(job.needs) && job.needs.length > 0) map[jobName] = job.needs;
910
- }
911
- return map;
912
- }, [jobs, visibleJobNames]);
913
- return /* @__PURE__ */ jsxs("div", {
914
- className: "flex flex-col",
915
- children: [/* @__PURE__ */ jsx("div", {
916
- className: "flex justify-end px-4 py-2 border-b border-[var(--theme-border)]",
917
- children: /* @__PURE__ */ jsx("button", {
918
- type: "button",
919
- onClick: allExpanded ? collapseAll : expandAll,
920
- className: "text-xs font-mono text-[var(--theme-muted-text-color)] hover:text-[var(--theme-app)] transition-colors px-3 py-1.5 border border-[var(--theme-border)] rounded",
921
- children: allExpanded ? "Collapse all" : "Expand all"
922
- })
923
- }), /* @__PURE__ */ jsx("div", { children: (() => {
924
- const matrixGroups = {};
925
- for (const jobName of visibleJobNames) {
926
- const matrix = jobs[jobName]?._matrix;
927
- if (matrix?.originaljob) (matrixGroups[matrix.originaljob] ??= {
928
- originaljob: matrix.originaljob,
929
- members: []
930
- }).members.push(jobName);
931
- }
932
- for (const group of Object.values(matrixGroups)) group.members.sort((a, b) => (jobs[a]?._matrix?.index ?? 0) - (jobs[b]?._matrix?.index ?? 0));
933
- const rendered = [];
934
- const seenMatrixGroups = /* @__PURE__ */ new Set();
935
- for (const jobName of visibleJobNames) {
936
- const matrixOrigin = jobs[jobName]?._matrix?.originaljob;
937
- if (matrixOrigin) {
938
- if (seenMatrixGroups.has(matrixOrigin)) continue;
939
- seenMatrixGroups.add(matrixOrigin);
940
- const group = matrixGroups[matrixOrigin];
941
- if (!group) continue;
942
- const isGroupExpanded = expandedJobs.has(`matrix:${matrixOrigin}`);
943
- const { aggStatus } = computeMatrixAggStatus(group.members.map((m) => jobs[m]?.status));
944
- rendered.push(/* @__PURE__ */ jsxs("div", {
945
- className: "border-b border-[var(--theme-border)]",
946
- children: [/* @__PURE__ */ jsxs("button", {
947
- type: "button",
948
- "aria-expanded": isGroupExpanded,
949
- "aria-controls": `matrix-panel-${matrixOrigin}`,
950
- className: "flex items-center gap-3 px-5 py-3 cursor-pointer transition-colors w-full text-left",
951
- onClick: () => toggleJob(`matrix:${matrixOrigin}`),
952
- children: [
953
- /* @__PURE__ */ jsx(motion.div, {
954
- animate: { rotate: isGroupExpanded ? 90 : 0 },
955
- transition: { duration: .15 },
956
- className: "flex-shrink-0 text-[var(--theme-muted-text-color)]",
957
- children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: cx("w-4 h-4", isGroupExpanded && "text-[var(--theme-element)]") })
958
- }),
959
- /* @__PURE__ */ jsx(Indicator, { status: aggStatus }),
960
- /* @__PURE__ */ jsxs("span", {
961
- className: "font-mono text-sm text-[var(--theme-app)]",
962
- children: [
963
- "Matrix: ",
964
- matrixOrigin,
965
- " (",
966
- group.members.length,
967
- " jobs)"
968
- ]
969
- })
970
- ]
971
- }), /* @__PURE__ */ jsx(AnimatePresence, { children: isGroupExpanded && /* @__PURE__ */ jsx(motion.div, {
972
- initial: {
973
- height: 0,
974
- opacity: 0
975
- },
976
- animate: {
977
- height: "auto",
978
- opacity: 1
979
- },
980
- exit: {
981
- height: 0,
982
- opacity: 0
983
- },
984
- transition: { duration: .15 },
985
- className: "overflow-hidden pl-4",
986
- id: `matrix-panel-${matrixOrigin}`,
987
- children: group.members.map((memberName) => /* @__PURE__ */ jsx(JobRow, {
988
- jobName: memberName,
989
- job: jobs[memberName],
990
- isExpanded: expandedJobs.has(memberName),
991
- onToggle: () => toggleJob(memberName),
992
- dependencies: dependenciesMap[memberName],
993
- workflowName,
994
- runNumber,
995
- slug,
996
- expandedJobs: expandedSubworkflowJobs,
997
- onJobToggle: toggleSubworkflowJob,
998
- expandedSteps,
999
- onStepToggle: toggleStep
1000
- }, memberName))
1001
- }) })]
1002
- }, `matrix-${matrixOrigin}`));
1003
- } else rendered.push(/* @__PURE__ */ jsx(JobRow, {
1004
- jobName,
1005
- job: jobs[jobName],
1006
- isExpanded: expandedJobs.has(jobName),
1007
- onToggle: () => toggleJob(jobName),
1008
- dependencies: dependenciesMap[jobName],
1009
- workflowName,
1010
- runNumber,
1011
- slug,
1012
- expandedJobs: expandedSubworkflowJobs,
1013
- onJobToggle: toggleSubworkflowJob,
1014
- expandedSteps,
1015
- onStepToggle: toggleStep
1016
- }, jobName));
1017
- }
1018
- return rendered;
1019
- })() })]
1020
- });
1021
- }
1022
- //#endregion
1023
- //#region src/workflow/MatrixGroup.tsx
1024
- /** Matrix group item in the summary sidebar. */
1025
- function MatrixGroupSummaryItem({ matrixGroup, status, isExpanded, onToggle, jobs, isStepsOpen, onJobClick, onStepClick, onStepLogClick, onSubworkflowIcon, isSubworkflowExpanded, renderSubworkflow, preview }) {
1026
- return /* @__PURE__ */ jsxs("div", {
1027
- className: "py-1.5",
1028
- children: [/* @__PURE__ */ jsxs("button", {
1029
- type: "button",
1030
- "aria-expanded": isExpanded,
1031
- onClick: onToggle,
1032
- className: "cursor-pointer flex items-center gap-x-1.5 w-full text-left",
1033
- children: [/* @__PURE__ */ jsx(Indicator, { status }), /* @__PURE__ */ jsxs("div", {
1034
- className: "font-semibold",
1035
- children: [
1036
- "Matrix: ",
1037
- matrixGroup.originaljob,
1038
- /* @__PURE__ */ jsxs("span", {
1039
- className: "font-normal text-sm ml-1",
1040
- children: [
1041
- "(",
1042
- matrixGroup.members.length,
1043
- " jobs)"
1044
- ]
1045
- })
1046
- ]
1047
- })]
1048
- }), /* @__PURE__ */ jsx(Reveal, {
1049
- open: isExpanded,
1050
- children: /* @__PURE__ */ jsx("div", {
1051
- className: "ml-3 mt-0.5",
1052
- children: /* @__PURE__ */ jsx(Joblist, {
1053
- jobs,
1054
- jobNames: matrixGroup.members,
1055
- onJobClick,
1056
- isStepsOpen,
1057
- onStepClick,
1058
- onStepLogClick,
1059
- onSubworkflowIcon,
1060
- isSubworkflowExpanded,
1061
- renderSubworkflow,
1062
- preview
1063
- })
1064
- })
1065
- })]
1066
- }, "matrix-" + matrixGroup.originaljob);
1067
- }
1068
- /** Matrix group node in the DAG visualization. */
1069
- function MatrixGroupNode({ matrixGroup, jobNames, idPrefix, isExpanded, onToggle, jobs, activeDists, onMouseEnter, onMouseLeave, hoveredRelated, animT, zBase, isStepsOpen, onJobClick, onStepClick, onStepLogClick, onSubworkflowIcon, isSubworkflowExpanded, renderSubworkflow, preview }) {
1070
- const matrixName = matrixGroup.originaljob;
1071
- const matrixMembers = matrixGroup.members;
1072
- const { aggStatus, statusLabel } = computeMatrixAggStatus(matrixMembers.map((m) => jobs[m]?.status));
1073
- return /* @__PURE__ */ jsxs("div", {
1074
- id: "node_" + idPrefix + jobNames[0],
1075
- role: "none",
1076
- className: "relative m-24",
1077
- style: { zIndex: (activeDists?.has(jobNames[0]) ? 25 : 1) + zBase },
1078
- onMouseEnter,
1079
- onMouseLeave,
1080
- onFocus: onMouseEnter,
1081
- onBlur: onMouseLeave,
1082
- children: [/* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-2xl bg-(--theme-panel-bg)" }), /* @__PURE__ */ jsxs("div", {
1083
- className: "relative border-solid shadow py-4 px-8 rounded-xl border-4 whitespace-nowrap bg-(--theme-panel-bg) text-2xl",
1084
- style: {
1085
- opacity: hoveredRelated && !hoveredRelated.has(jobNames[0]) ? .5 : 1,
1086
- transition: `opacity ${animT}s`
1087
- },
1088
- children: [/* @__PURE__ */ jsxs("button", {
1089
- type: "button",
1090
- "aria-expanded": isExpanded,
1091
- className: "cursor-pointer w-full text-left",
1092
- onClick: onToggle,
1093
- children: [/* @__PURE__ */ jsxs("div", {
1094
- className: "font-semibold text-md p-0.5",
1095
- children: [
1096
- "Matrix:",
1097
- " ",
1098
- matrixName.length > 20 ? matrixName.slice(0, 20) + "..." : matrixName
1099
- ]
1100
- }), !isExpanded && /* @__PURE__ */ jsxs("div", {
1101
- className: "mt-1 border-t border-(--theme-border) pt-1 flex items-center gap-x-1",
1102
- children: [/* @__PURE__ */ jsx(Indicator, { status: aggStatus }), /* @__PURE__ */ jsx("span", { children: statusLabel })]
1103
- })]
1104
- }), /* @__PURE__ */ jsx(Reveal, {
1105
- open: isExpanded,
1106
- growWidth: true,
1107
- children: /* @__PURE__ */ jsx("div", {
1108
- className: "mt-1 border-t border-(--theme-border) pt-1",
1109
- children: /* @__PURE__ */ jsx(Joblist, {
1110
- jobs,
1111
- jobNames: matrixMembers,
1112
- onJobClick,
1113
- isStepsOpen,
1114
- onStepClick,
1115
- onStepLogClick,
1116
- onSubworkflowIcon,
1117
- isSubworkflowExpanded,
1118
- renderSubworkflow,
1119
- growWidth: true,
1120
- preview
1121
- })
1122
- })
1123
- })]
1124
- })]
1125
- }, "node_" + jobNames[0]);
1126
- }
1127
- //#endregion
1128
- //#region src/workflow/types.ts
1129
- function isJobRecord(value) {
1130
- return typeof value === "object" && value !== null;
1131
- }
1132
- //#endregion
1133
- //#region src/workflow/DependencyGraph.tsx
22
+ //#region src/graph/DependencyGraph.tsx
1134
23
  function ViewToggle({ viewMode, setViewMode }) {
1135
24
  return /* @__PURE__ */ jsxs("div", {
1136
25
  className: "flex border border-[var(--theme-border)] rounded overflow-hidden",
@@ -1235,12 +124,12 @@ function parsePrefixToPath(prefix) {
1235
124
  }
1236
125
  return path;
1237
126
  }
1238
- function computeGraphLayout(jobs, matrixGroups) {
127
+ function computeGraphLayout(engine, jobs, matrixGroups) {
1239
128
  const visibleJobs = structuredClone(jobs);
1240
129
  for (const jobName of Object.keys(jobs ?? {})) if (jobs[jobName]?.if === false) delete visibleJobs[jobName];
1241
- collapseMatrixGroupsInLayout(matrixGroups, visibleJobs);
130
+ engine.collapseMatrixGroups(matrixGroups, visibleJobs);
1242
131
  const allDeps = {};
1243
- for (const jobName of Object.keys(visibleJobs)) allDeps[jobName] = getAllDeps(jobName, visibleJobs, allDeps);
132
+ for (const jobName of Object.keys(visibleJobs)) allDeps[jobName] = engine.jobDependencies(jobName, visibleJobs, allDeps);
1244
133
  const filteredDeps = {};
1245
134
  for (const jobName of Object.keys(visibleJobs)) {
1246
135
  const needs = visibleJobs[jobName]?.needs;
@@ -1253,19 +142,19 @@ function computeGraphLayout(jobs, matrixGroups) {
1253
142
  }).sort();
1254
143
  }
1255
144
  const depended_on = {};
1256
- Object.keys(visibleJobs).map((jobName) => {
145
+ Object.keys(visibleJobs).forEach((jobName) => {
1257
146
  if (filteredDeps[jobName] && Array.isArray(filteredDeps[jobName])) filteredDeps[jobName].forEach((dep) => {
1258
147
  if (depended_on[dep]) depended_on[dep].push(jobName);
1259
148
  else depended_on[dep] = [jobName];
1260
149
  });
1261
150
  });
1262
- Object.keys(visibleJobs).map((jobName) => {
151
+ Object.keys(visibleJobs).forEach((jobName) => {
1263
152
  if (depended_on[jobName]) depended_on[jobName].sort();
1264
153
  });
1265
154
  const unfinished = structuredClone(visibleJobs);
1266
155
  const rootLevel = [];
1267
156
  const deps = [rootLevel];
1268
- Object.keys(visibleJobs).map((jobName) => {
157
+ Object.keys(visibleJobs).forEach((jobName) => {
1269
158
  if (filteredDeps[jobName]?.length === 0) {
1270
159
  rootLevel.push(jobName);
1271
160
  delete unfinished[jobName];
@@ -1276,7 +165,7 @@ function computeGraphLayout(jobs, matrixGroups) {
1276
165
  const toAppend = [];
1277
166
  const unfinishedArr = Object.keys(unfinished);
1278
167
  prevLen = unfinishedArr.length;
1279
- unfinishedArr.map((jobName) => {
168
+ unfinishedArr.forEach((jobName) => {
1280
169
  if ((filteredDeps[jobName] ?? []).every((dep) => !unfinishedArr.includes(dep))) {
1281
170
  toAppend.push(jobName);
1282
171
  delete unfinished[jobName];
@@ -1285,14 +174,14 @@ function computeGraphLayout(jobs, matrixGroups) {
1285
174
  if (toAppend.length > 0) deps.push(toAppend);
1286
175
  } while (prevLen !== Object.keys(unfinished).length);
1287
176
  const finalDeps = [];
1288
- deps.map((col) => {
177
+ deps.forEach((col) => {
1289
178
  const toAppend = [];
1290
179
  while (col.length > 0) {
1291
180
  const head = col[0];
1292
181
  if (head === void 0) break;
1293
182
  const grouped = [head];
1294
183
  const toRm = [0];
1295
- col.slice(1).map((jobName, j) => {
184
+ col.slice(1).forEach((jobName, j) => {
1296
185
  if (visibleJobs[head]?._matrixGroup || visibleJobs[jobName]?._matrixGroup) return;
1297
186
  const depsA = depended_on[head];
1298
187
  const depsB = depended_on[jobName];
@@ -1375,14 +264,19 @@ function SubworkflowWell({ expanded, onSettled, className, children }) {
1375
264
  var SUBGRAPH_Z_STEP = 100;
1376
265
  var FIT_ANIMATION_MS = 300;
1377
266
  function Subgraph({ jobs, pathPrefix, depth, preview, animT, expandedSubworkflows, toggleSubworkflow, openInNewGraph, setSublogOpen, invalidateConnectors, requestFit, stepsOpen, toggleSteps, matrixOpen, toggleMatrix, onReady }) {
267
+ const engine = useWorkflowEngine();
1378
268
  const wrapperRef = useRef(null);
1379
269
  const zBase = depth * SUBGRAPH_Z_STEP;
1380
270
  const [hoveredJob, setHoveredJob] = useState(null);
1381
271
  const lastHoveredDistancesRef = useRef(null);
1382
272
  const maxHoveredDistRef = useRef(0);
1383
273
  const retractPathLensRef = useRef(/* @__PURE__ */ new Map());
1384
- const matrixGroups = useMemo(() => detectMatrixGroups(jobs), [jobs]);
1385
- const { dependencyCols, directDeps, displayJobs, ancestorDists, descendantDists } = useMemo(() => computeGraphLayout(jobs, matrixGroups), [jobs, matrixGroups]);
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
+ ]);
1386
280
  const hoveredDistances = useMemo(() => {
1387
281
  if (!hoveredJob) return null;
1388
282
  const dist = /* @__PURE__ */ new Map();
@@ -1474,9 +368,9 @@ function Subgraph({ jobs, pathPrefix, depth, preview, animT, expandedSubworkflow
1474
368
  const stepStatus = jobs[j]?.steps?.[+s]?.status;
1475
369
  if (stepStatus === "skipped" || stepStatus === "skipped-failed") return;
1476
370
  if (jobs[j]?.steps[+s]?.subworkflow) expandInline(j, +s);
1477
- else setSublogOpen(buildSubworkflowLogPath(pathPrefix, j, s));
371
+ else setSublogOpen(engine.stepLogPath(pathPrefix, j, s));
1478
372
  };
1479
- const onStepLogClick = (j, s) => setSublogOpen(buildSubworkflowLogPath(pathPrefix, j, s));
373
+ const onStepLogClick = (j, s) => setSublogOpen(engine.stepLogPath(pathPrefix, j, s));
1480
374
  const onSubworkflowIcon = (j, s) => openNew(j, s);
1481
375
  const isSubworkflowExpanded = (j, s) => expandedSubworkflows.has(`${pathPrefix}${j}:${s}`);
1482
376
  const renderSubworkflow = (j, s) => {
@@ -1486,7 +380,7 @@ function Subgraph({ jobs, pathPrefix, depth, preview, animT, expandedSubworkflow
1486
380
  expanded: isSubworkflowExpanded(j, s),
1487
381
  onSettled: requestFit,
1488
382
  className: "my-2 rounded-lg border-4 border-(--theme-border)",
1489
- children: /* @__PURE__ */ jsx(Subgraph, {
383
+ children: /* @__PURE__ */ jsx(NestedSubgraph, {
1490
384
  jobs: subJobs,
1491
385
  pathPrefix: `${pathPrefix}subworkflows/${j}/step_${s}/`,
1492
386
  depth: depth + 1,
@@ -1584,6 +478,7 @@ function Subgraph({ jobs, pathPrefix, depth, preview, animT, expandedSubworkflow
1584
478
  toggleMatrix,
1585
479
  openInNewGraph,
1586
480
  setSublogOpen,
481
+ engine,
1587
482
  invalidateConnectors,
1588
483
  requestFit
1589
484
  ]);
@@ -1772,6 +667,7 @@ function Subgraph({ jobs, pathPrefix, depth, preview, animT, expandedSubworkflow
1772
667
  })(), hasLiveNestedGraph ? buildBoxLayer() : memoizedBoxLayer]
1773
668
  });
1774
669
  }
670
+ var NestedSubgraph = Subgraph;
1775
671
  function SidebarWell({ expanded, children }) {
1776
672
  return /* @__PURE__ */ jsx(Collapse, {
1777
673
  expanded,
@@ -1780,8 +676,13 @@ function SidebarWell({ expanded, children }) {
1780
676
  });
1781
677
  }
1782
678
  function SidebarJobs({ jobs, pathPrefix, preview, stepsOpen, toggleSteps, expandedSubworkflows, toggleSubworkflow, matrixOpen, toggleMatrix, openInNewGraph, setSublogOpen }) {
1783
- const matrixGroups = useMemo(() => detectMatrixGroups(jobs), [jobs]);
1784
- const { displayJobs } = useMemo(() => computeGraphLayout(jobs, matrixGroups), [jobs, matrixGroups]);
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
+ ]);
1785
686
  const isStepsOpen = (j) => stepsOpen.has(pathPrefix + j);
1786
687
  const onJobClick = (j) => {
1787
688
  const status = jobs[j]?.status ?? displayJobs[j]?.status;
@@ -1794,9 +695,9 @@ function SidebarJobs({ jobs, pathPrefix, preview, stepsOpen, toggleSteps, expand
1794
695
  const stepStatus = jobs[j]?.steps?.[+s]?.status;
1795
696
  if (stepStatus === "skipped" || stepStatus === "skipped-failed") return;
1796
697
  if (jobs[j]?.steps[+s]?.subworkflow) expandInline(j, +s);
1797
- else setSublogOpen(buildSubworkflowLogPath(pathPrefix, j, s));
698
+ else setSublogOpen(engine.stepLogPath(pathPrefix, j, s));
1798
699
  };
1799
- const onStepLogClick = (j, s) => setSublogOpen(buildSubworkflowLogPath(pathPrefix, j, s));
700
+ const onStepLogClick = (j, s) => setSublogOpen(engine.stepLogPath(pathPrefix, j, s));
1800
701
  const onSubworkflowIcon = (j, s) => openNew(j, s);
1801
702
  const isSubworkflowExpanded = (j, s) => expandedSubworkflows.has(`${pathPrefix}${j}:${s}`);
1802
703
  const renderSubworkflow = (j, s) => {
@@ -1823,13 +724,14 @@ function SidebarJobs({ jobs, pathPrefix, preview, stepsOpen, toggleSteps, expand
1823
724
  });
1824
725
  };
1825
726
  const seenMatrixGroups = /* @__PURE__ */ new Set();
1826
- return /* @__PURE__ */ jsx(Fragment$1, { children: Object.keys(jobs).flatMap((jobName) => {
727
+ const orderedKeys = Object.keys(jobs).flatMap((jobName) => {
1827
728
  const originaljob = jobs[jobName]?._matrix?.originaljob;
1828
729
  if (!originaljob) return [jobName];
1829
730
  if (seenMatrixGroups.has(originaljob)) return [];
1830
731
  seenMatrixGroups.add(originaljob);
1831
732
  return [originaljob];
1832
- }).map((jobName) => {
733
+ });
734
+ return /* @__PURE__ */ jsx(Fragment$1, { children: orderedKeys.map((jobName) => {
1833
735
  const mg = displayJobs[jobName]?._matrixGroup;
1834
736
  if (mg) return /* @__PURE__ */ jsx(MatrixGroupSummaryItem, {
1835
737
  matrixGroup: mg,
@@ -1885,6 +787,7 @@ function walkJobPath(root, path) {
1885
787
  return isJobRecord(cursor) ? cursor : {};
1886
788
  }
1887
789
  function DependencyGraph({ run, preview = false, initialScale = 1, removeBorder, viewMode = "dag", setViewMode }) {
790
+ const engine = useWorkflowEngine();
1888
791
  const animT = .125;
1889
792
  const minScale = .2;
1890
793
  const maxScale = .6;
@@ -2039,8 +942,12 @@ function DependencyGraph({ run, preview = false, initialScale = 1, removeBorder,
2039
942
  const sublogIsStep = jobAndStep.length > 1;
2040
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]) : "";
2041
944
  const sublogIsSubworkflow = jobAndStep.length > 1 && jobs[jobAndStep[0]]?.steps[Number(jobAndStep[1])]?.subworkflow;
2042
- const matrixGroups = useMemo(() => detectMatrixGroups(jobs), [jobs]);
2043
- const { dependencyCols } = useMemo(() => computeGraphLayout(jobs, matrixGroups), [jobs, matrixGroups]);
945
+ const matrixGroups = useMemo(() => engine.matrixGroups(jobs), [engine, jobs]);
946
+ const { dependencyCols } = useMemo(() => computeGraphLayout(engine, jobs, matrixGroups), [
947
+ engine,
948
+ jobs,
949
+ matrixGroups
950
+ ]);
2044
951
  const fitGraph = useCallback((animationMs = 0) => {
2045
952
  const container = containerRef.current;
2046
953
  const content = graphContentRef.current;
@@ -2104,11 +1011,15 @@ function DependencyGraph({ run, preview = false, initialScale = 1, removeBorder,
2104
1011
  for (let i = 0; i < nestingDepth - 1; i++) parentPrefix += `subworkflows/${currentPath[5 * i]}/step_${currentPath[5 * i + 2]?.toString()}/`;
2105
1012
  const lastJob = String(currentPath[5 * (nestingDepth - 1)] ?? "");
2106
1013
  const lastStep = currentPath[5 * (nestingDepth - 1) + 2] ?? "";
2107
- generalLogPath = buildSubworkflowLogPath(parentPrefix, lastJob, lastStep);
1014
+ generalLogPath = engine.stepLogPath(parentPrefix, lastJob, lastStep);
2108
1015
  }
2109
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;
2110
- const currentSublog = sublogOpen === "" ? "" : preview ? dumpYaml(jobs[jobAndStep[0]]?.steps[Number(jobAndStep[1])]) : sublogsError || !sublogs ? missingLogText : sublogs;
2111
- const expansionKeys = useMemo(() => collectExpansionKeys(jobs, pathAppend), [jobs, pathAppend]);
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
+ ]);
2112
1023
  const hasExpandable = expansionKeys.subworkflowKeys.length > 0 || expansionKeys.matrixKeys.length > 0;
2113
1024
  const allExpanded = hasExpandable && expansionKeys.subworkflowKeys.every((k) => expandedSubworkflows.has(k)) && expansionKeys.matrixKeys.every((k) => matrixOpen.has(k));
2114
1025
  const toggleExpandAll = () => {
@@ -2132,13 +1043,14 @@ function DependencyGraph({ run, preview = false, initialScale = 1, removeBorder,
2132
1043
  let step = parseInt(sublogSplit[sublogSplit.length - 2].slice(5), 10);
2133
1044
  const jobArr = Object.keys(jobs);
2134
1045
  let idx = jobArr.indexOf(job);
2135
- if (backward) if (0 < step) step -= 1;
2136
- else {
2137
- if (idx === 0) idx = jobArr.length;
2138
- job = jobArr[idx - 1] ?? "";
2139
- step = (jobs[job]?.steps.length ?? 0) - 1;
2140
- }
2141
- else if ((jobs[job]?.steps.length ?? 0) - 1 > step) step += 1;
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;
2142
1054
  else {
2143
1055
  if (idx === jobArr.length - 1) idx = -1;
2144
1056
  job = jobArr[idx + 1] ?? "";
@@ -2181,7 +1093,8 @@ function DependencyGraph({ run, preview = false, initialScale = 1, removeBorder,
2181
1093
  ]);
2182
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;
2183
1095
  const startDt = run.createdAt ? DateTime.fromISO(run.createdAt) : now;
2184
- const runtime = toAbsHumanDuration(startDt, run.completedAt ? DateTime.fromISO(run.completedAt) : now);
1096
+ const endDt = run.completedAt ? DateTime.fromISO(run.completedAt) : now;
1097
+ const runtime = toAbsHumanDuration(startDt, endDt);
2185
1098
  const additionalBottomLeftBarComponents = sublogIsStep ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
2186
1099
  /* @__PURE__ */ jsxs("button", {
2187
1100
  type: "button",
@@ -2222,7 +1135,7 @@ function DependencyGraph({ run, preview = false, initialScale = 1, removeBorder,
2222
1135
  children: slots.runSessions({
2223
1136
  runId: run.id ?? "",
2224
1137
  runNumber,
2225
- runActive: isRunActive(run.status),
1138
+ runActive: engine.isRunActive(run.status),
2226
1139
  links: run.links
2227
1140
  })
2228
1141
  }),
@@ -2482,4 +1395,4 @@ function DependencyGraphPreview(inputs) {
2482
1395
  });
2483
1396
  }
2484
1397
  //#endregion
2485
- export { DependencyGraph, DependencyGraphPreview };
1398
+ export { DependencyGraphPreview, DependencyGraph as default };