@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,6 +1,8 @@
1
1
  import {ApiError} from '@shipfox/client-api';
2
2
  import {QueryLoadError} from '@shipfox/client-ui';
3
+ import {EmptyState} from '@shipfox/react-ui/empty-state';
3
4
  import {RelativeTimeProvider} from '@shipfox/react-ui/relative-time';
5
+ import {Tabs, TabsContent, TabsContents} from '@shipfox/react-ui/tabs';
4
6
  import {toast} from '@shipfox/react-ui/toast';
5
7
  import {useNavigate} from '@tanstack/react-router';
6
8
  import {useEffect, useId, useRef, useState} from 'react';
@@ -10,6 +12,7 @@ import {
10
12
  type StepSourceLocation,
11
13
  type WorkflowRunRerunMode,
12
14
  } from '#core/workflow-run.js';
15
+ import type {RunAnnotationSummary} from '#core/workflow-run-tabs.js';
13
16
  import {
14
17
  type WorkflowRunSelectionInput,
15
18
  withoutWorkflowRunSelectionSearch,
@@ -19,12 +22,20 @@ import {
19
22
  useRerunWorkflowRunMutation,
20
23
  useWorkflowRunAttemptQuery,
21
24
  } from '#hooks/api/workflow-runs.js';
25
+ import {WORKFLOW_RUN_TABS, type WorkflowRunTab} from '#routes/inputs.js';
22
26
  import {JobGraph} from '../job-graph/index.js';
27
+ import type {JobGraphSelectionSource} from '../job-graph/types.js';
23
28
  import {WorkflowRunSummary} from '../workflow-run-summary/index.js';
24
- import {WorkflowSourcePanel} from '../workflow-source-panel/index.js';
25
- import {JobCard} from './job-card.js';
29
+ import {
30
+ RunAnnotationSummaryLine,
31
+ RunAnnotationsEmpty,
32
+ RunJobsList,
33
+ RunTabStrip,
34
+ } from '../workflow-run-tabs/index.js';
35
+ import {WorkflowSourceContent, WorkflowSourcePanel} from '../workflow-source-panel/index.js';
26
36
  import {resolveWorkflowRunSelection} from './workflow-run-selection.js';
27
37
  import {
38
+ WorkflowRunContentSkeleton,
28
39
  WorkflowRunNotFound,
29
40
  WorkflowRunSkeleton,
30
41
  WorkflowRunStaleError,
@@ -36,24 +47,31 @@ interface WorkflowSourceFocus {
36
47
  }
37
48
 
38
49
  export interface WorkflowRunViewProps {
39
- workspaceId: string;
40
50
  projectId: string;
51
+ workspaceSlug?: string | undefined;
52
+ projectSlug?: string | undefined;
41
53
  workflowRunId?: string | undefined;
42
54
  selection?: WorkflowRunSelectionInput | undefined;
43
55
  onSelectionChange?: ((selection: WorkflowRunSelectionInput) => void) | undefined;
56
+ tab?: WorkflowRunTab | undefined;
57
+ onTabChange?: ((tab: WorkflowRunTab) => void) | undefined;
58
+ annotationSummary?: RunAnnotationSummary | undefined;
44
59
  }
45
60
 
46
61
  /**
47
- * Renders the run for `workflowRunId`, or a skeleton while `workflowRunId` is still unknown (the page is
48
- * resolving which run to show) or the run is loading, so the page never branches on the
49
- * loading state itself.
62
+ * Renders the run for `workflowRunId`, keeping the local tab strip mounted while the run
63
+ * request is pending so polling never inserts navigation under the user's cursor.
50
64
  */
51
65
  export function WorkflowRunView({
52
- workspaceId,
53
66
  projectId,
67
+ workspaceSlug,
68
+ projectSlug,
54
69
  workflowRunId,
55
70
  selection,
56
71
  onSelectionChange,
72
+ tab,
73
+ onTabChange,
74
+ annotationSummary,
57
75
  }: WorkflowRunViewProps) {
58
76
  const runQuery = useWorkflowRunAttemptQuery({workflowRunId, runAttempt: selection?.runAttempt});
59
77
  const rerunMutation = useRerunWorkflowRunMutation(projectId);
@@ -62,12 +80,15 @@ export function WorkflowRunView({
62
80
  <RelativeTimeProvider>
63
81
  <div className="flex min-h-0 min-w-0 flex-1 overflow-hidden">
64
82
  <RunViewContent
65
- workspaceId={workspaceId}
66
- projectId={projectId}
83
+ workspaceSlug={workspaceSlug}
84
+ projectSlug={projectSlug}
67
85
  query={runQuery}
68
86
  rerunMutation={rerunMutation}
69
87
  selection={selection}
70
88
  onSelectionChange={onSelectionChange}
89
+ tab={tab}
90
+ onTabChange={onTabChange}
91
+ annotationSummary={annotationSummary}
71
92
  />
72
93
  </div>
73
94
  </RelativeTimeProvider>
@@ -75,31 +96,38 @@ export function WorkflowRunView({
75
96
  }
76
97
 
77
98
  function RunViewContent({
78
- workspaceId,
79
- projectId,
99
+ workspaceSlug,
100
+ projectSlug,
80
101
  query,
81
102
  rerunMutation,
82
103
  selection,
83
104
  onSelectionChange,
105
+ tab,
106
+ onTabChange,
107
+ annotationSummary,
84
108
  }: {
85
- workspaceId: string;
86
- projectId: string;
109
+ workspaceSlug: string | undefined;
110
+ projectSlug: string | undefined;
87
111
  query: ReturnType<typeof useWorkflowRunAttemptQuery>;
88
112
  rerunMutation: ReturnType<typeof useRerunWorkflowRunMutation>;
89
113
  selection: WorkflowRunSelectionInput | undefined;
90
114
  onSelectionChange: ((selection: WorkflowRunSelectionInput) => void) | undefined;
115
+ tab: WorkflowRunTab | undefined;
116
+ onTabChange: ((tab: WorkflowRunTab) => void) | undefined;
117
+ annotationSummary: RunAnnotationSummary | undefined;
91
118
  }) {
92
119
  const navigate = useNavigate();
93
120
  const [selectedJobId, setSelectedJobId] = useState<string | undefined>();
94
121
  const [selectedJobExecutionId, setSelectedJobExecutionId] = useState<string | undefined>();
122
+ const [localTab, setLocalTab] = useState<WorkflowRunTab>(tab ?? 'summary');
95
123
  const [sourcePanelOpen, setSourcePanelOpen] = useState(false);
96
124
  const [sourceFocus, setSourceFocus] = useState<WorkflowSourceFocus | null>(null);
97
125
  const sourcePanelId = useId();
98
- const sourceButtonRef = useRef<HTMLButtonElement>(null);
99
- // The button that last opened the panel (summary or a step detail), so Escape /
100
- // Close returns focus to whoever opened it.
126
+ // The step Source button that last opened the panel, so Escape / Close returns focus to it.
101
127
  const lastSourceTriggerRef = useRef<HTMLButtonElement | null>(null);
102
128
  const selectionControlled = selection !== undefined;
129
+ const tabControlled = onTabChange !== undefined;
130
+ const activeTab = tabControlled ? (tab ?? 'summary') : localTab;
103
131
  const sourceAvailable =
104
132
  query.data?.sourceSnapshot !== null && query.data?.sourceSnapshot !== undefined;
105
133
  const cancelMutation = useCancelWorkflowRunMutation(query.data);
@@ -111,8 +139,8 @@ function RunViewContent({
111
139
  }
112
140
  }, [sourceAvailable]);
113
141
 
114
- // If a refetch drops the focused step or its location, degrade to whole-workflow
115
- // focus so the panel never points at an unmounted Source button.
142
+ // If a refetch drops the focused step or its location, close the panel so it never points at
143
+ // an unmounted Source button.
116
144
  useEffect(() => {
117
145
  if (!sourceFocus) return;
118
146
  const stillLocated = query.data?.jobs.some((job) =>
@@ -122,31 +150,26 @@ function RunViewContent({
122
150
  );
123
151
  if (!stillLocated) {
124
152
  setSourceFocus(null);
125
- lastSourceTriggerRef.current = sourceButtonRef.current;
153
+ setSourcePanelOpen(false);
126
154
  }
127
155
  }, [sourceFocus, query.data]);
128
156
 
129
- if (query.isPending) return <WorkflowRunSkeleton />;
130
-
131
- // Only show the full error placeholder when nothing ever loaded. A refetch that fails after
132
- // a prior success keeps the loaded run on screen (see below) instead of wiping the pane,
133
- // since active-run polling can hit a transient API error.
134
- if (query.isError && query.data === undefined) {
135
- if (query.error instanceof ApiError && query.error.status === 404) {
136
- return <WorkflowRunNotFound />;
137
- }
138
- return <QueryLoadError query={query} subject="workflow run" icon="pulseLine" />;
139
- }
140
-
141
- if (query.data === undefined) return <WorkflowRunSkeleton />;
142
-
143
157
  const runData = query.data;
144
- const resolvedSelection = selectionControlled
145
- ? resolveWorkflowRunSelection({run: runData, selection})
146
- : undefined;
158
+ const resolvedSelection =
159
+ selectionControlled && runData
160
+ ? resolveWorkflowRunSelection({
161
+ run: runData,
162
+ selection: selection as WorkflowRunSelectionInput,
163
+ })
164
+ : undefined;
165
+ const hasExplicitJobSelection = Boolean(
166
+ selection?.jobId || selection?.jobExecutionId || selection?.stepId || selection?.stepAttemptId,
167
+ );
147
168
  const selectedJob = selectionControlled
148
- ? resolvedSelection?.job
149
- : (runData.jobs.find((job) => job.id === selectedJobId) ?? runData.jobs.at(0));
169
+ ? hasExplicitJobSelection
170
+ ? resolvedSelection?.job
171
+ : undefined
172
+ : (runData?.jobs.find((job) => job.id === selectedJobId) ?? runData?.jobs.at(0));
150
173
  const selectedJobExecution = selectionControlled
151
174
  ? resolvedSelection?.jobExecution
152
175
  : selectedJob
@@ -155,18 +178,45 @@ function RunViewContent({
155
178
  const selectedAttemptId = selectionControlled
156
179
  ? (resolvedSelection?.selectedAttemptId ?? null)
157
180
  : undefined;
158
- // Explicit per-step focus wins; fall back to the URL-selected step so deep links
159
- // still pre-highlight when the summary opens the whole-workflow source.
181
+ // Explicit per-step focus wins; fall back to the URL-selected step so deep links still
182
+ // pre-highlight source in the Source tab.
160
183
  const highlightedLineRange =
161
184
  sourceFocus?.location ?? resolvedSelection?.step?.sourceLocation ?? null;
162
- const sourceSnapshot = runData.sourceSnapshot;
185
+ const sourceSnapshot = runData?.sourceSnapshot ?? null;
186
+
187
+ function changeTab(nextTab: WorkflowRunTab) {
188
+ if (!tabControlled) {
189
+ setLocalTab(nextTab);
190
+ } else if (activeTab !== nextTab) {
191
+ onTabChange?.(nextTab);
192
+ }
193
+
194
+ if (nextTab !== 'jobs') {
195
+ setSourcePanelOpen(false);
196
+ setSourceFocus(null);
197
+ }
198
+ }
199
+
200
+ function handleTabChange(nextTab: string) {
201
+ if (!WORKFLOW_RUN_TABS.some((value) => value === nextTab)) return;
202
+ changeTab(nextTab as WorkflowRunTab);
203
+ }
204
+
163
205
  async function rerun(mode: WorkflowRunRerunMode) {
206
+ if (!runData || !workspaceSlug || !projectSlug) {
207
+ toast.error('Could not start re-run from this route.');
208
+ return;
209
+ }
164
210
  try {
165
211
  const run = await rerunMutation.mutateAsync({workflowRunId: runData.id, mode});
166
212
  toast.success('Re-run started');
167
213
  await navigate({
168
- to: '/workspaces/$wid/projects/$pid/runs/$workflowRunId',
169
- params: {wid: workspaceId, pid: projectId, workflowRunId: run.id},
214
+ to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',
215
+ params: {
216
+ workspaceSlug,
217
+ projectSlug,
218
+ workflowRunId: run.id,
219
+ },
170
220
  search: ((previous: Record<string, unknown>) =>
171
221
  withoutWorkflowRunSelectionSearch(previous)) as never,
172
222
  });
@@ -175,12 +225,16 @@ function RunViewContent({
175
225
  }
176
226
  }
177
227
 
178
- function selectJob(jobId: string | undefined) {
228
+ function selectJob(jobId: string | undefined, source: JobGraphSelectionSource = 'pointer') {
229
+ if (source === 'pointer') changeTab('jobs');
179
230
  if (!selectionControlled) {
180
231
  setSelectedJobId(jobId);
181
232
  setSelectedJobExecutionId(undefined);
182
233
  return;
183
234
  }
235
+ // Keyboard roving focus is an interaction within the Summary graph. Keep that panel mounted
236
+ // and let JobGraphView own the visual selection so focus is not moved to a different tab.
237
+ if (source === 'keyboard') return;
184
238
 
185
239
  onSelectionChange?.(
186
240
  jobId ? {jobId, runAttempt: selection?.runAttempt} : {runAttempt: selection?.runAttempt},
@@ -225,20 +279,6 @@ function RunViewContent({
225
279
  });
226
280
  }
227
281
 
228
- function openWholeWorkflowSource() {
229
- setSourceFocus(null);
230
- lastSourceTriggerRef.current = sourceButtonRef.current;
231
- setSourcePanelOpen(true);
232
- }
233
-
234
- function toggleWholeWorkflowSource() {
235
- if (sourcePanelOpen && sourceFocus === null) {
236
- closeSourcePanel();
237
- return;
238
- }
239
- openWholeWorkflowSource();
240
- }
241
-
242
282
  function openStepSource(
243
283
  stepId: string,
244
284
  location: StepSourceLocation,
@@ -251,13 +291,11 @@ function RunViewContent({
251
291
 
252
292
  function closeSourcePanel() {
253
293
  const trigger = lastSourceTriggerRef.current;
254
- const fallbackTrigger = sourceButtonRef.current;
255
294
  setSourcePanelOpen(false);
256
- // Defer so focus lands after the panel unmounts; clear the focus only after
257
- // focusing so the opener button is still expanded (force-visible) on return.
295
+ // Defer so focus lands after the panel unmounts; clear the focus only after focusing so the
296
+ // opener button is still expanded (force-visible) on return.
258
297
  window.setTimeout(() => {
259
- const focusTarget = trigger?.isConnected ? trigger : fallbackTrigger;
260
- focusTarget?.focus();
298
+ if (trigger?.isConnected) trigger.focus();
261
299
  setSourceFocus(null);
262
300
  }, 0);
263
301
  }
@@ -270,38 +308,73 @@ function RunViewContent({
270
308
  });
271
309
  }
272
310
 
311
+ const fatalError = query.isError && runData === undefined;
312
+ const tabState = fatalError ? (
313
+ query.error instanceof ApiError && query.error.status === 404 ? (
314
+ <WorkflowRunNotFound />
315
+ ) : (
316
+ <QueryLoadError query={query} subject="workflow run" icon="pulseLine" />
317
+ )
318
+ ) : query.isPending || runData === undefined ? (
319
+ <WorkflowRunContentSkeleton />
320
+ ) : null;
321
+
273
322
  return (
274
323
  <>
275
- <div className="flex min-w-0 flex-1 flex-col">
276
- <WorkflowRunSummary
277
- workspaceId={workspaceId}
278
- projectId={projectId}
279
- run={runData}
280
- sourceAvailable={sourceAvailable}
281
- sourceOpen={sourcePanelOpen && sourceFocus === null}
282
- sourcePanelId={sourcePanelId}
283
- sourceButtonRef={sourceButtonRef}
284
- onSourceToggle={toggleWholeWorkflowSource}
285
- cancelling={cancelMutation.isPending}
286
- onCancel={cancelRun}
287
- rerunPending={rerunMutation.isPending}
288
- onRerun={(mode) => void rerun(mode)}
289
- latestAttempt={runData.latestAttempt}
324
+ <Tabs
325
+ value={activeTab}
326
+ onValueChange={handleTabChange}
327
+ className="flex min-w-0 flex-1 flex-col gap-0"
328
+ >
329
+ {runData ? (
330
+ <WorkflowRunSummary
331
+ workspaceSlug={workspaceSlug}
332
+ projectSlug={projectSlug}
333
+ run={runData}
334
+ cancelling={cancelMutation.isPending}
335
+ onCancel={cancelRun}
336
+ rerunPending={rerunMutation.isPending}
337
+ onRerun={(mode) => void rerun(mode)}
338
+ latestAttempt={runData.latestAttempt}
339
+ />
340
+ ) : (
341
+ <WorkflowRunSkeleton />
342
+ )}
343
+ {runData && query.isError ? <WorkflowRunStaleError query={query} /> : null}
344
+ <RunTabStrip
345
+ jobCount={runData?.jobs.length}
346
+ jobsFailed={runData?.jobs.filter((job) => job.status === 'failed').length}
347
+ annotationSummary={annotationSummary}
290
348
  />
291
- {query.isError ? <WorkflowRunStaleError query={query} /> : null}
292
- <div className="min-h-0 flex-1 overflow-auto bg-background-neutral-base p-16">
293
- <div className="mx-auto flex w-full max-w-[1280px] flex-col gap-16">
294
- <JobGraph
295
- run={runData}
296
- selectedJobId={selectedJob?.id}
297
- onSelectedJobChange={selectJob}
298
- />
299
- {selectedJob ? (
300
- <JobCard
301
- workspaceId={workspaceId}
302
- job={selectedJob}
349
+ <TabsContents className="min-h-0 flex-1 overflow-auto bg-background-neutral-background pb-24 pt-16">
350
+ <TabsContent
351
+ value="summary"
352
+ tabIndex={-1}
353
+ className="mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24 outline-none"
354
+ >
355
+ {runData ? (
356
+ <JobGraph
357
+ run={runData}
358
+ selectedJobId={selectedJob?.id}
359
+ onSelectedJobChange={selectJob}
360
+ />
361
+ ) : (
362
+ tabState
363
+ )}
364
+ </TabsContent>
365
+ <TabsContent
366
+ value="jobs"
367
+ tabIndex={-1}
368
+ className="mx-auto w-full max-w-[1120px] px-24 outline-none"
369
+ >
370
+ {runData ? (
371
+ <RunJobsList
372
+ jobs={runData.jobs}
373
+ selectedJobId={selectedJob?.id}
374
+ onSelectedJobChange={selectJob}
375
+ workspaceSlug={workspaceSlug}
303
376
  selectedJobExecution={selectedJobExecution}
304
- selectedAttemptId={selectedJob.carriedOver ? undefined : selectedAttemptId}
377
+ selectedAttemptId={selectedAttemptId}
305
378
  onSelectedJobExecutionChange={selectJobExecution}
306
379
  onSelectedAttemptChange={selectionControlled ? selectAttempt : undefined}
307
380
  sourcePanelId={sourcePanelId}
@@ -309,10 +382,61 @@ function RunViewContent({
309
382
  focusedSourceStepId={sourceFocus?.stepId ?? null}
310
383
  onOpenStepSource={openStepSource}
311
384
  />
312
- ) : null}
313
- </div>
314
- </div>
315
- </div>
385
+ ) : (
386
+ tabState
387
+ )}
388
+ </TabsContent>
389
+ <TabsContent
390
+ value="annotations"
391
+ tabIndex={-1}
392
+ className="mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24 outline-none"
393
+ >
394
+ {runData ? (
395
+ <>
396
+ <RunAnnotationSummaryLine
397
+ summary={annotationSummary}
398
+ workspaceSlug={workspaceSlug}
399
+ projectSlug={projectSlug}
400
+ workflowRunId={runData.id}
401
+ search={selection}
402
+ />
403
+ <RunAnnotationsEmpty />
404
+ </>
405
+ ) : (
406
+ tabState
407
+ )}
408
+ </TabsContent>
409
+ <TabsContent
410
+ value="source"
411
+ keepMounted={sourceSnapshot !== null}
412
+ tabIndex={-1}
413
+ className="mx-auto flex min-h-full w-full max-w-[1120px] px-24 outline-none"
414
+ >
415
+ {runData ? (
416
+ sourceSnapshot ? (
417
+ <WorkflowSourceContent
418
+ source={sourceSnapshot}
419
+ highlightedLineRange={highlightedLineRange}
420
+ scrollHighlightedIntoView
421
+ />
422
+ ) : (
423
+ <EmptyState
424
+ className="min-h-160 w-full"
425
+ icon="fileDamageLine"
426
+ title="Source snapshot unavailable"
427
+ description={
428
+ runData.isTemporary
429
+ ? 'Temporary runs do not capture workflow source.'
430
+ : 'This run was created before workflow source snapshots were captured.'
431
+ }
432
+ />
433
+ )
434
+ ) : (
435
+ tabState
436
+ )}
437
+ </TabsContent>
438
+ </TabsContents>
439
+ </Tabs>
316
440
  <WorkflowSourcePanel
317
441
  id={sourcePanelId}
318
442
  source={sourceSnapshot}
@@ -1 +1,2 @@
1
+ export * from './workflow-source-content.js';
1
2
  export * from './workflow-source-panel.js';
@@ -0,0 +1,84 @@
1
+ import {
2
+ CodeBlock,
3
+ CodeBlockBody,
4
+ CodeBlockContent,
5
+ CodeBlockCopyButton,
6
+ CodeBlockFilename,
7
+ CodeBlockFiles,
8
+ CodeBlockHeader,
9
+ type CodeBlockHighlightedLineRange,
10
+ CodeBlockItem,
11
+ } from '@shipfox/react-ui/code-block';
12
+ import {cn} from '@shipfox/react-ui/utils';
13
+ import type {ReactNode} from 'react';
14
+ import type {WorkflowSourceSnapshot} from '#core/workflow-run.js';
15
+
16
+ const WORKFLOW_SOURCE_FILENAME = 'workflow.yaml';
17
+ const WORKFLOW_SOURCE_CODE_THEMES = {
18
+ light: 'vitesse-dark',
19
+ dark: 'vitesse-dark',
20
+ };
21
+
22
+ export interface WorkflowSourceContentProps {
23
+ source: WorkflowSourceSnapshot;
24
+ highlightedLineRange?: CodeBlockHighlightedLineRange | null | undefined;
25
+ scrollHighlightedIntoView?: boolean | undefined;
26
+ headerAction?: ReactNode;
27
+ className?: string | undefined;
28
+ }
29
+
30
+ export function WorkflowSourceContent({
31
+ source,
32
+ highlightedLineRange,
33
+ scrollHighlightedIntoView,
34
+ headerAction,
35
+ className,
36
+ }: WorkflowSourceContentProps) {
37
+ const data = [
38
+ {
39
+ language: 'yaml',
40
+ filename: WORKFLOW_SOURCE_FILENAME,
41
+ code: source.content,
42
+ },
43
+ ];
44
+
45
+ return (
46
+ <CodeBlock
47
+ data={data}
48
+ className={cn(
49
+ 'flex size-full flex-col rounded-none bg-background-contrast-base shadow-none',
50
+ className,
51
+ )}
52
+ >
53
+ <CodeBlockHeader className="shrink-0 border-b border-border-contrast-base bg-background-contrast-base">
54
+ <CodeBlockFiles>
55
+ {(item) => <CodeBlockFilename value={item.filename}>{item.filename}</CodeBlockFilename>}
56
+ </CodeBlockFiles>
57
+ <CodeBlockCopyButton />
58
+ {headerAction}
59
+ </CodeBlockHeader>
60
+ <CodeBlockBody className="flex min-h-0 flex-1 overflow-auto scrollbar">
61
+ {(item) => (
62
+ <CodeBlockItem
63
+ value={item.filename}
64
+ className={cn(
65
+ 'min-h-full px-0 pb-0',
66
+ '[&>div]:rounded-none [&>div]:border-0 [&>div]:bg-background-contrast-base [&>div]:dark:bg-background-contrast-base',
67
+ '[&_code]:!text-sm [&_code]:!text-foreground-neutral-on-inverted [&_.line]:!text-sm [&_.line]:before:!text-sm [&_.line]:before:!text-foreground-neutral-muted',
68
+ )}
69
+ >
70
+ <CodeBlockContent
71
+ language="yaml"
72
+ themes={WORKFLOW_SOURCE_CODE_THEMES}
73
+ syntaxHighlighting
74
+ highlightedLineRange={highlightedLineRange}
75
+ scrollHighlightedIntoView={scrollHighlightedIntoView}
76
+ >
77
+ {item.code}
78
+ </CodeBlockContent>
79
+ </CodeBlockItem>
80
+ )}
81
+ </CodeBlockBody>
82
+ </CodeBlock>
83
+ );
84
+ }
@@ -1,24 +1,9 @@
1
1
  import {Button} from '@shipfox/react-ui/button';
2
- import {
3
- CodeBlock,
4
- CodeBlockBody,
5
- CodeBlockContent,
6
- CodeBlockCopyButton,
7
- CodeBlockFilename,
8
- CodeBlockFiles,
9
- CodeBlockHeader,
10
- type CodeBlockHighlightedLineRange,
11
- CodeBlockItem,
12
- } from '@shipfox/react-ui/code-block';
2
+ import type {CodeBlockHighlightedLineRange} from '@shipfox/react-ui/code-block';
13
3
  import {Sheet, SheetClose, SheetContent, SheetTitle} from '@shipfox/react-ui/sheet';
14
4
  import {cn} from '@shipfox/react-ui/utils';
15
5
  import type {WorkflowSourceSnapshot} from '#core/workflow-run.js';
16
-
17
- const WORKFLOW_SOURCE_FILENAME = 'workflow.yaml';
18
- const WORKFLOW_SOURCE_CODE_THEMES = {
19
- light: 'vitesse-dark',
20
- dark: 'vitesse-dark',
21
- };
6
+ import {WorkflowSourceContent} from './workflow-source-content.js';
22
7
 
23
8
  export interface WorkflowSourcePanelProps {
24
9
  id: string;
@@ -61,76 +46,24 @@ export function WorkflowSourcePanel({
61
46
  )}
62
47
  >
63
48
  <SheetTitle className="sr-only">Workflow source</SheetTitle>
64
- <WorkflowSourcePanelContent
49
+ <WorkflowSourceContent
65
50
  source={source}
66
51
  highlightedLineRange={highlightedLineRange}
67
52
  scrollHighlightedIntoView={scrollHighlightedIntoView}
53
+ headerAction={
54
+ <SheetClose asChild>
55
+ <Button
56
+ type="button"
57
+ variant="transparentMuted"
58
+ size="sm"
59
+ iconLeft="close"
60
+ aria-label="Close source"
61
+ />
62
+ </SheetClose>
63
+ }
68
64
  />
69
65
  </SheetContent>
70
66
  ) : null}
71
67
  </Sheet>
72
68
  );
73
69
  }
74
-
75
- function WorkflowSourcePanelContent({
76
- source,
77
- highlightedLineRange,
78
- scrollHighlightedIntoView,
79
- }: {
80
- source: WorkflowSourceSnapshot;
81
- highlightedLineRange: CodeBlockHighlightedLineRange | null | undefined;
82
- scrollHighlightedIntoView: boolean | undefined;
83
- }) {
84
- const data = [
85
- {
86
- language: 'yaml',
87
- filename: WORKFLOW_SOURCE_FILENAME,
88
- code: source.content,
89
- },
90
- ];
91
-
92
- return (
93
- <CodeBlock
94
- data={data}
95
- className="flex size-full flex-col rounded-none bg-background-contrast-base shadow-none"
96
- >
97
- <CodeBlockHeader className="shrink-0 border-b border-border-contrast-base bg-background-contrast-base">
98
- <CodeBlockFiles>
99
- {(item) => <CodeBlockFilename value={item.filename}>{item.filename}</CodeBlockFilename>}
100
- </CodeBlockFiles>
101
- <CodeBlockCopyButton />
102
- <SheetClose asChild>
103
- <Button
104
- type="button"
105
- variant="transparentMuted"
106
- size="sm"
107
- iconLeft="close"
108
- aria-label="Close source"
109
- />
110
- </SheetClose>
111
- </CodeBlockHeader>
112
- <CodeBlockBody className="flex min-h-0 flex-1 overflow-auto scrollbar">
113
- {(item) => (
114
- <CodeBlockItem
115
- value={item.filename}
116
- className={cn(
117
- 'min-h-full px-0 pb-0',
118
- '[&>div]:rounded-none [&>div]:border-0 [&>div]:bg-background-contrast-base [&>div]:dark:bg-background-contrast-base',
119
- '[&_code]:!text-sm [&_code]:!text-foreground-neutral-on-inverted [&_.line]:!text-sm [&_.line]:before:!text-sm [&_.line]:before:!text-foreground-neutral-muted',
120
- )}
121
- >
122
- <CodeBlockContent
123
- language="yaml"
124
- themes={WORKFLOW_SOURCE_CODE_THEMES}
125
- syntaxHighlighting
126
- highlightedLineRange={highlightedLineRange}
127
- scrollHighlightedIntoView={scrollHighlightedIntoView}
128
- >
129
- {item.code}
130
- </CodeBlockContent>
131
- </CodeBlockItem>
132
- )}
133
- </CodeBlockBody>
134
- </CodeBlock>
135
- );
136
- }
@@ -35,4 +35,13 @@ describe('getWorkflowStatusVisual', () => {
35
35
 
36
36
  expect(visual).toEqual({kind: 'running', label: 'Running', dot: 'info', badge: 'info'});
37
37
  });
38
+
39
+ test('returns the active listener visual', () => {
40
+ expect(getWorkflowStatusVisual('listening')).toEqual({
41
+ kind: 'listening',
42
+ label: 'Listening',
43
+ dot: 'info',
44
+ badge: 'info',
45
+ });
46
+ });
38
47
  });