@shipfox/client-workflows 12.0.2 → 14.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (325) hide show
  1. package/.storybook/main.ts +16 -1
  2. package/.storybook/preview.tsx +3 -3
  3. package/.storybook/workspace-source.ts +31 -0
  4. package/.turbo/turbo-build.log +1 -1
  5. package/CHANGELOG.md +79 -0
  6. package/dist/components/job-graph/job-duration-format.d.ts +2 -2
  7. package/dist/components/job-graph/job-duration-format.d.ts.map +1 -1
  8. package/dist/components/job-graph/job-duration-format.js +4 -2
  9. package/dist/components/job-graph/job-duration-format.js.map +1 -1
  10. package/dist/components/job-graph/job-duration-label.js +1 -1
  11. package/dist/components/job-graph/job-duration-label.js.map +1 -1
  12. package/dist/components/job-graph/job-graph-content.d.ts +2 -1
  13. package/dist/components/job-graph/job-graph-content.d.ts.map +1 -1
  14. package/dist/components/job-graph/job-graph-content.js +3 -3
  15. package/dist/components/job-graph/job-graph-content.js.map +1 -1
  16. package/dist/components/job-graph/job-graph-view.d.ts +2 -1
  17. package/dist/components/job-graph/job-graph-view.d.ts.map +1 -1
  18. package/dist/components/job-graph/job-graph-view.js +2 -2
  19. package/dist/components/job-graph/job-graph-view.js.map +1 -1
  20. package/dist/components/job-graph/job-node.d.ts +1 -1
  21. package/dist/components/job-graph/job-node.d.ts.map +1 -1
  22. package/dist/components/job-graph/job-node.js +10 -9
  23. package/dist/components/job-graph/job-node.js.map +1 -1
  24. package/dist/components/job-graph/types.d.ts +2 -1
  25. package/dist/components/job-graph/types.d.ts.map +1 -1
  26. package/dist/components/job-graph/types.js.map +1 -1
  27. package/dist/components/step-list/step-list.d.ts +2 -2
  28. package/dist/components/step-list/step-list.d.ts.map +1 -1
  29. package/dist/components/step-list/step-list.js.map +1 -1
  30. package/dist/components/workflow-run-duration-label.js +1 -1
  31. package/dist/components/workflow-run-duration-label.js.map +1 -1
  32. package/dist/components/workflow-run-list/job-status-strip.d.ts +21 -0
  33. package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -0
  34. package/dist/components/workflow-run-list/job-status-strip.js +185 -0
  35. package/dist/components/workflow-run-list/job-status-strip.js.map +1 -0
  36. package/dist/components/workflow-run-list/run-display.d.ts +27 -3
  37. package/dist/components/workflow-run-list/run-display.d.ts.map +1 -1
  38. package/dist/components/workflow-run-list/run-display.js +78 -7
  39. package/dist/components/workflow-run-list/run-display.js.map +1 -1
  40. package/dist/components/workflow-run-list/types.d.ts +23 -24
  41. package/dist/components/workflow-run-list/types.d.ts.map +1 -1
  42. package/dist/components/workflow-run-list/types.js.map +1 -1
  43. package/dist/components/workflow-run-list/workflow-run-filter-menu.d.ts +22 -0
  44. package/dist/components/workflow-run-list/workflow-run-filter-menu.d.ts.map +1 -0
  45. package/dist/components/workflow-run-list/workflow-run-filter-menu.js +68 -0
  46. package/dist/components/workflow-run-list/workflow-run-filter-menu.js.map +1 -0
  47. package/dist/components/workflow-run-list/workflow-run-filters.d.ts +18 -0
  48. package/dist/components/workflow-run-list/workflow-run-filters.d.ts.map +1 -0
  49. package/dist/components/workflow-run-list/workflow-run-filters.js +236 -0
  50. package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -0
  51. package/dist/components/workflow-run-list/workflow-run-list-content.d.ts +8 -4
  52. package/dist/components/workflow-run-list/workflow-run-list-content.d.ts.map +1 -1
  53. package/dist/components/workflow-run-list/workflow-run-list-content.js +40 -12
  54. package/dist/components/workflow-run-list/workflow-run-list-content.js.map +1 -1
  55. package/dist/components/workflow-run-list/workflow-run-list-states.d.ts +29 -2
  56. package/dist/components/workflow-run-list/workflow-run-list-states.d.ts.map +1 -1
  57. package/dist/components/workflow-run-list/workflow-run-list-states.js +103 -44
  58. package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
  59. package/dist/components/workflow-run-list/workflow-run-list-view.d.ts +1 -1
  60. package/dist/components/workflow-run-list/workflow-run-list-view.d.ts.map +1 -1
  61. package/dist/components/workflow-run-list/workflow-run-list-view.js +60 -43
  62. package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
  63. package/dist/components/workflow-run-list/workflow-run-list.d.ts +1 -1
  64. package/dist/components/workflow-run-list/workflow-run-list.d.ts.map +1 -1
  65. package/dist/components/workflow-run-list/workflow-run-list.js +20 -8
  66. package/dist/components/workflow-run-list/workflow-run-list.js.map +1 -1
  67. package/dist/components/workflow-run-list/workflow-run-row.d.ts +19 -9
  68. package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
  69. package/dist/components/workflow-run-list/workflow-run-row.js +179 -112
  70. package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
  71. package/dist/components/workflow-run-number-label.d.ts +15 -0
  72. package/dist/components/workflow-run-number-label.d.ts.map +1 -0
  73. package/dist/components/workflow-run-number-label.js +41 -0
  74. package/dist/components/workflow-run-number-label.js.map +1 -0
  75. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.d.ts +3 -3
  76. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.d.ts.map +1 -1
  77. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +10 -9
  78. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js.map +1 -1
  79. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts +4 -9
  80. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
  81. package/dist/components/workflow-run-summary/workflow-run-summary.js +107 -74
  82. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
  83. package/dist/components/workflow-run-tabs/index.d.ts +6 -0
  84. package/dist/components/workflow-run-tabs/index.d.ts.map +1 -0
  85. package/dist/components/workflow-run-tabs/index.js +7 -0
  86. package/dist/components/workflow-run-tabs/index.js.map +1 -0
  87. package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts +11 -0
  88. package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts.map +1 -0
  89. package/dist/components/workflow-run-tabs/run-annotation-summary-line.js +71 -0
  90. package/dist/components/workflow-run-tabs/run-annotation-summary-line.js.map +1 -0
  91. package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts +2 -0
  92. package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts.map +1 -0
  93. package/dist/components/workflow-run-tabs/run-annotations-empty.js +11 -0
  94. package/dist/components/workflow-run-tabs/run-annotations-empty.js.map +1 -0
  95. package/dist/components/workflow-run-tabs/run-jobs-list.d.ts +18 -0
  96. package/dist/components/workflow-run-tabs/run-jobs-list.d.ts.map +1 -0
  97. package/dist/components/workflow-run-tabs/run-jobs-list.js +107 -0
  98. package/dist/components/workflow-run-tabs/run-jobs-list.js.map +1 -0
  99. package/dist/components/workflow-run-tabs/run-tab-count.d.ts +6 -0
  100. package/dist/components/workflow-run-tabs/run-tab-count.d.ts.map +1 -0
  101. package/dist/components/workflow-run-tabs/run-tab-count.js +27 -0
  102. package/dist/components/workflow-run-tabs/run-tab-count.js.map +1 -0
  103. package/dist/components/workflow-run-tabs/run-tab-strip.d.ts +8 -0
  104. package/dist/components/workflow-run-tabs/run-tab-strip.d.ts.map +1 -0
  105. package/dist/components/workflow-run-tabs/run-tab-strip.js +82 -0
  106. package/dist/components/workflow-run-tabs/run-tab-strip.js.map +1 -0
  107. package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts +2 -2
  108. package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts.map +1 -1
  109. package/dist/components/workflow-run-view/agent-config-failure-callout.js +4 -4
  110. package/dist/components/workflow-run-view/agent-config-failure-callout.js.map +1 -1
  111. package/dist/components/workflow-run-view/job-card.d.ts +2 -2
  112. package/dist/components/workflow-run-view/job-card.d.ts.map +1 -1
  113. package/dist/components/workflow-run-view/job-card.js +51 -32
  114. package/dist/components/workflow-run-view/job-card.js.map +1 -1
  115. package/dist/components/workflow-run-view/job-execution-switcher.d.ts +1 -1
  116. package/dist/components/workflow-run-view/job-execution-switcher.d.ts.map +1 -1
  117. package/dist/components/workflow-run-view/job-execution-switcher.js +20 -8
  118. package/dist/components/workflow-run-view/job-execution-switcher.js.map +1 -1
  119. package/dist/components/workflow-run-view/workflow-run-selection.d.ts.map +1 -1
  120. package/dist/components/workflow-run-view/workflow-run-selection.js +40 -1
  121. package/dist/components/workflow-run-view/workflow-run-selection.js.map +1 -1
  122. package/dist/components/workflow-run-view/workflow-run-states.d.ts +1 -0
  123. package/dist/components/workflow-run-view/workflow-run-states.d.ts.map +1 -1
  124. package/dist/components/workflow-run-view/workflow-run-states.js +10 -1
  125. package/dist/components/workflow-run-view/workflow-run-states.js.map +1 -1
  126. package/dist/components/workflow-run-view/workflow-run-view.d.ts +10 -5
  127. package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
  128. package/dist/components/workflow-run-view/workflow-run-view.js +136 -85
  129. package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
  130. package/dist/components/workflow-source-panel/index.d.ts +1 -0
  131. package/dist/components/workflow-source-panel/index.d.ts.map +1 -1
  132. package/dist/components/workflow-source-panel/index.js +1 -0
  133. package/dist/components/workflow-source-panel/index.js.map +1 -1
  134. package/dist/components/workflow-source-panel/workflow-source-content.d.ts +12 -0
  135. package/dist/components/workflow-source-panel/workflow-source-content.d.ts.map +1 -0
  136. package/dist/components/workflow-source-panel/workflow-source-content.js +53 -0
  137. package/dist/components/workflow-source-panel/workflow-source-content.js.map +1 -0
  138. package/dist/components/workflow-source-panel/workflow-source-panel.d.ts +1 -1
  139. package/dist/components/workflow-source-panel/workflow-source-panel.d.ts.map +1 -1
  140. package/dist/components/workflow-source-panel/workflow-source-panel.js +7 -54
  141. package/dist/components/workflow-source-panel/workflow-source-panel.js.map +1 -1
  142. package/dist/components/workflow-status/status-visuals.d.ts +4 -4
  143. package/dist/components/workflow-status/status-visuals.d.ts.map +1 -1
  144. package/dist/components/workflow-status/status-visuals.js +7 -0
  145. package/dist/components/workflow-status/status-visuals.js.map +1 -1
  146. package/dist/components/workflow-status/workflow-status-icon.d.ts +3 -4
  147. package/dist/components/workflow-status/workflow-status-icon.d.ts.map +1 -1
  148. package/dist/components/workflow-status/workflow-status-icon.js +6 -4
  149. package/dist/components/workflow-status/workflow-status-icon.js.map +1 -1
  150. package/dist/core/entities/job-execution.d.ts +10 -0
  151. package/dist/core/entities/job-execution.d.ts.map +1 -1
  152. package/dist/core/entities/job-execution.js +11 -0
  153. package/dist/core/entities/job-execution.js.map +1 -1
  154. package/dist/core/entities/job.d.ts +4 -2
  155. package/dist/core/entities/job.d.ts.map +1 -1
  156. package/dist/core/entities/job.js +8 -1
  157. package/dist/core/entities/job.js.map +1 -1
  158. package/dist/core/entities/step.d.ts +1 -1
  159. package/dist/core/entities/step.d.ts.map +1 -1
  160. package/dist/core/entities/step.js +2 -0
  161. package/dist/core/entities/step.js.map +1 -1
  162. package/dist/core/entities/workflow-run.d.ts +52 -4
  163. package/dist/core/entities/workflow-run.d.ts.map +1 -1
  164. package/dist/core/entities/workflow-run.js +22 -3
  165. package/dist/core/entities/workflow-run.js.map +1 -1
  166. package/dist/core/workflow-run-tabs.d.ts +9 -0
  167. package/dist/core/workflow-run-tabs.d.ts.map +1 -0
  168. package/dist/core/workflow-run-tabs.js +3 -0
  169. package/dist/core/workflow-run-tabs.js.map +1 -0
  170. package/dist/core/workflow-run.d.ts +6 -6
  171. package/dist/core/workflow-run.d.ts.map +1 -1
  172. package/dist/core/workflow-run.js +3 -3
  173. package/dist/core/workflow-run.js.map +1 -1
  174. package/dist/feature.d.ts +7 -7
  175. package/dist/feature.js +5 -5
  176. package/dist/feature.js.map +1 -1
  177. package/dist/hooks/api/workflow-run-mapper.d.ts +4 -3
  178. package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
  179. package/dist/hooks/api/workflow-run-mapper.js +31 -3
  180. package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
  181. package/dist/hooks/api/workflow-runs.d.ts +4 -4
  182. package/dist/hooks/api/workflow-runs.d.ts.map +1 -1
  183. package/dist/hooks/api/workflow-runs.js +12 -4
  184. package/dist/hooks/api/workflow-runs.js.map +1 -1
  185. package/dist/index.d.ts +4 -3
  186. package/dist/index.d.ts.map +1 -1
  187. package/dist/index.js +3 -2
  188. package/dist/index.js.map +1 -1
  189. package/dist/pages/project-workflows-page.js +46 -1
  190. package/dist/pages/project-workflows-page.js.map +1 -1
  191. package/dist/pages/workflow-run-detail-page.d.ts +11 -0
  192. package/dist/pages/workflow-run-detail-page.d.ts.map +1 -0
  193. package/dist/pages/workflow-run-detail-page.js +52 -0
  194. package/dist/pages/workflow-run-detail-page.js.map +1 -0
  195. package/dist/pages/workflow-run-list-page.d.ts +10 -0
  196. package/dist/pages/workflow-run-list-page.d.ts.map +1 -0
  197. package/dist/pages/workflow-run-list-page.js +43 -0
  198. package/dist/pages/workflow-run-list-page.js.map +1 -0
  199. package/dist/routes/inputs.d.ts +94 -5
  200. package/dist/routes/inputs.d.ts.map +1 -1
  201. package/dist/routes/inputs.js +199 -13
  202. package/dist/routes/inputs.js.map +1 -1
  203. package/dist/routes/project-route.d.ts +6 -0
  204. package/dist/routes/project-route.d.ts.map +1 -0
  205. package/dist/routes/project-route.js +25 -0
  206. package/dist/routes/project-route.js.map +1 -0
  207. package/dist/routes/run-detail.d.ts.map +1 -1
  208. package/dist/routes/run-detail.js +12 -7
  209. package/dist/routes/run-detail.js.map +1 -1
  210. package/dist/routes/runs.d.ts.map +1 -1
  211. package/dist/routes/runs.js +11 -6
  212. package/dist/routes/runs.js.map +1 -1
  213. package/dist/routes/workflows.d.ts.map +1 -1
  214. package/dist/routes/workflows.js +6 -5
  215. package/dist/routes/workflows.js.map +1 -1
  216. package/dist/tsconfig.test.tsbuildinfo +1 -1
  217. package/package.json +9 -9
  218. package/src/components/job-graph/job-duration-format.ts +11 -2
  219. package/src/components/job-graph/job-duration-label.tsx +5 -1
  220. package/src/components/job-graph/job-graph-content.tsx +7 -4
  221. package/src/components/job-graph/job-graph-view.test.tsx +19 -8
  222. package/src/components/job-graph/job-graph-view.tsx +6 -3
  223. package/src/components/job-graph/job-graph.stories.tsx +24 -20
  224. package/src/components/job-graph/job-node.stories.tsx +11 -6
  225. package/src/components/job-graph/job-node.test.tsx +82 -12
  226. package/src/components/job-graph/job-node.tsx +22 -15
  227. package/src/components/job-graph/types.ts +5 -1
  228. package/src/components/step-list/step-list.stories.tsx +2 -3
  229. package/src/components/step-list/step-list.tsx +3 -2
  230. package/src/components/workflow-run-duration-label.tsx +1 -1
  231. package/src/components/workflow-run-list/job-status-strip.tsx +189 -0
  232. package/src/components/workflow-run-list/run-display.test.ts +169 -30
  233. package/src/components/workflow-run-list/run-display.ts +116 -9
  234. package/src/components/workflow-run-list/types.ts +21 -17
  235. package/src/components/workflow-run-list/workflow-run-filter-menu.tsx +100 -0
  236. package/src/components/workflow-run-list/workflow-run-filters.tsx +243 -0
  237. package/src/components/workflow-run-list/workflow-run-list-content.tsx +44 -16
  238. package/src/components/workflow-run-list/workflow-run-list-states.tsx +128 -27
  239. package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +441 -104
  240. package/src/components/workflow-run-list/workflow-run-list-view.tsx +73 -43
  241. package/src/components/workflow-run-list/workflow-run-list.stories.tsx +140 -20
  242. package/src/components/workflow-run-list/workflow-run-list.tsx +20 -10
  243. package/src/components/workflow-run-list/workflow-run-row.tsx +173 -106
  244. package/src/components/workflow-run-number-label.test.tsx +45 -0
  245. package/src/components/workflow-run-number-label.tsx +47 -0
  246. package/src/components/workflow-run-summary/workflow-run-attempt-switcher.test.tsx +7 -8
  247. package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +15 -14
  248. package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +29 -38
  249. package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +30 -24
  250. package/src/components/workflow-run-summary/workflow-run-summary.tsx +102 -77
  251. package/src/components/workflow-run-tabs/index.ts +5 -0
  252. package/src/components/workflow-run-tabs/run-annotation-summary-line.test.tsx +69 -0
  253. package/src/components/workflow-run-tabs/run-annotation-summary-line.tsx +97 -0
  254. package/src/components/workflow-run-tabs/run-annotations-empty.tsx +11 -0
  255. package/src/components/workflow-run-tabs/run-jobs-list.tsx +151 -0
  256. package/src/components/workflow-run-tabs/run-tab-count.tsx +28 -0
  257. package/src/components/workflow-run-tabs/run-tab-strip.stories.tsx +52 -0
  258. package/src/components/workflow-run-tabs/run-tab-strip.test.tsx +117 -0
  259. package/src/components/workflow-run-tabs/run-tab-strip.tsx +97 -0
  260. package/src/components/workflow-run-view/agent-config-failure-callout.stories.tsx +2 -3
  261. package/src/components/workflow-run-view/agent-config-failure-callout.tsx +4 -4
  262. package/src/components/workflow-run-view/job-card.stories.tsx +11 -10
  263. package/src/components/workflow-run-view/job-card.test.tsx +115 -0
  264. package/src/components/workflow-run-view/job-card.tsx +74 -35
  265. package/src/components/workflow-run-view/job-execution-switcher.tsx +31 -9
  266. package/src/components/workflow-run-view/workflow-run-selection.test.ts +48 -0
  267. package/src/components/workflow-run-view/workflow-run-selection.ts +48 -1
  268. package/src/components/workflow-run-view/workflow-run-states.tsx +12 -1
  269. package/src/components/workflow-run-view/workflow-run-view.test.tsx +152 -94
  270. package/src/components/workflow-run-view/workflow-run-view.tsx +219 -95
  271. package/src/components/workflow-source-panel/index.ts +1 -0
  272. package/src/components/workflow-source-panel/workflow-source-content.tsx +84 -0
  273. package/src/components/workflow-source-panel/workflow-source-panel.tsx +14 -81
  274. package/src/components/workflow-status/status-visuals.test.ts +9 -0
  275. package/src/components/workflow-status/status-visuals.ts +6 -4
  276. package/src/components/workflow-status/workflow-status-icon.stories.tsx +10 -2
  277. package/src/components/workflow-status/workflow-status-icon.test.tsx +18 -0
  278. package/src/components/workflow-status/workflow-status-icon.tsx +17 -24
  279. package/src/core/entities/job-execution.ts +22 -0
  280. package/src/core/entities/job-status.test.ts +104 -0
  281. package/src/core/entities/job.ts +18 -3
  282. package/src/core/entities/step.ts +4 -0
  283. package/src/core/entities/workflow-run.ts +76 -7
  284. package/src/core/workflow-run-tabs.ts +8 -0
  285. package/src/core/workflow-run.test.ts +24 -9
  286. package/src/core/workflow-run.ts +17 -2
  287. package/src/feature.ts +5 -5
  288. package/src/hooks/api/workflow-run-mapper.ts +30 -3
  289. package/src/hooks/api/workflow-runs.test.tsx +2 -0
  290. package/src/hooks/api/workflow-runs.ts +12 -7
  291. package/src/index.ts +7 -2
  292. package/src/page-harness-budget.test.ts +1 -1
  293. package/src/pages/project-workflows-page.test.tsx +59 -5
  294. package/src/pages/project-workflows-page.tsx +34 -1
  295. package/src/pages/workflow-run-detail-page.tsx +72 -0
  296. package/src/pages/workflow-run-list-page.tsx +65 -0
  297. package/src/pages/workflow-run-pages.test.tsx +823 -0
  298. package/src/routes/inputs.test.ts +240 -12
  299. package/src/routes/inputs.ts +215 -11
  300. package/src/routes/project-route.tsx +25 -0
  301. package/src/routes/run-detail.tsx +15 -8
  302. package/src/routes/runs.tsx +14 -7
  303. package/src/routes/workflows.tsx +5 -4
  304. package/src/storybook-config.test.ts +27 -0
  305. package/test/fixtures/workflow-run.ts +76 -7
  306. package/test/pages.tsx +18 -8
  307. package/test/render.tsx +8 -2
  308. package/tsconfig.build.tsbuildinfo +1 -1
  309. package/tsconfig.test.json +7 -1
  310. package/dist/components/workflow-run-list/workflow-run-list-header.d.ts +0 -10
  311. package/dist/components/workflow-run-list/workflow-run-list-header.d.ts.map +0 -1
  312. package/dist/components/workflow-run-list/workflow-run-list-header.js +0 -55
  313. package/dist/components/workflow-run-list/workflow-run-list-header.js.map +0 -1
  314. package/dist/pages/workflow-run-first-time-use.d.ts +0 -6
  315. package/dist/pages/workflow-run-first-time-use.d.ts.map +0 -1
  316. package/dist/pages/workflow-run-first-time-use.js +0 -17
  317. package/dist/pages/workflow-run-first-time-use.js.map +0 -1
  318. package/dist/pages/workflow-run-page.d.ts +0 -10
  319. package/dist/pages/workflow-run-page.d.ts.map +0 -1
  320. package/dist/pages/workflow-run-page.js +0 -90
  321. package/dist/pages/workflow-run-page.js.map +0 -1
  322. package/src/components/workflow-run-list/workflow-run-list-header.tsx +0 -53
  323. package/src/pages/workflow-run-first-time-use.tsx +0 -17
  324. package/src/pages/workflow-run-page.test.tsx +0 -519
  325. package/src/pages/workflow-run-page.tsx +0 -94
@@ -1,24 +1,26 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { ApiError } from '@shipfox/client-api';
3
3
  import { QueryLoadError } from '@shipfox/client-ui';
4
+ import { EmptyState } from '@shipfox/react-ui/empty-state';
4
5
  import { RelativeTimeProvider } from '@shipfox/react-ui/relative-time';
6
+ import { Tabs, TabsContent, TabsContents } from '@shipfox/react-ui/tabs';
5
7
  import { toast } from '@shipfox/react-ui/toast';
6
8
  import { useNavigate } from '@tanstack/react-router';
7
9
  import { useEffect, useId, useRef, useState } from 'react';
8
10
  import { resolveJobExecution } from '#core/workflow-run.js';
9
11
  import { withoutWorkflowRunSelectionSearch } from '#core/workflow-run-url-state.js';
10
12
  import { useCancelWorkflowRunMutation, useRerunWorkflowRunMutation, useWorkflowRunAttemptQuery } from '#hooks/api/workflow-runs.js';
13
+ import { WORKFLOW_RUN_TABS } from '#routes/inputs.js';
11
14
  import { JobGraph } from '../job-graph/index.js';
12
15
  import { WorkflowRunSummary } from '../workflow-run-summary/index.js';
13
- import { WorkflowSourcePanel } from '../workflow-source-panel/index.js';
14
- import { JobCard } from './job-card.js';
16
+ import { RunAnnotationSummaryLine, RunAnnotationsEmpty, RunJobsList, RunTabStrip } from '../workflow-run-tabs/index.js';
17
+ import { WorkflowSourceContent, WorkflowSourcePanel } from '../workflow-source-panel/index.js';
15
18
  import { resolveWorkflowRunSelection } from './workflow-run-selection.js';
16
- import { WorkflowRunNotFound, WorkflowRunSkeleton, WorkflowRunStaleError } from './workflow-run-states.js';
19
+ import { WorkflowRunContentSkeleton, WorkflowRunNotFound, WorkflowRunSkeleton, WorkflowRunStaleError } from './workflow-run-states.js';
17
20
  /**
18
- * Renders the run for `workflowRunId`, or a skeleton while `workflowRunId` is still unknown (the page is
19
- * resolving which run to show) or the run is loading, so the page never branches on the
20
- * loading state itself.
21
- */ export function WorkflowRunView({ workspaceId, projectId, workflowRunId, selection, onSelectionChange }) {
21
+ * Renders the run for `workflowRunId`, keeping the local tab strip mounted while the run
22
+ * request is pending so polling never inserts navigation under the user's cursor.
23
+ */ export function WorkflowRunView({ projectId, workspaceSlug, projectSlug, workflowRunId, selection, onSelectionChange, tab, onTabChange, annotationSummary }) {
22
24
  const runQuery = useWorkflowRunAttemptQuery({
23
25
  workflowRunId,
24
26
  runAttempt: selection?.runAttempt
@@ -28,28 +30,32 @@ import { WorkflowRunNotFound, WorkflowRunSkeleton, WorkflowRunStaleError } from
28
30
  children: /*#__PURE__*/ _jsx("div", {
29
31
  className: "flex min-h-0 min-w-0 flex-1 overflow-hidden",
30
32
  children: /*#__PURE__*/ _jsx(RunViewContent, {
31
- workspaceId: workspaceId,
32
- projectId: projectId,
33
+ workspaceSlug: workspaceSlug,
34
+ projectSlug: projectSlug,
33
35
  query: runQuery,
34
36
  rerunMutation: rerunMutation,
35
37
  selection: selection,
36
- onSelectionChange: onSelectionChange
38
+ onSelectionChange: onSelectionChange,
39
+ tab: tab,
40
+ onTabChange: onTabChange,
41
+ annotationSummary: annotationSummary
37
42
  })
38
43
  })
39
44
  });
40
45
  }
41
- function RunViewContent({ workspaceId, projectId, query, rerunMutation, selection, onSelectionChange }) {
46
+ function RunViewContent({ workspaceSlug, projectSlug, query, rerunMutation, selection, onSelectionChange, tab, onTabChange, annotationSummary }) {
42
47
  const navigate = useNavigate();
43
48
  const [selectedJobId, setSelectedJobId] = useState();
44
49
  const [selectedJobExecutionId, setSelectedJobExecutionId] = useState();
50
+ const [localTab, setLocalTab] = useState(tab ?? 'summary');
45
51
  const [sourcePanelOpen, setSourcePanelOpen] = useState(false);
46
52
  const [sourceFocus, setSourceFocus] = useState(null);
47
53
  const sourcePanelId = useId();
48
- const sourceButtonRef = useRef(null);
49
- // The button that last opened the panel (summary or a step detail), so Escape /
50
- // Close returns focus to whoever opened it.
54
+ // The step Source button that last opened the panel, so Escape / Close returns focus to it.
51
55
  const lastSourceTriggerRef = useRef(null);
52
56
  const selectionControlled = selection !== undefined;
57
+ const tabControlled = onTabChange !== undefined;
58
+ const activeTab = tabControlled ? tab ?? 'summary' : localTab;
53
59
  const sourceAvailable = query.data?.sourceSnapshot !== null && query.data?.sourceSnapshot !== undefined;
54
60
  const cancelMutation = useCancelWorkflowRunMutation(query.data);
55
61
  useEffect(()=>{
@@ -60,47 +66,52 @@ function RunViewContent({ workspaceId, projectId, query, rerunMutation, selectio
60
66
  }, [
61
67
  sourceAvailable
62
68
  ]);
63
- // If a refetch drops the focused step or its location, degrade to whole-workflow
64
- // focus so the panel never points at an unmounted Source button.
69
+ // If a refetch drops the focused step or its location, close the panel so it never points at
70
+ // an unmounted Source button.
65
71
  useEffect(()=>{
66
72
  if (!sourceFocus) return;
67
73
  const stillLocated = query.data?.jobs.some((job)=>job.jobExecutions.some((execution)=>execution.steps.some((step)=>step.id === sourceFocus.stepId && step.sourceLocation)));
68
74
  if (!stillLocated) {
69
75
  setSourceFocus(null);
70
- lastSourceTriggerRef.current = sourceButtonRef.current;
76
+ setSourcePanelOpen(false);
71
77
  }
72
78
  }, [
73
79
  sourceFocus,
74
80
  query.data
75
81
  ]);
76
- if (query.isPending) return /*#__PURE__*/ _jsx(WorkflowRunSkeleton, {});
77
- // Only show the full error placeholder when nothing ever loaded. A refetch that fails after
78
- // a prior success keeps the loaded run on screen (see below) instead of wiping the pane,
79
- // since active-run polling can hit a transient API error.
80
- if (query.isError && query.data === undefined) {
81
- if (query.error instanceof ApiError && query.error.status === 404) {
82
- return /*#__PURE__*/ _jsx(WorkflowRunNotFound, {});
83
- }
84
- return /*#__PURE__*/ _jsx(QueryLoadError, {
85
- query: query,
86
- subject: "workflow run",
87
- icon: "pulseLine"
88
- });
89
- }
90
- if (query.data === undefined) return /*#__PURE__*/ _jsx(WorkflowRunSkeleton, {});
91
82
  const runData = query.data;
92
- const resolvedSelection = selectionControlled ? resolveWorkflowRunSelection({
83
+ const resolvedSelection = selectionControlled && runData ? resolveWorkflowRunSelection({
93
84
  run: runData,
94
- selection
85
+ selection: selection
95
86
  }) : undefined;
96
- const selectedJob = selectionControlled ? resolvedSelection?.job : runData.jobs.find((job)=>job.id === selectedJobId) ?? runData.jobs.at(0);
87
+ const hasExplicitJobSelection = Boolean(selection?.jobId || selection?.jobExecutionId || selection?.stepId || selection?.stepAttemptId);
88
+ const selectedJob = selectionControlled ? hasExplicitJobSelection ? resolvedSelection?.job : undefined : runData?.jobs.find((job)=>job.id === selectedJobId) ?? runData?.jobs.at(0);
97
89
  const selectedJobExecution = selectionControlled ? resolvedSelection?.jobExecution : selectedJob ? resolveJobExecution(selectedJob, selectedJobExecutionId) : undefined;
98
90
  const selectedAttemptId = selectionControlled ? resolvedSelection?.selectedAttemptId ?? null : undefined;
99
- // Explicit per-step focus wins; fall back to the URL-selected step so deep links
100
- // still pre-highlight when the summary opens the whole-workflow source.
91
+ // Explicit per-step focus wins; fall back to the URL-selected step so deep links still
92
+ // pre-highlight source in the Source tab.
101
93
  const highlightedLineRange = sourceFocus?.location ?? resolvedSelection?.step?.sourceLocation ?? null;
102
- const sourceSnapshot = runData.sourceSnapshot;
94
+ const sourceSnapshot = runData?.sourceSnapshot ?? null;
95
+ function changeTab(nextTab) {
96
+ if (!tabControlled) {
97
+ setLocalTab(nextTab);
98
+ } else if (activeTab !== nextTab) {
99
+ onTabChange?.(nextTab);
100
+ }
101
+ if (nextTab !== 'jobs') {
102
+ setSourcePanelOpen(false);
103
+ setSourceFocus(null);
104
+ }
105
+ }
106
+ function handleTabChange(nextTab) {
107
+ if (!WORKFLOW_RUN_TABS.some((value)=>value === nextTab)) return;
108
+ changeTab(nextTab);
109
+ }
103
110
  async function rerun(mode) {
111
+ if (!runData || !workspaceSlug || !projectSlug) {
112
+ toast.error('Could not start re-run from this route.');
113
+ return;
114
+ }
104
115
  try {
105
116
  const run = await rerunMutation.mutateAsync({
106
117
  workflowRunId: runData.id,
@@ -108,10 +119,10 @@ function RunViewContent({ workspaceId, projectId, query, rerunMutation, selectio
108
119
  });
109
120
  toast.success('Re-run started');
110
121
  await navigate({
111
- to: '/workspaces/$wid/projects/$pid/runs/$workflowRunId',
122
+ to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',
112
123
  params: {
113
- wid: workspaceId,
114
- pid: projectId,
124
+ workspaceSlug,
125
+ projectSlug,
115
126
  workflowRunId: run.id
116
127
  },
117
128
  search: (previous)=>withoutWorkflowRunSelectionSearch(previous)
@@ -120,12 +131,16 @@ function RunViewContent({ workspaceId, projectId, query, rerunMutation, selectio
120
131
  toast.error(error instanceof ApiError ? error.message : 'Could not start re-run');
121
132
  }
122
133
  }
123
- function selectJob(jobId) {
134
+ function selectJob(jobId, source = 'pointer') {
135
+ if (source === 'pointer') changeTab('jobs');
124
136
  if (!selectionControlled) {
125
137
  setSelectedJobId(jobId);
126
138
  setSelectedJobExecutionId(undefined);
127
139
  return;
128
140
  }
141
+ // Keyboard roving focus is an interaction within the Summary graph. Keep that panel mounted
142
+ // and let JobGraphView own the visual selection so focus is not moved to a different tab.
143
+ if (source === 'keyboard') return;
129
144
  onSelectionChange?.(jobId ? {
130
145
  jobId,
131
146
  runAttempt: selection?.runAttempt
@@ -165,18 +180,6 @@ function RunViewContent({ workspaceId, projectId, query, rerunMutation, selectio
165
180
  runAttempt: selection?.runAttempt
166
181
  });
167
182
  }
168
- function openWholeWorkflowSource() {
169
- setSourceFocus(null);
170
- lastSourceTriggerRef.current = sourceButtonRef.current;
171
- setSourcePanelOpen(true);
172
- }
173
- function toggleWholeWorkflowSource() {
174
- if (sourcePanelOpen && sourceFocus === null) {
175
- closeSourcePanel();
176
- return;
177
- }
178
- openWholeWorkflowSource();
179
- }
180
183
  function openStepSource(stepId, location, trigger) {
181
184
  setSourceFocus({
182
185
  stepId,
@@ -187,13 +190,11 @@ function RunViewContent({ workspaceId, projectId, query, rerunMutation, selectio
187
190
  }
188
191
  function closeSourcePanel() {
189
192
  const trigger = lastSourceTriggerRef.current;
190
- const fallbackTrigger = sourceButtonRef.current;
191
193
  setSourcePanelOpen(false);
192
- // Defer so focus lands after the panel unmounts; clear the focus only after
193
- // focusing so the opener button is still expanded (force-visible) on return.
194
+ // Defer so focus lands after the panel unmounts; clear the focus only after focusing so the
195
+ // opener button is still expanded (force-visible) on return.
194
196
  window.setTimeout(()=>{
195
- const focusTarget = trigger?.isConnected ? trigger : fallbackTrigger;
196
- focusTarget?.focus();
197
+ if (trigger?.isConnected) trigger.focus();
197
198
  setSourceFocus(null);
198
199
  }, 0);
199
200
  }
@@ -204,53 +205,103 @@ function RunViewContent({ workspaceId, projectId, query, rerunMutation, selectio
204
205
  }
205
206
  });
206
207
  }
208
+ const fatalError = query.isError && runData === undefined;
209
+ const tabState = fatalError ? query.error instanceof ApiError && query.error.status === 404 ? /*#__PURE__*/ _jsx(WorkflowRunNotFound, {}) : /*#__PURE__*/ _jsx(QueryLoadError, {
210
+ query: query,
211
+ subject: "workflow run",
212
+ icon: "pulseLine"
213
+ }) : query.isPending || runData === undefined ? /*#__PURE__*/ _jsx(WorkflowRunContentSkeleton, {}) : null;
207
214
  return /*#__PURE__*/ _jsxs(_Fragment, {
208
215
  children: [
209
- /*#__PURE__*/ _jsxs("div", {
210
- className: "flex min-w-0 flex-1 flex-col",
216
+ /*#__PURE__*/ _jsxs(Tabs, {
217
+ value: activeTab,
218
+ onValueChange: handleTabChange,
219
+ className: "flex min-w-0 flex-1 flex-col gap-0",
211
220
  children: [
212
- /*#__PURE__*/ _jsx(WorkflowRunSummary, {
213
- workspaceId: workspaceId,
214
- projectId: projectId,
221
+ runData ? /*#__PURE__*/ _jsx(WorkflowRunSummary, {
222
+ workspaceSlug: workspaceSlug,
223
+ projectSlug: projectSlug,
215
224
  run: runData,
216
- sourceAvailable: sourceAvailable,
217
- sourceOpen: sourcePanelOpen && sourceFocus === null,
218
- sourcePanelId: sourcePanelId,
219
- sourceButtonRef: sourceButtonRef,
220
- onSourceToggle: toggleWholeWorkflowSource,
221
225
  cancelling: cancelMutation.isPending,
222
226
  onCancel: cancelRun,
223
227
  rerunPending: rerunMutation.isPending,
224
228
  onRerun: (mode)=>void rerun(mode),
225
229
  latestAttempt: runData.latestAttempt
226
- }),
227
- query.isError ? /*#__PURE__*/ _jsx(WorkflowRunStaleError, {
230
+ }) : /*#__PURE__*/ _jsx(WorkflowRunSkeleton, {}),
231
+ runData && query.isError ? /*#__PURE__*/ _jsx(WorkflowRunStaleError, {
228
232
  query: query
229
233
  }) : null,
230
- /*#__PURE__*/ _jsx("div", {
231
- className: "min-h-0 flex-1 overflow-auto bg-background-neutral-base p-16",
232
- children: /*#__PURE__*/ _jsxs("div", {
233
- className: "mx-auto flex w-full max-w-[1280px] flex-col gap-16",
234
- children: [
235
- /*#__PURE__*/ _jsx(JobGraph, {
234
+ /*#__PURE__*/ _jsx(RunTabStrip, {
235
+ jobCount: runData?.jobs.length,
236
+ jobsFailed: runData?.jobs.filter((job)=>job.status === 'failed').length,
237
+ annotationSummary: annotationSummary
238
+ }),
239
+ /*#__PURE__*/ _jsxs(TabsContents, {
240
+ className: "min-h-0 flex-1 overflow-auto bg-background-neutral-background pb-24 pt-16",
241
+ children: [
242
+ /*#__PURE__*/ _jsx(TabsContent, {
243
+ value: "summary",
244
+ tabIndex: -1,
245
+ className: "mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24 outline-none",
246
+ children: runData ? /*#__PURE__*/ _jsx(JobGraph, {
236
247
  run: runData,
237
248
  selectedJobId: selectedJob?.id,
238
249
  onSelectedJobChange: selectJob
239
- }),
240
- selectedJob ? /*#__PURE__*/ _jsx(JobCard, {
241
- workspaceId: workspaceId,
242
- job: selectedJob,
250
+ }) : tabState
251
+ }),
252
+ /*#__PURE__*/ _jsx(TabsContent, {
253
+ value: "jobs",
254
+ tabIndex: -1,
255
+ className: "mx-auto w-full max-w-[1120px] px-24 outline-none",
256
+ children: runData ? /*#__PURE__*/ _jsx(RunJobsList, {
257
+ jobs: runData.jobs,
258
+ selectedJobId: selectedJob?.id,
259
+ onSelectedJobChange: selectJob,
260
+ workspaceSlug: workspaceSlug,
243
261
  selectedJobExecution: selectedJobExecution,
244
- selectedAttemptId: selectedJob.carriedOver ? undefined : selectedAttemptId,
262
+ selectedAttemptId: selectedAttemptId,
245
263
  onSelectedJobExecutionChange: selectJobExecution,
246
264
  onSelectedAttemptChange: selectionControlled ? selectAttempt : undefined,
247
265
  sourcePanelId: sourcePanelId,
248
266
  sourceAvailable: sourceAvailable,
249
267
  focusedSourceStepId: sourceFocus?.stepId ?? null,
250
268
  onOpenStepSource: openStepSource
251
- }) : null
252
- ]
253
- })
269
+ }) : tabState
270
+ }),
271
+ /*#__PURE__*/ _jsx(TabsContent, {
272
+ value: "annotations",
273
+ tabIndex: -1,
274
+ className: "mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24 outline-none",
275
+ children: runData ? /*#__PURE__*/ _jsxs(_Fragment, {
276
+ children: [
277
+ /*#__PURE__*/ _jsx(RunAnnotationSummaryLine, {
278
+ summary: annotationSummary,
279
+ workspaceSlug: workspaceSlug,
280
+ projectSlug: projectSlug,
281
+ workflowRunId: runData.id,
282
+ search: selection
283
+ }),
284
+ /*#__PURE__*/ _jsx(RunAnnotationsEmpty, {})
285
+ ]
286
+ }) : tabState
287
+ }),
288
+ /*#__PURE__*/ _jsx(TabsContent, {
289
+ value: "source",
290
+ keepMounted: sourceSnapshot !== null,
291
+ tabIndex: -1,
292
+ className: "mx-auto flex min-h-full w-full max-w-[1120px] px-24 outline-none",
293
+ children: runData ? sourceSnapshot ? /*#__PURE__*/ _jsx(WorkflowSourceContent, {
294
+ source: sourceSnapshot,
295
+ highlightedLineRange: highlightedLineRange,
296
+ scrollHighlightedIntoView: true
297
+ }) : /*#__PURE__*/ _jsx(EmptyState, {
298
+ className: "min-h-160 w-full",
299
+ icon: "fileDamageLine",
300
+ title: "Source snapshot unavailable",
301
+ description: runData.isTemporary ? 'Temporary runs do not capture workflow source.' : 'This run was created before workflow source snapshots were captured.'
302
+ }) : tabState
303
+ })
304
+ ]
254
305
  })
255
306
  ]
256
307
  }),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/workflow-run-view/workflow-run-view.tsx"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {RelativeTimeProvider} from '@shipfox/react-ui/relative-time';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {useNavigate} from '@tanstack/react-router';\nimport {useEffect, useId, useRef, useState} from 'react';\nimport {\n type JobExecution,\n resolveJobExecution,\n type StepSourceLocation,\n type WorkflowRunRerunMode,\n} from '#core/workflow-run.js';\nimport {\n type WorkflowRunSelectionInput,\n withoutWorkflowRunSelectionSearch,\n} from '#core/workflow-run-url-state.js';\nimport {\n useCancelWorkflowRunMutation,\n useRerunWorkflowRunMutation,\n useWorkflowRunAttemptQuery,\n} from '#hooks/api/workflow-runs.js';\nimport {JobGraph} from '../job-graph/index.js';\nimport {WorkflowRunSummary} from '../workflow-run-summary/index.js';\nimport {WorkflowSourcePanel} from '../workflow-source-panel/index.js';\nimport {JobCard} from './job-card.js';\nimport {resolveWorkflowRunSelection} from './workflow-run-selection.js';\nimport {\n WorkflowRunNotFound,\n WorkflowRunSkeleton,\n WorkflowRunStaleError,\n} from './workflow-run-states.js';\n\ninterface WorkflowSourceFocus {\n stepId: string;\n location: StepSourceLocation;\n}\n\nexport interface WorkflowRunViewProps {\n workspaceId: string;\n projectId: string;\n workflowRunId?: string | undefined;\n selection?: WorkflowRunSelectionInput | undefined;\n onSelectionChange?: ((selection: WorkflowRunSelectionInput) => void) | undefined;\n}\n\n/**\n * Renders the run for `workflowRunId`, or a skeleton while `workflowRunId` is still unknown (the page is\n * resolving which run to show) or the run is loading, so the page never branches on the\n * loading state itself.\n */\nexport function WorkflowRunView({\n workspaceId,\n projectId,\n workflowRunId,\n selection,\n onSelectionChange,\n}: WorkflowRunViewProps) {\n const runQuery = useWorkflowRunAttemptQuery({workflowRunId, runAttempt: selection?.runAttempt});\n const rerunMutation = useRerunWorkflowRunMutation(projectId);\n\n return (\n <RelativeTimeProvider>\n <div className=\"flex min-h-0 min-w-0 flex-1 overflow-hidden\">\n <RunViewContent\n workspaceId={workspaceId}\n projectId={projectId}\n query={runQuery}\n rerunMutation={rerunMutation}\n selection={selection}\n onSelectionChange={onSelectionChange}\n />\n </div>\n </RelativeTimeProvider>\n );\n}\n\nfunction RunViewContent({\n workspaceId,\n projectId,\n query,\n rerunMutation,\n selection,\n onSelectionChange,\n}: {\n workspaceId: string;\n projectId: string;\n query: ReturnType<typeof useWorkflowRunAttemptQuery>;\n rerunMutation: ReturnType<typeof useRerunWorkflowRunMutation>;\n selection: WorkflowRunSelectionInput | undefined;\n onSelectionChange: ((selection: WorkflowRunSelectionInput) => void) | undefined;\n}) {\n const navigate = useNavigate();\n const [selectedJobId, setSelectedJobId] = useState<string | undefined>();\n const [selectedJobExecutionId, setSelectedJobExecutionId] = useState<string | undefined>();\n const [sourcePanelOpen, setSourcePanelOpen] = useState(false);\n const [sourceFocus, setSourceFocus] = useState<WorkflowSourceFocus | null>(null);\n const sourcePanelId = useId();\n const sourceButtonRef = useRef<HTMLButtonElement>(null);\n // The button that last opened the panel (summary or a step detail), so Escape /\n // Close returns focus to whoever opened it.\n const lastSourceTriggerRef = useRef<HTMLButtonElement | null>(null);\n const selectionControlled = selection !== undefined;\n const sourceAvailable =\n query.data?.sourceSnapshot !== null && query.data?.sourceSnapshot !== undefined;\n const cancelMutation = useCancelWorkflowRunMutation(query.data);\n\n useEffect(() => {\n if (!sourceAvailable) {\n setSourcePanelOpen(false);\n setSourceFocus(null);\n }\n }, [sourceAvailable]);\n\n // If a refetch drops the focused step or its location, degrade to whole-workflow\n // focus so the panel never points at an unmounted Source button.\n useEffect(() => {\n if (!sourceFocus) return;\n const stillLocated = query.data?.jobs.some((job) =>\n job.jobExecutions.some((execution) =>\n execution.steps.some((step) => step.id === sourceFocus.stepId && step.sourceLocation),\n ),\n );\n if (!stillLocated) {\n setSourceFocus(null);\n lastSourceTriggerRef.current = sourceButtonRef.current;\n }\n }, [sourceFocus, query.data]);\n\n if (query.isPending) return <WorkflowRunSkeleton />;\n\n // Only show the full error placeholder when nothing ever loaded. A refetch that fails after\n // a prior success keeps the loaded run on screen (see below) instead of wiping the pane,\n // since active-run polling can hit a transient API error.\n if (query.isError && query.data === undefined) {\n if (query.error instanceof ApiError && query.error.status === 404) {\n return <WorkflowRunNotFound />;\n }\n return <QueryLoadError query={query} subject=\"workflow run\" icon=\"pulseLine\" />;\n }\n\n if (query.data === undefined) return <WorkflowRunSkeleton />;\n\n const runData = query.data;\n const resolvedSelection = selectionControlled\n ? resolveWorkflowRunSelection({run: runData, selection})\n : undefined;\n const selectedJob = selectionControlled\n ? resolvedSelection?.job\n : (runData.jobs.find((job) => job.id === selectedJobId) ?? runData.jobs.at(0));\n const selectedJobExecution = selectionControlled\n ? resolvedSelection?.jobExecution\n : selectedJob\n ? resolveJobExecution(selectedJob, selectedJobExecutionId)\n : undefined;\n const selectedAttemptId = selectionControlled\n ? (resolvedSelection?.selectedAttemptId ?? null)\n : undefined;\n // Explicit per-step focus wins; fall back to the URL-selected step so deep links\n // still pre-highlight when the summary opens the whole-workflow source.\n const highlightedLineRange =\n sourceFocus?.location ?? resolvedSelection?.step?.sourceLocation ?? null;\n const sourceSnapshot = runData.sourceSnapshot;\n async function rerun(mode: WorkflowRunRerunMode) {\n try {\n const run = await rerunMutation.mutateAsync({workflowRunId: runData.id, mode});\n toast.success('Re-run started');\n await navigate({\n to: '/workspaces/$wid/projects/$pid/runs/$workflowRunId',\n params: {wid: workspaceId, pid: projectId, workflowRunId: run.id},\n search: ((previous: Record<string, unknown>) =>\n withoutWorkflowRunSelectionSearch(previous)) as never,\n });\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : 'Could not start re-run');\n }\n }\n\n function selectJob(jobId: string | undefined) {\n if (!selectionControlled) {\n setSelectedJobId(jobId);\n setSelectedJobExecutionId(undefined);\n return;\n }\n\n onSelectionChange?.(\n jobId ? {jobId, runAttempt: selection?.runAttempt} : {runAttempt: selection?.runAttempt},\n );\n }\n\n function selectJobExecution(jobExecutionId: string | undefined) {\n if (!selectionControlled) {\n setSelectedJobExecutionId(jobExecutionId);\n return;\n }\n if (!selectedJob) return;\n\n onSelectionChange?.({\n jobId: selectedJob.id,\n jobExecutionId,\n runAttempt: selection?.runAttempt,\n });\n }\n\n function selectAttempt(attemptId: string | undefined) {\n if (!selectionControlled || !selectedJob || !selectedJobExecution) return;\n\n if (!attemptId) {\n onSelectionChange?.({\n jobId: selectedJob.id,\n jobExecutionId: selectedJobExecution.id,\n runAttempt: selection?.runAttempt,\n });\n return;\n }\n\n const match = findAttemptSelection(selectedJobExecution, attemptId);\n if (!match) return;\n\n onSelectionChange?.({\n jobId: selectedJob.id,\n jobExecutionId: selectedJobExecution.id,\n stepId: match.stepId,\n stepAttemptId: match.attemptId,\n runAttempt: selection?.runAttempt,\n });\n }\n\n function openWholeWorkflowSource() {\n setSourceFocus(null);\n lastSourceTriggerRef.current = sourceButtonRef.current;\n setSourcePanelOpen(true);\n }\n\n function toggleWholeWorkflowSource() {\n if (sourcePanelOpen && sourceFocus === null) {\n closeSourcePanel();\n return;\n }\n openWholeWorkflowSource();\n }\n\n function openStepSource(\n stepId: string,\n location: StepSourceLocation,\n trigger: HTMLButtonElement | null,\n ) {\n setSourceFocus({stepId, location});\n lastSourceTriggerRef.current = trigger;\n setSourcePanelOpen(true);\n }\n\n function closeSourcePanel() {\n const trigger = lastSourceTriggerRef.current;\n const fallbackTrigger = sourceButtonRef.current;\n setSourcePanelOpen(false);\n // Defer so focus lands after the panel unmounts; clear the focus only after\n // focusing so the opener button is still expanded (force-visible) on return.\n window.setTimeout(() => {\n const focusTarget = trigger?.isConnected ? trigger : fallbackTrigger;\n focusTarget?.focus();\n setSourceFocus(null);\n }, 0);\n }\n\n function cancelRun() {\n cancelMutation.mutate(undefined, {\n onError: (error) => {\n toast.error(cancelErrorMessage(error));\n },\n });\n }\n\n return (\n <>\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <WorkflowRunSummary\n workspaceId={workspaceId}\n projectId={projectId}\n run={runData}\n sourceAvailable={sourceAvailable}\n sourceOpen={sourcePanelOpen && sourceFocus === null}\n sourcePanelId={sourcePanelId}\n sourceButtonRef={sourceButtonRef}\n onSourceToggle={toggleWholeWorkflowSource}\n cancelling={cancelMutation.isPending}\n onCancel={cancelRun}\n rerunPending={rerunMutation.isPending}\n onRerun={(mode) => void rerun(mode)}\n latestAttempt={runData.latestAttempt}\n />\n {query.isError ? <WorkflowRunStaleError query={query} /> : null}\n <div className=\"min-h-0 flex-1 overflow-auto bg-background-neutral-base p-16\">\n <div className=\"mx-auto flex w-full max-w-[1280px] flex-col gap-16\">\n <JobGraph\n run={runData}\n selectedJobId={selectedJob?.id}\n onSelectedJobChange={selectJob}\n />\n {selectedJob ? (\n <JobCard\n workspaceId={workspaceId}\n job={selectedJob}\n selectedJobExecution={selectedJobExecution}\n selectedAttemptId={selectedJob.carriedOver ? undefined : selectedAttemptId}\n onSelectedJobExecutionChange={selectJobExecution}\n onSelectedAttemptChange={selectionControlled ? selectAttempt : undefined}\n sourcePanelId={sourcePanelId}\n sourceAvailable={sourceAvailable}\n focusedSourceStepId={sourceFocus?.stepId ?? null}\n onOpenStepSource={openStepSource}\n />\n ) : null}\n </div>\n </div>\n </div>\n <WorkflowSourcePanel\n id={sourcePanelId}\n source={sourceSnapshot}\n open={sourcePanelOpen && sourceAvailable}\n onClose={closeSourcePanel}\n highlightedLineRange={highlightedLineRange}\n scrollHighlightedIntoView\n />\n </>\n );\n}\n\nfunction cancelErrorMessage(error: unknown): string {\n if (error instanceof ApiError && error.code === 'run-already-finished') {\n return 'This workflow run has already finished.';\n }\n return 'Could not cancel workflow run.';\n}\n\nfunction findAttemptSelection(jobExecution: JobExecution, attemptId: string) {\n for (const step of jobExecution.steps) {\n const attempt = step.attempts.find((candidate) => candidate.id === attemptId);\n if (attempt) return {stepId: step.id, attemptId: attempt.id};\n }\n return undefined;\n}\n"],"names":["ApiError","QueryLoadError","RelativeTimeProvider","toast","useNavigate","useEffect","useId","useRef","useState","resolveJobExecution","withoutWorkflowRunSelectionSearch","useCancelWorkflowRunMutation","useRerunWorkflowRunMutation","useWorkflowRunAttemptQuery","JobGraph","WorkflowRunSummary","WorkflowSourcePanel","JobCard","resolveWorkflowRunSelection","WorkflowRunNotFound","WorkflowRunSkeleton","WorkflowRunStaleError","WorkflowRunView","workspaceId","projectId","workflowRunId","selection","onSelectionChange","runQuery","runAttempt","rerunMutation","div","className","RunViewContent","query","navigate","selectedJobId","setSelectedJobId","selectedJobExecutionId","setSelectedJobExecutionId","sourcePanelOpen","setSourcePanelOpen","sourceFocus","setSourceFocus","sourcePanelId","sourceButtonRef","lastSourceTriggerRef","selectionControlled","undefined","sourceAvailable","data","sourceSnapshot","cancelMutation","stillLocated","jobs","some","job","jobExecutions","execution","steps","step","id","stepId","sourceLocation","current","isPending","isError","error","status","subject","icon","runData","resolvedSelection","run","selectedJob","find","at","selectedJobExecution","jobExecution","selectedAttemptId","highlightedLineRange","location","rerun","mode","mutateAsync","success","to","params","wid","pid","search","previous","message","selectJob","jobId","selectJobExecution","jobExecutionId","selectAttempt","attemptId","match","findAttemptSelection","stepAttemptId","openWholeWorkflowSource","toggleWholeWorkflowSource","closeSourcePanel","openStepSource","trigger","fallbackTrigger","window","setTimeout","focusTarget","isConnected","focus","cancelRun","mutate","onError","cancelErrorMessage","sourceOpen","onSourceToggle","cancelling","onCancel","rerunPending","onRerun","latestAttempt","onSelectedJobChange","carriedOver","onSelectedJobExecutionChange","onSelectedAttemptChange","focusedSourceStepId","onOpenStepSource","source","open","onClose","scrollHighlightedIntoView","code","attempt","attempts","candidate"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/workflow-run-view/workflow-run-view.tsx"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {RelativeTimeProvider} from '@shipfox/react-ui/relative-time';\nimport {Tabs, TabsContent, TabsContents} from '@shipfox/react-ui/tabs';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {useNavigate} from '@tanstack/react-router';\nimport {useEffect, useId, useRef, useState} from 'react';\nimport {\n type JobExecution,\n resolveJobExecution,\n type StepSourceLocation,\n type WorkflowRunRerunMode,\n} from '#core/workflow-run.js';\nimport type {RunAnnotationSummary} from '#core/workflow-run-tabs.js';\nimport {\n type WorkflowRunSelectionInput,\n withoutWorkflowRunSelectionSearch,\n} from '#core/workflow-run-url-state.js';\nimport {\n useCancelWorkflowRunMutation,\n useRerunWorkflowRunMutation,\n useWorkflowRunAttemptQuery,\n} from '#hooks/api/workflow-runs.js';\nimport {WORKFLOW_RUN_TABS, type WorkflowRunTab} from '#routes/inputs.js';\nimport {JobGraph} from '../job-graph/index.js';\nimport type {JobGraphSelectionSource} from '../job-graph/types.js';\nimport {WorkflowRunSummary} from '../workflow-run-summary/index.js';\nimport {\n RunAnnotationSummaryLine,\n RunAnnotationsEmpty,\n RunJobsList,\n RunTabStrip,\n} from '../workflow-run-tabs/index.js';\nimport {WorkflowSourceContent, WorkflowSourcePanel} from '../workflow-source-panel/index.js';\nimport {resolveWorkflowRunSelection} from './workflow-run-selection.js';\nimport {\n WorkflowRunContentSkeleton,\n WorkflowRunNotFound,\n WorkflowRunSkeleton,\n WorkflowRunStaleError,\n} from './workflow-run-states.js';\n\ninterface WorkflowSourceFocus {\n stepId: string;\n location: StepSourceLocation;\n}\n\nexport interface WorkflowRunViewProps {\n projectId: string;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n workflowRunId?: string | undefined;\n selection?: WorkflowRunSelectionInput | undefined;\n onSelectionChange?: ((selection: WorkflowRunSelectionInput) => void) | undefined;\n tab?: WorkflowRunTab | undefined;\n onTabChange?: ((tab: WorkflowRunTab) => void) | undefined;\n annotationSummary?: RunAnnotationSummary | undefined;\n}\n\n/**\n * Renders the run for `workflowRunId`, keeping the local tab strip mounted while the run\n * request is pending so polling never inserts navigation under the user's cursor.\n */\nexport function WorkflowRunView({\n projectId,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n selection,\n onSelectionChange,\n tab,\n onTabChange,\n annotationSummary,\n}: WorkflowRunViewProps) {\n const runQuery = useWorkflowRunAttemptQuery({workflowRunId, runAttempt: selection?.runAttempt});\n const rerunMutation = useRerunWorkflowRunMutation(projectId);\n\n return (\n <RelativeTimeProvider>\n <div className=\"flex min-h-0 min-w-0 flex-1 overflow-hidden\">\n <RunViewContent\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n query={runQuery}\n rerunMutation={rerunMutation}\n selection={selection}\n onSelectionChange={onSelectionChange}\n tab={tab}\n onTabChange={onTabChange}\n annotationSummary={annotationSummary}\n />\n </div>\n </RelativeTimeProvider>\n );\n}\n\nfunction RunViewContent({\n workspaceSlug,\n projectSlug,\n query,\n rerunMutation,\n selection,\n onSelectionChange,\n tab,\n onTabChange,\n annotationSummary,\n}: {\n workspaceSlug: string | undefined;\n projectSlug: string | undefined;\n query: ReturnType<typeof useWorkflowRunAttemptQuery>;\n rerunMutation: ReturnType<typeof useRerunWorkflowRunMutation>;\n selection: WorkflowRunSelectionInput | undefined;\n onSelectionChange: ((selection: WorkflowRunSelectionInput) => void) | undefined;\n tab: WorkflowRunTab | undefined;\n onTabChange: ((tab: WorkflowRunTab) => void) | undefined;\n annotationSummary: RunAnnotationSummary | undefined;\n}) {\n const navigate = useNavigate();\n const [selectedJobId, setSelectedJobId] = useState<string | undefined>();\n const [selectedJobExecutionId, setSelectedJobExecutionId] = useState<string | undefined>();\n const [localTab, setLocalTab] = useState<WorkflowRunTab>(tab ?? 'summary');\n const [sourcePanelOpen, setSourcePanelOpen] = useState(false);\n const [sourceFocus, setSourceFocus] = useState<WorkflowSourceFocus | null>(null);\n const sourcePanelId = useId();\n // The step Source button that last opened the panel, so Escape / Close returns focus to it.\n const lastSourceTriggerRef = useRef<HTMLButtonElement | null>(null);\n const selectionControlled = selection !== undefined;\n const tabControlled = onTabChange !== undefined;\n const activeTab = tabControlled ? (tab ?? 'summary') : localTab;\n const sourceAvailable =\n query.data?.sourceSnapshot !== null && query.data?.sourceSnapshot !== undefined;\n const cancelMutation = useCancelWorkflowRunMutation(query.data);\n\n useEffect(() => {\n if (!sourceAvailable) {\n setSourcePanelOpen(false);\n setSourceFocus(null);\n }\n }, [sourceAvailable]);\n\n // If a refetch drops the focused step or its location, close the panel so it never points at\n // an unmounted Source button.\n useEffect(() => {\n if (!sourceFocus) return;\n const stillLocated = query.data?.jobs.some((job) =>\n job.jobExecutions.some((execution) =>\n execution.steps.some((step) => step.id === sourceFocus.stepId && step.sourceLocation),\n ),\n );\n if (!stillLocated) {\n setSourceFocus(null);\n setSourcePanelOpen(false);\n }\n }, [sourceFocus, query.data]);\n\n const runData = query.data;\n const resolvedSelection =\n selectionControlled && runData\n ? resolveWorkflowRunSelection({\n run: runData,\n selection: selection as WorkflowRunSelectionInput,\n })\n : undefined;\n const hasExplicitJobSelection = Boolean(\n selection?.jobId || selection?.jobExecutionId || selection?.stepId || selection?.stepAttemptId,\n );\n const selectedJob = selectionControlled\n ? hasExplicitJobSelection\n ? resolvedSelection?.job\n : undefined\n : (runData?.jobs.find((job) => job.id === selectedJobId) ?? runData?.jobs.at(0));\n const selectedJobExecution = selectionControlled\n ? resolvedSelection?.jobExecution\n : selectedJob\n ? resolveJobExecution(selectedJob, selectedJobExecutionId)\n : undefined;\n const selectedAttemptId = selectionControlled\n ? (resolvedSelection?.selectedAttemptId ?? null)\n : undefined;\n // Explicit per-step focus wins; fall back to the URL-selected step so deep links still\n // pre-highlight source in the Source tab.\n const highlightedLineRange =\n sourceFocus?.location ?? resolvedSelection?.step?.sourceLocation ?? null;\n const sourceSnapshot = runData?.sourceSnapshot ?? null;\n\n function changeTab(nextTab: WorkflowRunTab) {\n if (!tabControlled) {\n setLocalTab(nextTab);\n } else if (activeTab !== nextTab) {\n onTabChange?.(nextTab);\n }\n\n if (nextTab !== 'jobs') {\n setSourcePanelOpen(false);\n setSourceFocus(null);\n }\n }\n\n function handleTabChange(nextTab: string) {\n if (!WORKFLOW_RUN_TABS.some((value) => value === nextTab)) return;\n changeTab(nextTab as WorkflowRunTab);\n }\n\n async function rerun(mode: WorkflowRunRerunMode) {\n if (!runData || !workspaceSlug || !projectSlug) {\n toast.error('Could not start re-run from this route.');\n return;\n }\n try {\n const run = await rerunMutation.mutateAsync({workflowRunId: runData.id, mode});\n toast.success('Re-run started');\n await navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',\n params: {\n workspaceSlug,\n projectSlug,\n workflowRunId: run.id,\n },\n search: ((previous: Record<string, unknown>) =>\n withoutWorkflowRunSelectionSearch(previous)) as never,\n });\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : 'Could not start re-run');\n }\n }\n\n function selectJob(jobId: string | undefined, source: JobGraphSelectionSource = 'pointer') {\n if (source === 'pointer') changeTab('jobs');\n if (!selectionControlled) {\n setSelectedJobId(jobId);\n setSelectedJobExecutionId(undefined);\n return;\n }\n // Keyboard roving focus is an interaction within the Summary graph. Keep that panel mounted\n // and let JobGraphView own the visual selection so focus is not moved to a different tab.\n if (source === 'keyboard') return;\n\n onSelectionChange?.(\n jobId ? {jobId, runAttempt: selection?.runAttempt} : {runAttempt: selection?.runAttempt},\n );\n }\n\n function selectJobExecution(jobExecutionId: string | undefined) {\n if (!selectionControlled) {\n setSelectedJobExecutionId(jobExecutionId);\n return;\n }\n if (!selectedJob) return;\n\n onSelectionChange?.({\n jobId: selectedJob.id,\n jobExecutionId,\n runAttempt: selection?.runAttempt,\n });\n }\n\n function selectAttempt(attemptId: string | undefined) {\n if (!selectionControlled || !selectedJob || !selectedJobExecution) return;\n\n if (!attemptId) {\n onSelectionChange?.({\n jobId: selectedJob.id,\n jobExecutionId: selectedJobExecution.id,\n runAttempt: selection?.runAttempt,\n });\n return;\n }\n\n const match = findAttemptSelection(selectedJobExecution, attemptId);\n if (!match) return;\n\n onSelectionChange?.({\n jobId: selectedJob.id,\n jobExecutionId: selectedJobExecution.id,\n stepId: match.stepId,\n stepAttemptId: match.attemptId,\n runAttempt: selection?.runAttempt,\n });\n }\n\n function openStepSource(\n stepId: string,\n location: StepSourceLocation,\n trigger: HTMLButtonElement | null,\n ) {\n setSourceFocus({stepId, location});\n lastSourceTriggerRef.current = trigger;\n setSourcePanelOpen(true);\n }\n\n function closeSourcePanel() {\n const trigger = lastSourceTriggerRef.current;\n setSourcePanelOpen(false);\n // Defer so focus lands after the panel unmounts; clear the focus only after focusing so the\n // opener button is still expanded (force-visible) on return.\n window.setTimeout(() => {\n if (trigger?.isConnected) trigger.focus();\n setSourceFocus(null);\n }, 0);\n }\n\n function cancelRun() {\n cancelMutation.mutate(undefined, {\n onError: (error) => {\n toast.error(cancelErrorMessage(error));\n },\n });\n }\n\n const fatalError = query.isError && runData === undefined;\n const tabState = fatalError ? (\n query.error instanceof ApiError && query.error.status === 404 ? (\n <WorkflowRunNotFound />\n ) : (\n <QueryLoadError query={query} subject=\"workflow run\" icon=\"pulseLine\" />\n )\n ) : query.isPending || runData === undefined ? (\n <WorkflowRunContentSkeleton />\n ) : null;\n\n return (\n <>\n <Tabs\n value={activeTab}\n onValueChange={handleTabChange}\n className=\"flex min-w-0 flex-1 flex-col gap-0\"\n >\n {runData ? (\n <WorkflowRunSummary\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n run={runData}\n cancelling={cancelMutation.isPending}\n onCancel={cancelRun}\n rerunPending={rerunMutation.isPending}\n onRerun={(mode) => void rerun(mode)}\n latestAttempt={runData.latestAttempt}\n />\n ) : (\n <WorkflowRunSkeleton />\n )}\n {runData && query.isError ? <WorkflowRunStaleError query={query} /> : null}\n <RunTabStrip\n jobCount={runData?.jobs.length}\n jobsFailed={runData?.jobs.filter((job) => job.status === 'failed').length}\n annotationSummary={annotationSummary}\n />\n <TabsContents className=\"min-h-0 flex-1 overflow-auto bg-background-neutral-background pb-24 pt-16\">\n <TabsContent\n value=\"summary\"\n tabIndex={-1}\n className=\"mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24 outline-none\"\n >\n {runData ? (\n <JobGraph\n run={runData}\n selectedJobId={selectedJob?.id}\n onSelectedJobChange={selectJob}\n />\n ) : (\n tabState\n )}\n </TabsContent>\n <TabsContent\n value=\"jobs\"\n tabIndex={-1}\n className=\"mx-auto w-full max-w-[1120px] px-24 outline-none\"\n >\n {runData ? (\n <RunJobsList\n jobs={runData.jobs}\n selectedJobId={selectedJob?.id}\n onSelectedJobChange={selectJob}\n workspaceSlug={workspaceSlug}\n selectedJobExecution={selectedJobExecution}\n selectedAttemptId={selectedAttemptId}\n onSelectedJobExecutionChange={selectJobExecution}\n onSelectedAttemptChange={selectionControlled ? selectAttempt : undefined}\n sourcePanelId={sourcePanelId}\n sourceAvailable={sourceAvailable}\n focusedSourceStepId={sourceFocus?.stepId ?? null}\n onOpenStepSource={openStepSource}\n />\n ) : (\n tabState\n )}\n </TabsContent>\n <TabsContent\n value=\"annotations\"\n tabIndex={-1}\n className=\"mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24 outline-none\"\n >\n {runData ? (\n <>\n <RunAnnotationSummaryLine\n summary={annotationSummary}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={runData.id}\n search={selection}\n />\n <RunAnnotationsEmpty />\n </>\n ) : (\n tabState\n )}\n </TabsContent>\n <TabsContent\n value=\"source\"\n keepMounted={sourceSnapshot !== null}\n tabIndex={-1}\n className=\"mx-auto flex min-h-full w-full max-w-[1120px] px-24 outline-none\"\n >\n {runData ? (\n sourceSnapshot ? (\n <WorkflowSourceContent\n source={sourceSnapshot}\n highlightedLineRange={highlightedLineRange}\n scrollHighlightedIntoView\n />\n ) : (\n <EmptyState\n className=\"min-h-160 w-full\"\n icon=\"fileDamageLine\"\n title=\"Source snapshot unavailable\"\n description={\n runData.isTemporary\n ? 'Temporary runs do not capture workflow source.'\n : 'This run was created before workflow source snapshots were captured.'\n }\n />\n )\n ) : (\n tabState\n )}\n </TabsContent>\n </TabsContents>\n </Tabs>\n <WorkflowSourcePanel\n id={sourcePanelId}\n source={sourceSnapshot}\n open={sourcePanelOpen && sourceAvailable}\n onClose={closeSourcePanel}\n highlightedLineRange={highlightedLineRange}\n scrollHighlightedIntoView\n />\n </>\n );\n}\n\nfunction cancelErrorMessage(error: unknown): string {\n if (error instanceof ApiError && error.code === 'run-already-finished') {\n return 'This workflow run has already finished.';\n }\n return 'Could not cancel workflow run.';\n}\n\nfunction findAttemptSelection(jobExecution: JobExecution, attemptId: string) {\n for (const step of jobExecution.steps) {\n const attempt = step.attempts.find((candidate) => candidate.id === attemptId);\n if (attempt) return {stepId: step.id, attemptId: attempt.id};\n }\n return undefined;\n}\n"],"names":["ApiError","QueryLoadError","EmptyState","RelativeTimeProvider","Tabs","TabsContent","TabsContents","toast","useNavigate","useEffect","useId","useRef","useState","resolveJobExecution","withoutWorkflowRunSelectionSearch","useCancelWorkflowRunMutation","useRerunWorkflowRunMutation","useWorkflowRunAttemptQuery","WORKFLOW_RUN_TABS","JobGraph","WorkflowRunSummary","RunAnnotationSummaryLine","RunAnnotationsEmpty","RunJobsList","RunTabStrip","WorkflowSourceContent","WorkflowSourcePanel","resolveWorkflowRunSelection","WorkflowRunContentSkeleton","WorkflowRunNotFound","WorkflowRunSkeleton","WorkflowRunStaleError","WorkflowRunView","projectId","workspaceSlug","projectSlug","workflowRunId","selection","onSelectionChange","tab","onTabChange","annotationSummary","runQuery","runAttempt","rerunMutation","div","className","RunViewContent","query","navigate","selectedJobId","setSelectedJobId","selectedJobExecutionId","setSelectedJobExecutionId","localTab","setLocalTab","sourcePanelOpen","setSourcePanelOpen","sourceFocus","setSourceFocus","sourcePanelId","lastSourceTriggerRef","selectionControlled","undefined","tabControlled","activeTab","sourceAvailable","data","sourceSnapshot","cancelMutation","stillLocated","jobs","some","job","jobExecutions","execution","steps","step","id","stepId","sourceLocation","runData","resolvedSelection","run","hasExplicitJobSelection","Boolean","jobId","jobExecutionId","stepAttemptId","selectedJob","find","at","selectedJobExecution","jobExecution","selectedAttemptId","highlightedLineRange","location","changeTab","nextTab","handleTabChange","value","rerun","mode","error","mutateAsync","success","to","params","search","previous","message","selectJob","source","selectJobExecution","selectAttempt","attemptId","match","findAttemptSelection","openStepSource","trigger","current","closeSourcePanel","window","setTimeout","isConnected","focus","cancelRun","mutate","onError","cancelErrorMessage","fatalError","isError","tabState","status","subject","icon","isPending","onValueChange","cancelling","onCancel","rerunPending","onRerun","latestAttempt","jobCount","length","jobsFailed","filter","tabIndex","onSelectedJobChange","onSelectedJobExecutionChange","onSelectedAttemptChange","focusedSourceStepId","onOpenStepSource","summary","keepMounted","scrollHighlightedIntoView","title","description","isTemporary","open","onClose","code","attempt","attempts","candidate"],"mappings":"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"}
@@ -1,2 +1,3 @@
1
+ export * from './workflow-source-content.js';
1
2
  export * from './workflow-source-panel.js';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-source-panel/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-source-panel/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC"}
@@ -1,3 +1,4 @@
1
+ export * from './workflow-source-content.js';
1
2
  export * from './workflow-source-panel.js';
2
3
 
3
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/workflow-source-panel/index.ts"],"sourcesContent":["export * from './workflow-source-panel.js';\n"],"names":[],"mappings":"AAAA,cAAc,6BAA6B"}
1
+ {"version":3,"sources":["../../../src/components/workflow-source-panel/index.ts"],"sourcesContent":["export * from './workflow-source-content.js';\nexport * from './workflow-source-panel.js';\n"],"names":[],"mappings":"AAAA,cAAc,+BAA+B;AAC7C,cAAc,6BAA6B"}
@@ -0,0 +1,12 @@
1
+ import { type CodeBlockHighlightedLineRange } from '@shipfox/react-ui/code-block';
2
+ import type { ReactNode } from 'react';
3
+ import type { WorkflowSourceSnapshot } from '#core/workflow-run.js';
4
+ export interface WorkflowSourceContentProps {
5
+ source: WorkflowSourceSnapshot;
6
+ highlightedLineRange?: CodeBlockHighlightedLineRange | null | undefined;
7
+ scrollHighlightedIntoView?: boolean | undefined;
8
+ headerAction?: ReactNode;
9
+ className?: string | undefined;
10
+ }
11
+ export declare function WorkflowSourceContent({ source, highlightedLineRange, scrollHighlightedIntoView, headerAction, className, }: WorkflowSourceContentProps): import("react").JSX.Element;
12
+ //# sourceMappingURL=workflow-source-content.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-source-content.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-source-panel/workflow-source-content.tsx"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,6BAA6B,EAEnC,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AACrC,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,uBAAuB,CAAC;AAQlE,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,sBAAsB,CAAC;IAC/B,oBAAoB,CAAC,EAAE,6BAA6B,GAAG,IAAI,GAAG,SAAS,CAAC;IACxE,yBAAyB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAChD,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,wBAAgB,qBAAqB,CAAC,EACpC,MAAM,EACN,oBAAoB,EACpB,yBAAyB,EACzB,YAAY,EACZ,SAAS,GACV,EAAE,0BAA0B,+BAgD5B"}
@@ -0,0 +1,53 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { CodeBlock, CodeBlockBody, CodeBlockContent, CodeBlockCopyButton, CodeBlockFilename, CodeBlockFiles, CodeBlockHeader, CodeBlockItem } from '@shipfox/react-ui/code-block';
3
+ import { cn } from '@shipfox/react-ui/utils';
4
+ const WORKFLOW_SOURCE_FILENAME = 'workflow.yaml';
5
+ const WORKFLOW_SOURCE_CODE_THEMES = {
6
+ light: 'vitesse-dark',
7
+ dark: 'vitesse-dark'
8
+ };
9
+ export function WorkflowSourceContent({ source, highlightedLineRange, scrollHighlightedIntoView, headerAction, className }) {
10
+ const data = [
11
+ {
12
+ language: 'yaml',
13
+ filename: WORKFLOW_SOURCE_FILENAME,
14
+ code: source.content
15
+ }
16
+ ];
17
+ return /*#__PURE__*/ _jsxs(CodeBlock, {
18
+ data: data,
19
+ className: cn('flex size-full flex-col rounded-none bg-background-contrast-base shadow-none', className),
20
+ children: [
21
+ /*#__PURE__*/ _jsxs(CodeBlockHeader, {
22
+ className: "shrink-0 border-b border-border-contrast-base bg-background-contrast-base",
23
+ children: [
24
+ /*#__PURE__*/ _jsx(CodeBlockFiles, {
25
+ children: (item)=>/*#__PURE__*/ _jsx(CodeBlockFilename, {
26
+ value: item.filename,
27
+ children: item.filename
28
+ })
29
+ }),
30
+ /*#__PURE__*/ _jsx(CodeBlockCopyButton, {}),
31
+ headerAction
32
+ ]
33
+ }),
34
+ /*#__PURE__*/ _jsx(CodeBlockBody, {
35
+ className: "flex min-h-0 flex-1 overflow-auto scrollbar",
36
+ children: (item)=>/*#__PURE__*/ _jsx(CodeBlockItem, {
37
+ value: item.filename,
38
+ className: cn('min-h-full px-0 pb-0', '[&>div]:rounded-none [&>div]:border-0 [&>div]:bg-background-contrast-base [&>div]:dark:bg-background-contrast-base', '[&_code]:!text-sm [&_code]:!text-foreground-neutral-on-inverted [&_.line]:!text-sm [&_.line]:before:!text-sm [&_.line]:before:!text-foreground-neutral-muted'),
39
+ children: /*#__PURE__*/ _jsx(CodeBlockContent, {
40
+ language: "yaml",
41
+ themes: WORKFLOW_SOURCE_CODE_THEMES,
42
+ syntaxHighlighting: true,
43
+ highlightedLineRange: highlightedLineRange,
44
+ scrollHighlightedIntoView: scrollHighlightedIntoView,
45
+ children: item.code
46
+ })
47
+ })
48
+ })
49
+ ]
50
+ });
51
+ }
52
+
53
+ //# sourceMappingURL=workflow-source-content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/workflow-source-panel/workflow-source-content.tsx"],"sourcesContent":["import {\n CodeBlock,\n CodeBlockBody,\n CodeBlockContent,\n CodeBlockCopyButton,\n CodeBlockFilename,\n CodeBlockFiles,\n CodeBlockHeader,\n type CodeBlockHighlightedLineRange,\n CodeBlockItem,\n} from '@shipfox/react-ui/code-block';\nimport {cn} from '@shipfox/react-ui/utils';\nimport type {ReactNode} from 'react';\nimport type {WorkflowSourceSnapshot} from '#core/workflow-run.js';\n\nconst WORKFLOW_SOURCE_FILENAME = 'workflow.yaml';\nconst WORKFLOW_SOURCE_CODE_THEMES = {\n light: 'vitesse-dark',\n dark: 'vitesse-dark',\n};\n\nexport interface WorkflowSourceContentProps {\n source: WorkflowSourceSnapshot;\n highlightedLineRange?: CodeBlockHighlightedLineRange | null | undefined;\n scrollHighlightedIntoView?: boolean | undefined;\n headerAction?: ReactNode;\n className?: string | undefined;\n}\n\nexport function WorkflowSourceContent({\n source,\n highlightedLineRange,\n scrollHighlightedIntoView,\n headerAction,\n className,\n}: WorkflowSourceContentProps) {\n const data = [\n {\n language: 'yaml',\n filename: WORKFLOW_SOURCE_FILENAME,\n code: source.content,\n },\n ];\n\n return (\n <CodeBlock\n data={data}\n className={cn(\n 'flex size-full flex-col rounded-none bg-background-contrast-base shadow-none',\n className,\n )}\n >\n <CodeBlockHeader className=\"shrink-0 border-b border-border-contrast-base bg-background-contrast-base\">\n <CodeBlockFiles>\n {(item) => <CodeBlockFilename value={item.filename}>{item.filename}</CodeBlockFilename>}\n </CodeBlockFiles>\n <CodeBlockCopyButton />\n {headerAction}\n </CodeBlockHeader>\n <CodeBlockBody className=\"flex min-h-0 flex-1 overflow-auto scrollbar\">\n {(item) => (\n <CodeBlockItem\n value={item.filename}\n className={cn(\n 'min-h-full px-0 pb-0',\n '[&>div]:rounded-none [&>div]:border-0 [&>div]:bg-background-contrast-base [&>div]:dark:bg-background-contrast-base',\n '[&_code]:!text-sm [&_code]:!text-foreground-neutral-on-inverted [&_.line]:!text-sm [&_.line]:before:!text-sm [&_.line]:before:!text-foreground-neutral-muted',\n )}\n >\n <CodeBlockContent\n language=\"yaml\"\n themes={WORKFLOW_SOURCE_CODE_THEMES}\n syntaxHighlighting\n highlightedLineRange={highlightedLineRange}\n scrollHighlightedIntoView={scrollHighlightedIntoView}\n >\n {item.code}\n </CodeBlockContent>\n </CodeBlockItem>\n )}\n </CodeBlockBody>\n </CodeBlock>\n );\n}\n"],"names":["CodeBlock","CodeBlockBody","CodeBlockContent","CodeBlockCopyButton","CodeBlockFilename","CodeBlockFiles","CodeBlockHeader","CodeBlockItem","cn","WORKFLOW_SOURCE_FILENAME","WORKFLOW_SOURCE_CODE_THEMES","light","dark","WorkflowSourceContent","source","highlightedLineRange","scrollHighlightedIntoView","headerAction","className","data","language","filename","code","content","item","value","themes","syntaxHighlighting"],"mappings":";AAAA,SACEA,SAAS,EACTC,aAAa,EACbC,gBAAgB,EAChBC,mBAAmB,EACnBC,iBAAiB,EACjBC,cAAc,EACdC,eAAe,EAEfC,aAAa,QACR,+BAA+B;AACtC,SAAQC,EAAE,QAAO,0BAA0B;AAI3C,MAAMC,2BAA2B;AACjC,MAAMC,8BAA8B;IAClCC,OAAO;IACPC,MAAM;AACR;AAUA,OAAO,SAASC,sBAAsB,EACpCC,MAAM,EACNC,oBAAoB,EACpBC,yBAAyB,EACzBC,YAAY,EACZC,SAAS,EACkB;IAC3B,MAAMC,OAAO;QACX;YACEC,UAAU;YACVC,UAAUZ;YACVa,MAAMR,OAAOS,OAAO;QACtB;KACD;IAED,qBACE,MAACvB;QACCmB,MAAMA;QACND,WAAWV,GACT,gFACAU;;0BAGF,MAACZ;gBAAgBY,WAAU;;kCACzB,KAACb;kCACE,CAACmB,qBAAS,KAACpB;gCAAkBqB,OAAOD,KAAKH,QAAQ;0CAAGG,KAAKH,QAAQ;;;kCAEpE,KAAClB;oBACAc;;;0BAEH,KAAChB;gBAAciB,WAAU;0BACtB,CAACM,qBACA,KAACjB;wBACCkB,OAAOD,KAAKH,QAAQ;wBACpBH,WAAWV,GACT,wBACA,sHACA;kCAGF,cAAA,KAACN;4BACCkB,UAAS;4BACTM,QAAQhB;4BACRiB,kBAAkB;4BAClBZ,sBAAsBA;4BACtBC,2BAA2BA;sCAE1BQ,KAAKF,IAAI;;;;;;AAOxB"}
@@ -1,4 +1,4 @@
1
- import { type CodeBlockHighlightedLineRange } from '@shipfox/react-ui/code-block';
1
+ import type { CodeBlockHighlightedLineRange } from '@shipfox/react-ui/code-block';
2
2
  import type { WorkflowSourceSnapshot } from '#core/workflow-run.js';
3
3
  export interface WorkflowSourcePanelProps {
4
4
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-source-panel.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-source-panel/workflow-source-panel.tsx"],"names":[],"mappings":"AACA,OAAO,EAQL,KAAK,6BAA6B,EAEnC,MAAM,8BAA8B,CAAC;AAGtC,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,uBAAuB,CAAC;AAQlE,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACtC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,oBAAoB,CAAC,EAAE,6BAA6B,GAAG,IAAI,GAAG,SAAS,CAAC;IACxE,yBAAyB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAChD,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,wBAAgB,mBAAmB,CAAC,EAClC,EAAE,EACF,MAAM,EACN,IAAI,EACJ,OAAO,EACP,oBAAoB,EACpB,yBAAyB,EACzB,SAAS,GACV,EAAE,wBAAwB,sCAgC1B"}
1
+ {"version":3,"file":"workflow-source-panel.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-source-panel/workflow-source-panel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,6BAA6B,EAAC,MAAM,8BAA8B,CAAC;AAGhF,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,uBAAuB,CAAC;AAGlE,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACtC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,oBAAoB,CAAC,EAAE,6BAA6B,GAAG,IAAI,GAAG,SAAS,CAAC;IACxE,yBAAyB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAChD,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,wBAAgB,mBAAmB,CAAC,EAClC,EAAE,EACF,MAAM,EACN,IAAI,EACJ,OAAO,EACP,oBAAoB,EACpB,yBAAyB,EACzB,SAAS,GACV,EAAE,wBAAwB,sCA2C1B"}