@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 +1 @@
1
- {"version":3,"sources":["../../../src/components/workflow-run-view/job-card.tsx"],"sourcesContent":["import {TriggerSourceIcon} from '@shipfox/client-triggers';\nimport {Badge} from '@shipfox/react-ui/badge';\nimport {Button} from '@shipfox/react-ui/button';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {RelativeTime} from '@shipfox/react-ui/relative-time';\nimport {useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Text} from '@shipfox/react-ui/typography';\nimport type {ReactNode} from 'react';\nimport {Fragment, useId, useRef, useState} from 'react';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {\n AGENT_CONFIG_ISSUES,\n type Job,\n type JobExecution,\n type JobExecutionTime,\n STEP_ERROR_REASONS,\n type Step,\n type StepError,\n type StepSourceLocation,\n WORKFLOW_JOB_STATUSES,\n workflowRunTriggerDisplayLabel,\n workflowRunTriggerLabel,\n} from '#core/workflow-run.js';\nimport {type StepExpandedContext, StepList, type StepListEmptyState} from '../step-list/index.js';\nimport {AgentConfigFailureCallout} from './agent-config-failure-callout.js';\nimport {JobExecutionSwitcher} from './job-execution-switcher.js';\nimport {formatJobExecutionTime, JobExecutionTimeText} from './job-execution-time-text.js';\nimport {StepAttemptLogPanel} from './step-attempt-log-panel.js';\n\nconst STATUS_BADGE_LABEL_WIDTH_CH = Math.max(\n ...WORKFLOW_JOB_STATUSES.map((status) => getWorkflowStatusVisual(status).label.length),\n);\n\ninterface LocalSelectedAttempt {\n jobExecutionId: string | undefined;\n attemptId: string | null;\n}\n\nexport function JobCard({\n workspaceId,\n job,\n selectedJobExecution,\n selectedAttemptId,\n onSelectedJobExecutionChange,\n onSelectedAttemptChange,\n renderExpandedStep,\n sourcePanelId,\n sourceAvailable,\n focusedSourceStepId,\n onOpenStepSource,\n}: {\n workspaceId: string;\n job: Job;\n selectedJobExecution: JobExecution | undefined;\n selectedAttemptId: string | null | undefined;\n onSelectedJobExecutionChange: ((jobExecutionId: string | undefined) => void) | undefined;\n onSelectedAttemptChange: ((attemptId: string | undefined) => void) | undefined;\n renderExpandedStep?: ((context: StepExpandedContext) => ReactNode) | undefined;\n sourcePanelId?: string | undefined;\n sourceAvailable?: boolean | undefined;\n focusedSourceStepId?: string | null | undefined;\n onOpenStepSource?:\n | ((stepId: string, location: StepSourceLocation, trigger: HTMLButtonElement | null) => void)\n | undefined;\n}) {\n const titleId = useId();\n const sourceButtonRef = useRef<HTMLButtonElement>(null);\n const [localSelectedAttempt, setLocalSelectedAttempt] = useState<LocalSelectedAttempt>({\n jobExecutionId: undefined,\n attemptId: null,\n });\n const selectedExecutionStatus = selectedJobExecution?.status ?? job.status;\n const effectiveSelectedAttemptId =\n selectedAttemptId === undefined &&\n localSelectedAttempt.jobExecutionId === selectedJobExecution?.id\n ? localSelectedAttempt.attemptId\n : selectedAttemptId;\n const selectedSourceAction = selectedStepSourceAction(\n selectedJobExecution,\n effectiveSelectedAttemptId,\n );\n const defaultRenderExpandedStep = ({\n step,\n stepId,\n attempt,\n attemptError,\n attemptStatus,\n carriedOver,\n }: StepExpandedContext) =>\n carriedOver ? (\n <CarriedOverStepPanel />\n ) : (\n <StepAttemptDetailPanel\n workspaceId={workspaceId}\n step={step}\n stepId={stepId}\n attempt={attempt}\n attemptError={attemptError}\n attemptStatus={attemptStatus}\n />\n );\n\n function selectAttempt(attemptId: string | undefined) {\n if (selectedAttemptId === undefined) {\n setLocalSelectedAttempt({\n jobExecutionId: selectedJobExecution?.id,\n attemptId: attemptId ?? null,\n });\n }\n\n onSelectedAttemptChange?.(attemptId);\n }\n\n return (\n <section\n aria-labelledby={titleId}\n className=\"flex min-h-0 flex-col rounded-8 border border-border-neutral-base bg-background-components-base\"\n >\n <div className=\"grid min-h-52 min-w-0 grid-cols-1 gap-y-4 px-16 py-12\">\n <div className=\"flex min-w-0 flex-wrap items-center justify-between gap-10\">\n {job.mode === 'listening' &&\n job.jobExecutions.length > 0 &&\n onSelectedJobExecutionChange ? (\n <>\n <Text as=\"h2\" id={titleId} size=\"sm\" bold className=\"sr-only\">\n {job.displayName}\n </Text>\n <div className=\"flex min-w-0 items-center gap-8\">\n <JobStatusBadge status={selectedExecutionStatus} />\n <JobExecutionSwitcher\n job={job}\n selectedJobExecution={selectedJobExecution?.id ?? null}\n onSelectedJobExecutionChange={onSelectedJobExecutionChange}\n variant=\"title\"\n />\n </div>\n </>\n ) : (\n <div className=\"flex min-w-0 items-center gap-8\">\n <JobStatusBadge status={selectedExecutionStatus} />\n <Text\n as=\"h2\"\n id={titleId}\n size=\"sm\"\n bold\n className=\"min-w-0 truncate text-foreground-neutral-base\"\n >\n {job.displayName}\n </Text>\n </div>\n )}\n {sourceAvailable && selectedSourceAction && sourcePanelId && onOpenStepSource ? (\n <div className=\"flex min-w-max items-center gap-6\">\n <Button\n ref={sourceButtonRef}\n type=\"button\"\n variant=\"secondary\"\n size=\"xs\"\n aria-controls={sourcePanelId}\n aria-expanded={focusedSourceStepId === selectedSourceAction.stepId}\n onClick={() =>\n onOpenStepSource(\n selectedSourceAction.stepId,\n selectedSourceAction.location,\n sourceButtonRef.current,\n )\n }\n >\n View source\n </Button>\n </div>\n ) : null}\n </div>\n <JobExecutionMetadata execution={selectedJobExecution} />\n </div>\n <div className=\"min-h-0 border-t border-border-neutral-strong\">\n {selectedJobExecution ? (\n <StepList\n job={job}\n jobExecution={selectedJobExecution}\n selectedAttemptId={selectedAttemptId}\n onSelectedAttemptChange={selectAttempt}\n autoSelectActiveAttempt\n emptyState={emptyStateForJob(job, selectedJobExecution)}\n showHeader={false}\n className=\"rounded-none border-0 bg-transparent\"\n renderExpandedStep={renderExpandedStep ?? defaultRenderExpandedStep}\n />\n ) : (\n <JobExecutionEmptyState job={job} />\n )}\n </div>\n </section>\n );\n}\n\nfunction JobStatusBadge({status}: {status: Job['status'] | JobExecution['status']}) {\n const visual = getWorkflowStatusVisual(status);\n\n return (\n <Badge variant={visual.badge} size=\"xs\">\n <span className=\"text-center\" style={{width: `${STATUS_BADGE_LABEL_WIDTH_CH}ch`}}>\n {visual.label}\n </span>\n </Badge>\n );\n}\n\nfunction JobExecutionEmptyState({job}: {job: Job}) {\n const emptyState = emptyStateForMissingExecution(job);\n\n return (\n <EmptyState\n className=\"min-h-120 px-16 py-20\"\n icon=\"componentLine\"\n title={emptyState.title}\n description={emptyState.description}\n variant=\"compact\"\n />\n );\n}\n\nfunction JobExecutionMetadata({execution}: {execution: JobExecution | undefined}) {\n if (!execution) return null;\n\n const queueTime = execution.queueTime;\n const runTime = execution.runTime;\n const executionStartedAt = execution.startedAt ?? execution.queuedAt ?? execution.createdAt;\n const items = [\n execution.triggerEvents.length > 0 ? {key: 'trigger', kind: 'trigger' as const} : null,\n {key: 'started', kind: 'started' as const, value: executionStartedAt},\n queueTime\n ? {key: 'queue', icon: 'hourglassLine' as const, kind: 'queue' as const, time: queueTime}\n : null,\n runTime ? {key: 'run', icon: 'timerLine' as const, kind: 'run' as const, time: runTime} : null,\n ].filter((item) => item !== null);\n\n if (items.length === 0) return null;\n\n return (\n <div className=\"flex min-w-0 items-center gap-12 overflow-hidden text-foreground-neutral-muted\">\n {items.map((item, index) => (\n <Fragment key={item.key}>\n {index > 0 ? <MetadataSeparator /> : null}\n {item.kind === 'started' ? (\n <RelativeTime\n value={item.value}\n className=\"shrink-0 whitespace-nowrap text-xs leading-20 text-foreground-neutral-muted\"\n />\n ) : item.kind === 'trigger' ? (\n <JobExecutionTriggerMetadata execution={execution} />\n ) : (\n <JobExecutionMetadataItem icon={item.icon} kind={item.kind} time={item.time} />\n )}\n </Fragment>\n ))}\n </div>\n );\n}\n\nfunction JobExecutionTriggerMetadata({execution}: {execution: JobExecution}) {\n const triggerEvent = execution.triggerEvents[0];\n if (!triggerEvent) return null;\n\n const triggerLabel = workflowRunTriggerLabel({\n triggerSource: triggerEvent.source,\n triggerEvent: triggerEvent.event,\n });\n const triggerDisplayLabel = workflowRunTriggerDisplayLabel({\n triggerSource: triggerEvent.source,\n triggerEvent: triggerEvent.event,\n });\n const triggerCount = execution.triggerEvents.length;\n const countLabel = triggerCount > 1 ? ` (${triggerCount})` : '';\n const tooltip = triggerCount > 1 ? `${triggerLabel} (${triggerCount} events)` : triggerLabel;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={tooltip}\n className=\"inline-flex min-w-0 max-w-full cursor-default items-center gap-4 rounded-4 border-0 bg-transparent p-0 text-left text-foreground-neutral-muted outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <TriggerSourceIcon\n provider={triggerEvent.source}\n source={triggerEvent.source}\n aria-hidden=\"true\"\n className=\"size-12 shrink-0\"\n />\n <Text as=\"span\" size=\"xs\" className=\"min-w-0 truncate\">\n {triggerDisplayLabel}\n {countLabel}\n </Text>\n </button>\n </TooltipTrigger>\n <TooltipContent>\n <Text as=\"span\" size=\"xs\" className=\"block max-w-[360px] break-words\">\n {tooltip}\n </Text>\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction JobExecutionMetadataItem({\n icon,\n kind,\n time,\n}: {\n icon: 'hourglassLine' | 'timerLine';\n kind: 'queue' | 'run';\n time: JobExecutionTime;\n}) {\n useTimeTick();\n\n const tooltip = `${jobExecutionTimeVerb(kind, time)} for ${formatJobExecutionTime(time)}`;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={tooltip}\n className=\"inline-flex min-w-0 cursor-default items-center gap-4 rounded-4 border-0 bg-transparent p-0 text-foreground-neutral-muted outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <Icon name={icon} className=\"size-12 shrink-0\" aria-hidden=\"true\" />\n <span className=\"shrink-0 text-xs leading-20 text-foreground-neutral-muted tabular-nums\">\n <JobExecutionTimeText time={time} />\n </span>\n </button>\n </TooltipTrigger>\n <TooltipContent>\n <Text as=\"span\" size=\"xs\">\n {tooltip}\n </Text>\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction jobExecutionTimeVerb(kind: 'queue' | 'run', time: JobExecutionTime): string {\n if (kind === 'queue') return time.state === 'live' ? 'Queuing' : 'Queued';\n return time.state === 'live' ? 'Running' : 'Ran';\n}\n\nfunction MetadataSeparator() {\n return <span aria-hidden=\"true\" className=\"h-12 w-px shrink-0 bg-border-neutral-strong\" />;\n}\n\nfunction selectedStepSourceAction(\n jobExecution: JobExecution | undefined,\n selectedAttemptId: string | null | undefined,\n): {stepId: string; location: StepSourceLocation} | null {\n if (!jobExecution || !selectedAttemptId) return null;\n\n for (const step of jobExecution.steps) {\n const selected =\n selectedAttemptId === `carried-over:${step.id}` ||\n step.attempts.some((attempt) => attempt.id === selectedAttemptId);\n if (selected && step.sourceLocation) {\n return {stepId: step.id, location: step.sourceLocation};\n }\n }\n\n return null;\n}\n\nfunction StepAttemptDetailPanel({\n workspaceId,\n step,\n stepId,\n attempt,\n attemptError,\n attemptStatus,\n}: {\n workspaceId: string;\n step: Step;\n stepId: string;\n attempt: number;\n attemptError: Record<string, unknown> | null;\n attemptStatus: string;\n}) {\n const selectedAttemptError = toSelectedAttemptError(step, attemptError) ?? step.error;\n\n return (\n <div className=\"flex min-w-0 flex-col gap-10\">\n {isAgentConfigFailure(step, selectedAttemptError) ? (\n <AgentConfigFailureCallout\n workspaceId={workspaceId}\n config={step.agentConfig}\n error={selectedAttemptError}\n />\n ) : null}\n <StepAttemptLogPanel stepId={stepId} attempt={attempt} attemptStatus={attemptStatus} />\n </div>\n );\n}\n\nfunction toSelectedAttemptError(\n step: Step,\n error: Record<string, unknown> | null,\n): StepError | null {\n if (error === null) return null;\n\n const rawReason = error.reason;\n const parsedReason =\n typeof rawReason === 'string' &&\n STEP_ERROR_REASONS.has(rawReason as StepError['reason'] & string)\n ? (rawReason as NonNullable<StepError['reason']>)\n : undefined;\n const rawAgentConfigIssue = error.agentConfigIssue ?? error.agent_config_issue;\n const agentConfigIssue =\n typeof rawAgentConfigIssue === 'string' &&\n AGENT_CONFIG_ISSUES.has(rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)\n ? (rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)\n : undefined;\n const exitCode = error.exitCode ?? error.exit_code;\n const resolvedReason = parsedReason\n ? parsedReason\n : agentConfigIssue\n ? 'agent_config_invalid'\n : undefined;\n\n if (resolvedReason === undefined) return null;\n\n return {\n message: typeof error.message === 'string' ? error.message : '',\n exitCode: exitCode === null || typeof exitCode === 'number' ? exitCode : null,\n signal: typeof error.signal === 'string' ? error.signal : undefined,\n reason: resolvedReason,\n agentConfigIssue,\n category: step.type === 'setup' ? 'setup' : 'user',\n };\n}\n\nfunction isAgentConfigFailure(step: Step, error: StepError | null): boolean {\n return step.type === 'agent' && error?.reason === 'agent_config_invalid';\n}\n\nfunction emptyStateForJob(job: Job, jobExecution: JobExecution): StepListEmptyState | undefined {\n if (job.carriedOver) {\n return {\n title: 'Carried over from a previous attempt',\n description: 'This job did not execute in this run.',\n status: 'succeeded',\n };\n }\n\n if (jobExecution.status === 'pending') {\n return {\n title: 'Waiting for this job to start',\n description: 'Steps will appear here once the job starts.',\n status: jobExecution.status,\n };\n }\n\n if (jobExecution.status === 'running') {\n return {\n title: 'Waiting for the first step',\n description: 'This job is running, but no steps have started yet.',\n status: jobExecution.status,\n };\n }\n\n if (jobExecution.status === 'cancelled') {\n return {\n title: 'Cancelled before start',\n description: 'This job was cancelled before any step started.',\n status: jobExecution.status,\n };\n }\n\n if (jobExecution.status === 'succeeded' || jobExecution.status === 'failed') {\n return {\n title: 'No steps recorded',\n description: `Execution #${jobExecution.sequence} finished without recorded steps.`,\n status: jobExecution.status,\n };\n }\n\n return undefined;\n}\n\nfunction emptyStateForMissingExecution(job: Job): StepListEmptyState {\n if (job.carriedOver) {\n return {\n title: 'Carried over from a previous attempt',\n description: 'This job did not execute in this run.',\n status: 'succeeded',\n };\n }\n\n if (job.mode === 'listening' && job.listenerStatus === 'listening') {\n return {\n title: 'Waiting for trigger events',\n description: 'Matching trigger events will create job executions here.',\n status: 'running',\n };\n }\n\n if (job.mode === 'listening' && job.listenerStatus === 'resolved') {\n return {\n title: 'Listener resolved without executions',\n description: 'No matching trigger event created a job execution before the listener stopped.',\n status: job.status,\n };\n }\n\n if (job.status === 'pending') {\n return {\n title: 'Waiting for this job to start',\n description: 'No execution has been created for this job yet.',\n status: 'pending',\n };\n }\n\n if (job.status === 'skipped') {\n return {\n title: 'This job was skipped',\n description: skippedJobDescription(job.statusReason),\n status: 'skipped',\n };\n }\n\n if (job.status === 'cancelled') {\n return {\n title: 'Cancelled before start',\n description: 'This job was cancelled before an execution was created.',\n status: 'cancelled',\n };\n }\n\n return {\n title: 'Execution details unavailable',\n description: 'This job finished, but no job execution record is available.',\n status: job.status,\n };\n}\n\nfunction CarriedOverStepPanel() {\n return (\n <EmptyState\n className=\"min-h-120 rounded-8 border border-border-neutral-base bg-background-components-base px-16 py-20\"\n icon=\"componentLine\"\n title=\"Carried over from a previous attempt\"\n description=\"Not executed in this run.\"\n variant=\"compact\"\n />\n );\n}\n\nfunction skippedJobDescription(reason: Job['statusReason']): string {\n switch (reason) {\n case 'dependency_not_completed':\n return 'A required job did not complete, so this job was skipped.';\n case 'default_gate_rejected':\n return 'A required job did not succeed, so this job was skipped.';\n case 'condition_false':\n case 'condition_rejected':\n return 'The job condition did not match, so this job was skipped.';\n case 'condition_errored':\n return 'The job condition could not be evaluated, so this job was skipped.';\n case 'user_cancelled':\n case 'run_cancelled':\n case 'timed_out':\n case 'runner_lost':\n case 'step_failed':\n case 'unknown':\n case null:\n return 'This job did not start.';\n }\n}\n"],"names":["TriggerSourceIcon","Badge","Button","EmptyState","Icon","RelativeTime","useTimeTick","Tooltip","TooltipContent","TooltipTrigger","Text","Fragment","useId","useRef","useState","getWorkflowStatusVisual","AGENT_CONFIG_ISSUES","STEP_ERROR_REASONS","WORKFLOW_JOB_STATUSES","workflowRunTriggerDisplayLabel","workflowRunTriggerLabel","StepList","AgentConfigFailureCallout","JobExecutionSwitcher","formatJobExecutionTime","JobExecutionTimeText","StepAttemptLogPanel","STATUS_BADGE_LABEL_WIDTH_CH","Math","max","map","status","label","length","JobCard","workspaceId","job","selectedJobExecution","selectedAttemptId","onSelectedJobExecutionChange","onSelectedAttemptChange","renderExpandedStep","sourcePanelId","sourceAvailable","focusedSourceStepId","onOpenStepSource","titleId","sourceButtonRef","localSelectedAttempt","setLocalSelectedAttempt","jobExecutionId","undefined","attemptId","selectedExecutionStatus","effectiveSelectedAttemptId","id","selectedSourceAction","selectedStepSourceAction","defaultRenderExpandedStep","step","stepId","attempt","attemptError","attemptStatus","carriedOver","CarriedOverStepPanel","StepAttemptDetailPanel","selectAttempt","section","aria-labelledby","className","div","mode","jobExecutions","as","size","bold","displayName","JobStatusBadge","variant","ref","type","aria-controls","aria-expanded","onClick","location","current","JobExecutionMetadata","execution","jobExecution","autoSelectActiveAttempt","emptyState","emptyStateForJob","showHeader","JobExecutionEmptyState","visual","badge","span","style","width","emptyStateForMissingExecution","icon","title","description","queueTime","runTime","executionStartedAt","startedAt","queuedAt","createdAt","items","triggerEvents","key","kind","value","time","filter","item","index","MetadataSeparator","JobExecutionTriggerMetadata","JobExecutionMetadataItem","triggerEvent","triggerLabel","triggerSource","source","event","triggerDisplayLabel","triggerCount","countLabel","tooltip","asChild","button","aria-label","provider","aria-hidden","jobExecutionTimeVerb","name","state","steps","selected","attempts","some","sourceLocation","selectedAttemptError","toSelectedAttemptError","error","isAgentConfigFailure","config","agentConfig","rawReason","reason","parsedReason","has","rawAgentConfigIssue","agentConfigIssue","agent_config_issue","exitCode","exit_code","resolvedReason","message","signal","category","sequence","listenerStatus","skippedJobDescription","statusReason"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/workflow-run-view/job-card.tsx"],"sourcesContent":["import {TriggerSourceIcon} from '@shipfox/client-triggers';\nimport {Badge} from '@shipfox/react-ui/badge';\nimport {Button} from '@shipfox/react-ui/button';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {RelativeTime} from '@shipfox/react-ui/relative-time';\nimport {useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Text} from '@shipfox/react-ui/typography';\nimport type {ReactNode} from 'react';\nimport {Fragment, useId, useRef, useState} from 'react';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {\n AGENT_CONFIG_ISSUES,\n defaultJobExecution,\n deriveJobDisplayStatus,\n deriveJobExecutionDisplayStatus,\n type Job,\n type JobDisplayStatus,\n type JobExecution,\n type JobExecutionTime,\n STEP_ERROR_REASONS,\n type Step,\n type StepError,\n type StepSourceLocation,\n WORKFLOW_JOB_STATUSES,\n workflowRunTriggerDisplayLabel,\n workflowRunTriggerLabel,\n} from '#core/workflow-run.js';\nimport {type StepExpandedContext, StepList, type StepListEmptyState} from '../step-list/index.js';\nimport {AgentConfigFailureCallout} from './agent-config-failure-callout.js';\nimport {JobExecutionSwitcher} from './job-execution-switcher.js';\nimport {formatJobExecutionTime, JobExecutionTimeText} from './job-execution-time-text.js';\nimport {StepAttemptLogPanel} from './step-attempt-log-panel.js';\n\nconst STATUS_BADGE_LABEL_WIDTH_CH = Math.max(\n ...[...WORKFLOW_JOB_STATUSES, 'listening' as const].map(\n (status) => getWorkflowStatusVisual(status).label.length,\n ),\n);\n\ninterface LocalSelectedAttempt {\n jobExecutionId: string | undefined;\n attemptId: string | null;\n}\n\nexport function JobCard({\n workspaceSlug,\n job,\n selectedJobExecution,\n selectedAttemptId,\n onSelectedJobExecutionChange,\n onSelectedAttemptChange,\n renderExpandedStep,\n sourcePanelId,\n sourceAvailable,\n focusedSourceStepId,\n onOpenStepSource,\n}: {\n workspaceSlug?: string | undefined;\n job: Job;\n selectedJobExecution: JobExecution | undefined;\n selectedAttemptId: string | null | undefined;\n onSelectedJobExecutionChange: ((jobExecutionId: string | undefined) => void) | undefined;\n onSelectedAttemptChange: ((attemptId: string | undefined) => void) | undefined;\n renderExpandedStep?: ((context: StepExpandedContext) => ReactNode) | undefined;\n sourcePanelId?: string | undefined;\n sourceAvailable?: boolean | undefined;\n focusedSourceStepId?: string | null | undefined;\n onOpenStepSource?:\n | ((stepId: string, location: StepSourceLocation, trigger: HTMLButtonElement | null) => void)\n | undefined;\n}) {\n const titleId = useId();\n const sourceButtonRef = useRef<HTMLButtonElement>(null);\n const [localSelectedAttempt, setLocalSelectedAttempt] = useState<LocalSelectedAttempt>({\n jobExecutionId: undefined,\n attemptId: null,\n });\n const title = selectedJobExecution?.displayName ?? job.displayName;\n const isNonDefaultExecutionSelected =\n selectedJobExecution !== undefined && selectedJobExecution.id !== defaultJobExecution(job)?.id;\n const selectedExecutionStatus = isNonDefaultExecutionSelected\n ? deriveJobExecutionDisplayStatus(selectedJobExecution)\n : deriveJobDisplayStatus(job);\n const effectiveSelectedAttemptId =\n selectedAttemptId === undefined &&\n localSelectedAttempt.jobExecutionId === selectedJobExecution?.id\n ? localSelectedAttempt.attemptId\n : selectedAttemptId;\n const selectedSourceAction = selectedStepSourceAction(\n selectedJobExecution,\n effectiveSelectedAttemptId,\n );\n const defaultRenderExpandedStep = ({\n step,\n stepId,\n attempt,\n attemptError,\n attemptStatus,\n carriedOver,\n }: StepExpandedContext) =>\n carriedOver ? (\n <CarriedOverStepPanel />\n ) : (\n <StepAttemptDetailPanel\n workspaceSlug={workspaceSlug}\n step={step}\n stepId={stepId}\n attempt={attempt}\n attemptError={attemptError}\n attemptStatus={attemptStatus}\n />\n );\n\n function selectAttempt(attemptId: string | undefined) {\n if (selectedAttemptId === undefined) {\n setLocalSelectedAttempt({\n jobExecutionId: selectedJobExecution?.id,\n attemptId: attemptId ?? null,\n });\n }\n\n onSelectedAttemptChange?.(attemptId);\n }\n\n return (\n <section\n aria-labelledby={titleId}\n className=\"flex min-h-0 flex-col rounded-8 border border-border-neutral-base bg-background-components-base\"\n >\n <div className=\"grid min-h-52 min-w-0 grid-cols-1 gap-y-4 px-16 py-12\">\n <div className=\"flex min-w-0 flex-wrap items-center justify-between gap-10\">\n {job.mode === 'listening' &&\n job.jobExecutions.length > 0 &&\n onSelectedJobExecutionChange ? (\n <>\n <Text as=\"h2\" id={titleId} size=\"sm\" bold className=\"sr-only\">\n {title}\n </Text>\n <div className=\"flex min-w-0 items-center gap-8\">\n <JobStatusBadge status={selectedExecutionStatus} />\n <JobExecutionSwitcher\n job={job}\n selectedJobExecution={selectedJobExecution?.id ?? null}\n onSelectedJobExecutionChange={onSelectedJobExecutionChange}\n variant=\"title\"\n />\n </div>\n </>\n ) : (\n <div className=\"flex min-w-0 items-center gap-8\">\n <JobStatusBadge status={selectedExecutionStatus} />\n <div className=\"flex min-w-0 items-center gap-8\">\n <Text\n as=\"h2\"\n id={titleId}\n size=\"sm\"\n bold\n className=\"min-w-0 truncate text-foreground-neutral-base\"\n >\n {title}\n </Text>\n </div>\n </div>\n )}\n {sourceAvailable && selectedSourceAction && sourcePanelId && onOpenStepSource ? (\n <div className=\"flex min-w-max items-center gap-6\">\n <Button\n ref={sourceButtonRef}\n type=\"button\"\n variant=\"secondary\"\n size=\"xs\"\n aria-controls={sourcePanelId}\n aria-expanded={focusedSourceStepId === selectedSourceAction.stepId}\n onClick={() =>\n onOpenStepSource(\n selectedSourceAction.stepId,\n selectedSourceAction.location,\n sourceButtonRef.current,\n )\n }\n >\n View source\n </Button>\n </div>\n ) : null}\n </div>\n <JobExecutionMetadata execution={selectedJobExecution} />\n </div>\n <div className=\"min-h-0 border-t border-border-neutral-strong\">\n {selectedJobExecution ? (\n <StepList\n job={job}\n jobExecution={selectedJobExecution}\n selectedAttemptId={selectedAttemptId}\n onSelectedAttemptChange={selectAttempt}\n autoSelectActiveAttempt\n emptyState={emptyStateForJob(job, selectedJobExecution)}\n showHeader={false}\n className=\"rounded-none border-0 bg-transparent\"\n renderExpandedStep={renderExpandedStep ?? defaultRenderExpandedStep}\n />\n ) : (\n <JobExecutionEmptyState job={job} />\n )}\n </div>\n </section>\n );\n}\n\nfunction JobStatusBadge({status}: {status: JobDisplayStatus}) {\n const visual = getWorkflowStatusVisual(status);\n\n return (\n <Badge variant={visual.badge} size=\"xs\">\n <span className=\"text-center\" style={{width: `${STATUS_BADGE_LABEL_WIDTH_CH}ch`}}>\n {visual.label}\n </span>\n </Badge>\n );\n}\n\nfunction JobExecutionEmptyState({job}: {job: Job}) {\n const emptyState = emptyStateForMissingExecution(job);\n\n return (\n <EmptyState\n className=\"min-h-120 px-16 py-20\"\n icon=\"componentLine\"\n title={emptyState.title}\n description={emptyState.description}\n variant=\"compact\"\n />\n );\n}\n\nfunction JobExecutionMetadata({execution}: {execution: JobExecution | undefined}) {\n if (!execution) return null;\n\n const queueTime = execution.queueTime;\n const runTime = execution.runTime;\n const executionStartedAt = execution.startedAt ?? execution.queuedAt ?? execution.createdAt;\n const items = [\n execution.triggerEvents.length > 0 ? {key: 'trigger', kind: 'trigger' as const} : null,\n {key: 'started', kind: 'started' as const, value: executionStartedAt},\n queueTime\n ? {key: 'queue', icon: 'hourglassLine' as const, kind: 'queue' as const, time: queueTime}\n : null,\n runTime ? {key: 'run', icon: 'timerLine' as const, kind: 'run' as const, time: runTime} : null,\n ].filter((item) => item !== null);\n\n if (items.length === 0) return null;\n\n return (\n <div className=\"flex min-w-0 items-center gap-12 overflow-hidden text-foreground-neutral-muted\">\n {items.map((item, index) => (\n <Fragment key={item.key}>\n {index > 0 ? <MetadataSeparator /> : null}\n {item.kind === 'started' ? (\n <RelativeTime\n value={item.value}\n className=\"shrink-0 whitespace-nowrap text-xs leading-20 text-foreground-neutral-muted\"\n />\n ) : item.kind === 'trigger' ? (\n <JobExecutionTriggerMetadata execution={execution} />\n ) : (\n <JobExecutionMetadataItem\n icon={item.icon}\n kind={item.kind}\n time={item.time}\n displayStatus={deriveJobExecutionDisplayStatus(execution)}\n />\n )}\n </Fragment>\n ))}\n </div>\n );\n}\n\nfunction JobExecutionTriggerMetadata({execution}: {execution: JobExecution}) {\n const triggerEvent = execution.triggerEvents[0];\n if (!triggerEvent) return null;\n\n const triggerLabel = workflowRunTriggerLabel({\n triggerSource: triggerEvent.source,\n triggerEvent: triggerEvent.event,\n });\n const triggerDisplayLabel = workflowRunTriggerDisplayLabel({\n triggerSource: triggerEvent.source,\n triggerEvent: triggerEvent.event,\n });\n const triggerCount = execution.triggerEvents.length;\n const countLabel = triggerCount > 1 ? ` (${triggerCount})` : '';\n const tooltip = triggerCount > 1 ? `${triggerLabel} (${triggerCount} events)` : triggerLabel;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={tooltip}\n className=\"inline-flex min-w-0 max-w-full cursor-default items-center gap-4 rounded-4 border-0 bg-transparent p-0 text-left text-foreground-neutral-muted outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <TriggerSourceIcon\n provider={triggerEvent.source}\n source={triggerEvent.source}\n aria-hidden=\"true\"\n className=\"size-12 shrink-0\"\n />\n <Text as=\"span\" size=\"xs\" className=\"min-w-0 truncate\">\n {triggerDisplayLabel}\n {countLabel}\n </Text>\n </button>\n </TooltipTrigger>\n <TooltipContent>\n <Text as=\"span\" size=\"xs\" className=\"block max-w-[360px] break-words\">\n {tooltip}\n </Text>\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction JobExecutionMetadataItem({\n icon,\n kind,\n time,\n displayStatus,\n}: {\n icon: 'hourglassLine' | 'timerLine';\n kind: 'queue' | 'run';\n time: JobExecutionTime;\n displayStatus: JobExecution['status'];\n}) {\n useTimeTick();\n\n const tooltip = `${jobExecutionTimeVerb(kind, time, displayStatus)} for ${formatJobExecutionTime(time)}`;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={tooltip}\n className=\"inline-flex min-w-0 cursor-default items-center gap-4 rounded-4 border-0 bg-transparent p-0 text-foreground-neutral-muted outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <Icon name={icon} className=\"size-12 shrink-0\" aria-hidden=\"true\" />\n <span className=\"shrink-0 text-xs leading-20 text-foreground-neutral-muted tabular-nums\">\n <JobExecutionTimeText time={time} />\n </span>\n </button>\n </TooltipTrigger>\n <TooltipContent>\n <Text as=\"span\" size=\"xs\">\n {tooltip}\n </Text>\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction jobExecutionTimeVerb(\n kind: 'queue' | 'run',\n time: JobExecutionTime,\n displayStatus: JobExecution['status'],\n): string {\n if (kind === 'queue') return time.state === 'live' ? 'Queueing' : 'Queued';\n if (time.state !== 'live') return 'Ran';\n if (displayStatus === 'running') return 'Running';\n return displayStatus === 'pending' ? 'Runner assigned' : 'Execution elapsed';\n}\n\nfunction MetadataSeparator() {\n return <span aria-hidden=\"true\" className=\"h-12 w-px shrink-0 bg-border-neutral-strong\" />;\n}\n\nfunction selectedStepSourceAction(\n jobExecution: JobExecution | undefined,\n selectedAttemptId: string | null | undefined,\n): {stepId: string; location: StepSourceLocation} | null {\n if (!jobExecution || !selectedAttemptId) return null;\n\n for (const step of jobExecution.steps) {\n const selected =\n selectedAttemptId === `carried-over:${step.id}` ||\n step.attempts.some((attempt) => attempt.id === selectedAttemptId);\n if (selected && step.sourceLocation) {\n return {stepId: step.id, location: step.sourceLocation};\n }\n }\n\n return null;\n}\n\nfunction StepAttemptDetailPanel({\n workspaceSlug,\n step,\n stepId,\n attempt,\n attemptError,\n attemptStatus,\n}: {\n workspaceSlug?: string | undefined;\n step: Step;\n stepId: string;\n attempt: number;\n attemptError: Record<string, unknown> | null;\n attemptStatus: string;\n}) {\n const selectedAttemptError = toSelectedAttemptError(step, attemptError) ?? step.error;\n\n return (\n <div className=\"flex min-w-0 flex-col gap-10\">\n {isAgentConfigFailure(step, selectedAttemptError) ? (\n <AgentConfigFailureCallout\n workspaceSlug={workspaceSlug}\n config={step.agentConfig}\n error={selectedAttemptError}\n />\n ) : null}\n <StepAttemptLogPanel stepId={stepId} attempt={attempt} attemptStatus={attemptStatus} />\n </div>\n );\n}\n\nfunction toSelectedAttemptError(\n step: Step,\n error: Record<string, unknown> | null,\n): StepError | null {\n if (error === null) return null;\n\n const rawReason = error.reason;\n const parsedReason =\n typeof rawReason === 'string' &&\n STEP_ERROR_REASONS.has(rawReason as StepError['reason'] & string)\n ? (rawReason as NonNullable<StepError['reason']>)\n : undefined;\n const rawAgentConfigIssue = error.agentConfigIssue ?? error.agent_config_issue;\n const agentConfigIssue =\n typeof rawAgentConfigIssue === 'string' &&\n AGENT_CONFIG_ISSUES.has(rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)\n ? (rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)\n : undefined;\n const exitCode = error.exitCode ?? error.exit_code;\n const resolvedReason = parsedReason\n ? parsedReason\n : agentConfigIssue\n ? 'agent_config_invalid'\n : undefined;\n\n if (resolvedReason === undefined) return null;\n\n return {\n message: typeof error.message === 'string' ? error.message : '',\n exitCode: exitCode === null || typeof exitCode === 'number' ? exitCode : null,\n signal: typeof error.signal === 'string' ? error.signal : undefined,\n reason: resolvedReason,\n agentConfigIssue,\n category: step.type === 'setup' || step.type === 'checkout' ? 'setup' : 'user',\n };\n}\n\nfunction isAgentConfigFailure(step: Step, error: StepError | null): boolean {\n return step.type === 'agent' && error?.reason === 'agent_config_invalid';\n}\n\nfunction emptyStateForJob(job: Job, jobExecution: JobExecution): StepListEmptyState | undefined {\n if (job.carriedOver) {\n return {\n title: 'Carried over from a previous attempt',\n description: 'This job did not execute in this run.',\n status: 'succeeded',\n };\n }\n\n const displayStatus =\n job.mode === 'listening'\n ? deriveJobExecutionDisplayStatus(jobExecution)\n : deriveJobDisplayStatus(job);\n\n if (jobExecution.status === 'running' && displayStatus === 'pending') {\n return {\n title: 'Runner preparing job',\n description: 'A runner is assigned. Steps will appear here when work begins.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'pending') {\n return {\n title: 'Waiting for this job to start',\n description: 'Steps will appear here once the job starts.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'running') {\n return {\n title: 'Waiting for the first step',\n description: 'The first running step will appear here shortly.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'cancelled') {\n return {\n title: 'Cancelled before start',\n description: 'This job was cancelled before any step started.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'succeeded' || displayStatus === 'failed') {\n return {\n title: 'No steps recorded',\n description: `Execution #${jobExecution.sequence} finished without recorded steps.`,\n status: displayStatus,\n };\n }\n\n return undefined;\n}\n\nfunction emptyStateForMissingExecution(job: Job): StepListEmptyState {\n if (job.carriedOver) {\n return {\n title: 'Carried over from a previous attempt',\n description: 'This job did not execute in this run.',\n status: 'succeeded',\n };\n }\n\n if (job.mode === 'listening' && job.listenerStatus === 'listening') {\n return {\n title: 'Waiting for trigger events',\n description: 'Matching trigger events will create job executions here.',\n status: deriveJobDisplayStatus(job),\n };\n }\n\n if (job.mode === 'listening' && job.listenerStatus === 'resolved') {\n return {\n title: 'Listener resolved without executions',\n description: 'No matching trigger event created a job execution before the listener stopped.',\n status: job.status,\n };\n }\n\n if (job.status === 'pending') {\n return {\n title: 'Waiting for this job to start',\n description: 'No execution has been created for this job yet.',\n status: 'pending',\n };\n }\n\n if (job.status === 'skipped') {\n return {\n title: 'This job was skipped',\n description: skippedJobDescription(job.statusReason),\n status: 'skipped',\n };\n }\n\n if (job.status === 'cancelled') {\n return {\n title: 'Cancelled before start',\n description: 'This job was cancelled before an execution was created.',\n status: 'cancelled',\n };\n }\n\n return {\n title: 'Execution details unavailable',\n description: 'This job finished, but no job execution record is available.',\n status: job.status,\n };\n}\n\nfunction CarriedOverStepPanel() {\n return (\n <EmptyState\n className=\"min-h-120 rounded-8 border border-border-neutral-base bg-background-components-base px-16 py-20\"\n icon=\"componentLine\"\n title=\"Carried over from a previous attempt\"\n description=\"Not executed in this run.\"\n variant=\"compact\"\n />\n );\n}\n\nfunction skippedJobDescription(reason: Job['statusReason']): string {\n switch (reason) {\n case 'dependency_not_completed':\n return 'A required job did not complete, so this job was skipped.';\n case 'default_gate_rejected':\n return 'A required job did not succeed, so this job was skipped.';\n case 'condition_false':\n case 'condition_rejected':\n return 'The job condition did not match, so this job was skipped.';\n case 'condition_errored':\n return 'The job condition could not be evaluated, so this job was skipped.';\n case 'user_cancelled':\n case 'run_cancelled':\n case 'timed_out':\n case 'runner_lost':\n case 'step_failed':\n case 'unknown':\n case null:\n return 'This job did not start.';\n }\n}\n"],"names":["TriggerSourceIcon","Badge","Button","EmptyState","Icon","RelativeTime","useTimeTick","Tooltip","TooltipContent","TooltipTrigger","Text","Fragment","useId","useRef","useState","getWorkflowStatusVisual","AGENT_CONFIG_ISSUES","defaultJobExecution","deriveJobDisplayStatus","deriveJobExecutionDisplayStatus","STEP_ERROR_REASONS","WORKFLOW_JOB_STATUSES","workflowRunTriggerDisplayLabel","workflowRunTriggerLabel","StepList","AgentConfigFailureCallout","JobExecutionSwitcher","formatJobExecutionTime","JobExecutionTimeText","StepAttemptLogPanel","STATUS_BADGE_LABEL_WIDTH_CH","Math","max","map","status","label","length","JobCard","workspaceSlug","job","selectedJobExecution","selectedAttemptId","onSelectedJobExecutionChange","onSelectedAttemptChange","renderExpandedStep","sourcePanelId","sourceAvailable","focusedSourceStepId","onOpenStepSource","titleId","sourceButtonRef","localSelectedAttempt","setLocalSelectedAttempt","jobExecutionId","undefined","attemptId","title","displayName","isNonDefaultExecutionSelected","id","selectedExecutionStatus","effectiveSelectedAttemptId","selectedSourceAction","selectedStepSourceAction","defaultRenderExpandedStep","step","stepId","attempt","attemptError","attemptStatus","carriedOver","CarriedOverStepPanel","StepAttemptDetailPanel","selectAttempt","section","aria-labelledby","className","div","mode","jobExecutions","as","size","bold","JobStatusBadge","variant","ref","type","aria-controls","aria-expanded","onClick","location","current","JobExecutionMetadata","execution","jobExecution","autoSelectActiveAttempt","emptyState","emptyStateForJob","showHeader","JobExecutionEmptyState","visual","badge","span","style","width","emptyStateForMissingExecution","icon","description","queueTime","runTime","executionStartedAt","startedAt","queuedAt","createdAt","items","triggerEvents","key","kind","value","time","filter","item","index","MetadataSeparator","JobExecutionTriggerMetadata","JobExecutionMetadataItem","displayStatus","triggerEvent","triggerLabel","triggerSource","source","event","triggerDisplayLabel","triggerCount","countLabel","tooltip","asChild","button","aria-label","provider","aria-hidden","jobExecutionTimeVerb","name","state","steps","selected","attempts","some","sourceLocation","selectedAttemptError","toSelectedAttemptError","error","isAgentConfigFailure","config","agentConfig","rawReason","reason","parsedReason","has","rawAgentConfigIssue","agentConfigIssue","agent_config_issue","exitCode","exit_code","resolvedReason","message","signal","category","sequence","listenerStatus","skippedJobDescription","statusReason"],"mappings":";AAAA,SAAQA,iBAAiB,QAAO,2BAA2B;AAC3D,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,MAAM,QAAO,2BAA2B;AAChD,SAAQC,UAAU,QAAO,gCAAgC;AACzD,SAAQC,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,YAAY,QAAO,kCAAkC;AAC7D,SAAQC,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,OAAO,EAAEC,cAAc,EAAEC,cAAc,QAAO,4BAA4B;AAClF,SAAQC,IAAI,QAAO,+BAA+B;AAElD,SAAQC,QAAQ,EAAEC,KAAK,EAAEC,MAAM,EAAEC,QAAQ,QAAO,QAAQ;AACxD,SAAQC,uBAAuB,QAAO,gDAAgD;AACtF,SACEC,mBAAmB,EACnBC,mBAAmB,EACnBC,sBAAsB,EACtBC,+BAA+B,EAK/BC,kBAAkB,EAIlBC,qBAAqB,EACrBC,8BAA8B,EAC9BC,uBAAuB,QAClB,wBAAwB;AAC/B,SAAkCC,QAAQ,QAAgC,wBAAwB;AAClG,SAAQC,yBAAyB,QAAO,oCAAoC;AAC5E,SAAQC,oBAAoB,QAAO,8BAA8B;AACjE,SAAQC,sBAAsB,EAAEC,oBAAoB,QAAO,+BAA+B;AAC1F,SAAQC,mBAAmB,QAAO,8BAA8B;AAEhE,MAAMC,8BAA8BC,KAAKC,GAAG,IACvC;OAAIX;IAAuB;CAAqB,CAACY,GAAG,CACrD,CAACC,SAAWnB,wBAAwBmB,QAAQC,KAAK,CAACC,MAAM;AAS5D,OAAO,SAASC,QAAQ,EACtBC,aAAa,EACbC,GAAG,EACHC,oBAAoB,EACpBC,iBAAiB,EACjBC,4BAA4B,EAC5BC,uBAAuB,EACvBC,kBAAkB,EAClBC,aAAa,EACbC,eAAe,EACfC,mBAAmB,EACnBC,gBAAgB,EAejB;IACC,MAAMC,UAAUrC;IAChB,MAAMsC,kBAAkBrC,OAA0B;IAClD,MAAM,CAACsC,sBAAsBC,wBAAwB,GAAGtC,SAA+B;QACrFuC,gBAAgBC;QAChBC,WAAW;IACb;IACA,MAAMC,QAAQhB,sBAAsBiB,eAAelB,IAAIkB,WAAW;IAClE,MAAMC,gCACJlB,yBAAyBc,aAAad,qBAAqBmB,EAAE,KAAK1C,oBAAoBsB,MAAMoB;IAC9F,MAAMC,0BAA0BF,gCAC5BvC,gCAAgCqB,wBAChCtB,uBAAuBqB;IAC3B,MAAMsB,6BACJpB,sBAAsBa,aACtBH,qBAAqBE,cAAc,KAAKb,sBAAsBmB,KAC1DR,qBAAqBI,SAAS,GAC9Bd;IACN,MAAMqB,uBAAuBC,yBAC3BvB,sBACAqB;IAEF,MAAMG,4BAA4B,CAAC,EACjCC,IAAI,EACJC,MAAM,EACNC,OAAO,EACPC,YAAY,EACZC,aAAa,EACbC,WAAW,EACS,GACpBA,4BACE,KAACC,0CAED,KAACC;YACClC,eAAeA;YACf2B,MAAMA;YACNC,QAAQA;YACRC,SAASA;YACTC,cAAcA;YACdC,eAAeA;;IAIrB,SAASI,cAAclB,SAA6B;QAClD,IAAId,sBAAsBa,WAAW;YACnCF,wBAAwB;gBACtBC,gBAAgBb,sBAAsBmB;gBACtCJ,WAAWA,aAAa;YAC1B;QACF;QAEAZ,0BAA0BY;IAC5B;IAEA,qBACE,MAACmB;QACCC,mBAAiB1B;QACjB2B,WAAU;;0BAEV,MAACC;gBAAID,WAAU;;kCACb,MAACC;wBAAID,WAAU;;4BACZrC,IAAIuC,IAAI,KAAK,eACdvC,IAAIwC,aAAa,CAAC3C,MAAM,GAAG,KAC3BM,6CACE;;kDACE,KAAChC;wCAAKsE,IAAG;wCAAKrB,IAAIV;wCAASgC,MAAK;wCAAKC,IAAI;wCAACN,WAAU;kDACjDpB;;kDAEH,MAACqB;wCAAID,WAAU;;0DACb,KAACO;gDAAejD,QAAQ0B;;0DACxB,KAAClC;gDACCa,KAAKA;gDACLC,sBAAsBA,sBAAsBmB,MAAM;gDAClDjB,8BAA8BA;gDAC9B0C,SAAQ;;;;;+CAKd,MAACP;gCAAID,WAAU;;kDACb,KAACO;wCAAejD,QAAQ0B;;kDACxB,KAACiB;wCAAID,WAAU;kDACb,cAAA,KAAClE;4CACCsE,IAAG;4CACHrB,IAAIV;4CACJgC,MAAK;4CACLC,IAAI;4CACJN,WAAU;sDAETpB;;;;;4BAKRV,mBAAmBgB,wBAAwBjB,iBAAiBG,iCAC3D,KAAC6B;gCAAID,WAAU;0CACb,cAAA,KAAC1E;oCACCmF,KAAKnC;oCACLoC,MAAK;oCACLF,SAAQ;oCACRH,MAAK;oCACLM,iBAAe1C;oCACf2C,iBAAezC,wBAAwBe,qBAAqBI,MAAM;oCAClEuB,SAAS,IACPzC,iBACEc,qBAAqBI,MAAM,EAC3BJ,qBAAqB4B,QAAQ,EAC7BxC,gBAAgByC,OAAO;8CAG5B;;iCAID;;;kCAEN,KAACC;wBAAqBC,WAAWrD;;;;0BAEnC,KAACqC;gBAAID,WAAU;0BACZpC,qCACC,KAAChB;oBACCe,KAAKA;oBACLuD,cAActD;oBACdC,mBAAmBA;oBACnBE,yBAAyB8B;oBACzBsB,uBAAuB;oBACvBC,YAAYC,iBAAiB1D,KAAKC;oBAClC0D,YAAY;oBACZtB,WAAU;oBACVhC,oBAAoBA,sBAAsBoB;mCAG5C,KAACmC;oBAAuB5D,KAAKA;;;;;AAKvC;AAEA,SAAS4C,eAAe,EAACjD,MAAM,EAA6B;IAC1D,MAAMkE,SAASrF,wBAAwBmB;IAEvC,qBACE,KAACjC;QAAMmF,SAASgB,OAAOC,KAAK;QAAEpB,MAAK;kBACjC,cAAA,KAACqB;YAAK1B,WAAU;YAAc2B,OAAO;gBAACC,OAAO,GAAG1E,4BAA4B,EAAE,CAAC;YAAA;sBAC5EsE,OAAOjE,KAAK;;;AAIrB;AAEA,SAASgE,uBAAuB,EAAC5D,GAAG,EAAa;IAC/C,MAAMyD,aAAaS,8BAA8BlE;IAEjD,qBACE,KAACpC;QACCyE,WAAU;QACV8B,MAAK;QACLlD,OAAOwC,WAAWxC,KAAK;QACvBmD,aAAaX,WAAWW,WAAW;QACnCvB,SAAQ;;AAGd;AAEA,SAASQ,qBAAqB,EAACC,SAAS,EAAwC;IAC9E,IAAI,CAACA,WAAW,OAAO;IAEvB,MAAMe,YAAYf,UAAUe,SAAS;IACrC,MAAMC,UAAUhB,UAAUgB,OAAO;IACjC,MAAMC,qBAAqBjB,UAAUkB,SAAS,IAAIlB,UAAUmB,QAAQ,IAAInB,UAAUoB,SAAS;IAC3F,MAAMC,QAAQ;QACZrB,UAAUsB,aAAa,CAAC/E,MAAM,GAAG,IAAI;YAACgF,KAAK;YAAWC,MAAM;QAAkB,IAAI;QAClF;YAACD,KAAK;YAAWC,MAAM;YAAoBC,OAAOR;QAAkB;QACpEF,YACI;YAACQ,KAAK;YAASV,MAAM;YAA0BW,MAAM;YAAkBE,MAAMX;QAAS,IACtF;QACJC,UAAU;YAACO,KAAK;YAAOV,MAAM;YAAsBW,MAAM;YAAgBE,MAAMV;QAAO,IAAI;KAC3F,CAACW,MAAM,CAAC,CAACC,OAASA,SAAS;IAE5B,IAAIP,MAAM9E,MAAM,KAAK,GAAG,OAAO;IAE/B,qBACE,KAACyC;QAAID,WAAU;kBACZsC,MAAMjF,GAAG,CAAC,CAACwF,MAAMC,sBAChB,MAAC/G;;oBACE+G,QAAQ,kBAAI,KAACC,yBAAuB;oBACpCF,KAAKJ,IAAI,KAAK,0BACb,KAAChH;wBACCiH,OAAOG,KAAKH,KAAK;wBACjB1C,WAAU;yBAEV6C,KAAKJ,IAAI,KAAK,0BAChB,KAACO;wBAA4B/B,WAAWA;uCAExC,KAACgC;wBACCnB,MAAMe,KAAKf,IAAI;wBACfW,MAAMI,KAAKJ,IAAI;wBACfE,MAAME,KAAKF,IAAI;wBACfO,eAAe3G,gCAAgC0E;;;eAdtC4B,KAAKL,GAAG;;AAqB/B;AAEA,SAASQ,4BAA4B,EAAC/B,SAAS,EAA4B;IACzE,MAAMkC,eAAelC,UAAUsB,aAAa,CAAC,EAAE;IAC/C,IAAI,CAACY,cAAc,OAAO;IAE1B,MAAMC,eAAezG,wBAAwB;QAC3C0G,eAAeF,aAAaG,MAAM;QAClCH,cAAcA,aAAaI,KAAK;IAClC;IACA,MAAMC,sBAAsB9G,+BAA+B;QACzD2G,eAAeF,aAAaG,MAAM;QAClCH,cAAcA,aAAaI,KAAK;IAClC;IACA,MAAME,eAAexC,UAAUsB,aAAa,CAAC/E,MAAM;IACnD,MAAMkG,aAAaD,eAAe,IAAI,CAAC,EAAE,EAAEA,aAAa,CAAC,CAAC,GAAG;IAC7D,MAAME,UAAUF,eAAe,IAAI,GAAGL,aAAa,EAAE,EAAEK,aAAa,QAAQ,CAAC,GAAGL;IAEhF,qBACE,MAACzH;;0BACC,KAACE;gBAAe+H,OAAO;0BACrB,cAAA,MAACC;oBACCnD,MAAK;oBACLoD,cAAYH;oBACZ3D,WAAU;;sCAEV,KAAC5E;4BACC2I,UAAUZ,aAAaG,MAAM;4BAC7BA,QAAQH,aAAaG,MAAM;4BAC3BU,eAAY;4BACZhE,WAAU;;sCAEZ,MAAClE;4BAAKsE,IAAG;4BAAOC,MAAK;4BAAKL,WAAU;;gCACjCwD;gCACAE;;;;;;0BAIP,KAAC9H;0BACC,cAAA,KAACE;oBAAKsE,IAAG;oBAAOC,MAAK;oBAAKL,WAAU;8BACjC2D;;;;;AAKX;AAEA,SAASV,yBAAyB,EAChCnB,IAAI,EACJW,IAAI,EACJE,IAAI,EACJO,aAAa,EAMd;IACCxH;IAEA,MAAMiI,UAAU,GAAGM,qBAAqBxB,MAAME,MAAMO,eAAe,KAAK,EAAEnG,uBAAuB4F,OAAO;IAExG,qBACE,MAAChH;;0BACC,KAACE;gBAAe+H,OAAO;0BACrB,cAAA,MAACC;oBACCnD,MAAK;oBACLoD,cAAYH;oBACZ3D,WAAU;;sCAEV,KAACxE;4BAAK0I,MAAMpC;4BAAM9B,WAAU;4BAAmBgE,eAAY;;sCAC3D,KAACtC;4BAAK1B,WAAU;sCACd,cAAA,KAAChD;gCAAqB2F,MAAMA;;;;;;0BAIlC,KAAC/G;0BACC,cAAA,KAACE;oBAAKsE,IAAG;oBAAOC,MAAK;8BAClBsD;;;;;AAKX;AAEA,SAASM,qBACPxB,IAAqB,EACrBE,IAAsB,EACtBO,aAAqC;IAErC,IAAIT,SAAS,SAAS,OAAOE,KAAKwB,KAAK,KAAK,SAAS,aAAa;IAClE,IAAIxB,KAAKwB,KAAK,KAAK,QAAQ,OAAO;IAClC,IAAIjB,kBAAkB,WAAW,OAAO;IACxC,OAAOA,kBAAkB,YAAY,oBAAoB;AAC3D;AAEA,SAASH;IACP,qBAAO,KAACrB;QAAKsC,eAAY;QAAOhE,WAAU;;AAC5C;AAEA,SAASb,yBACP+B,YAAsC,EACtCrD,iBAA4C;IAE5C,IAAI,CAACqD,gBAAgB,CAACrD,mBAAmB,OAAO;IAEhD,KAAK,MAAMwB,QAAQ6B,aAAakD,KAAK,CAAE;QACrC,MAAMC,WACJxG,sBAAsB,CAAC,aAAa,EAAEwB,KAAKN,EAAE,EAAE,IAC/CM,KAAKiF,QAAQ,CAACC,IAAI,CAAC,CAAChF,UAAYA,QAAQR,EAAE,KAAKlB;QACjD,IAAIwG,YAAYhF,KAAKmF,cAAc,EAAE;YACnC,OAAO;gBAAClF,QAAQD,KAAKN,EAAE;gBAAE+B,UAAUzB,KAAKmF,cAAc;YAAA;QACxD;IACF;IAEA,OAAO;AACT;AAEA,SAAS5E,uBAAuB,EAC9BlC,aAAa,EACb2B,IAAI,EACJC,MAAM,EACNC,OAAO,EACPC,YAAY,EACZC,aAAa,EAQd;IACC,MAAMgF,uBAAuBC,uBAAuBrF,MAAMG,iBAAiBH,KAAKsF,KAAK;IAErF,qBACE,MAAC1E;QAAID,WAAU;;YACZ4E,qBAAqBvF,MAAMoF,sCAC1B,KAAC5H;gBACCa,eAAeA;gBACfmH,QAAQxF,KAAKyF,WAAW;gBACxBH,OAAOF;iBAEP;0BACJ,KAACxH;gBAAoBqC,QAAQA;gBAAQC,SAASA;gBAASE,eAAeA;;;;AAG5E;AAEA,SAASiF,uBACPrF,IAAU,EACVsF,KAAqC;IAErC,IAAIA,UAAU,MAAM,OAAO;IAE3B,MAAMI,YAAYJ,MAAMK,MAAM;IAC9B,MAAMC,eACJ,OAAOF,cAAc,YACrBvI,mBAAmB0I,GAAG,CAACH,aAClBA,YACDrG;IACN,MAAMyG,sBAAsBR,MAAMS,gBAAgB,IAAIT,MAAMU,kBAAkB;IAC9E,MAAMD,mBACJ,OAAOD,wBAAwB,YAC/B/I,oBAAoB8I,GAAG,CAACC,uBACnBA,sBACDzG;IACN,MAAM4G,WAAWX,MAAMW,QAAQ,IAAIX,MAAMY,SAAS;IAClD,MAAMC,iBAAiBP,eACnBA,eACAG,mBACE,yBACA1G;IAEN,IAAI8G,mBAAmB9G,WAAW,OAAO;IAEzC,OAAO;QACL+G,SAAS,OAAOd,MAAMc,OAAO,KAAK,WAAWd,MAAMc,OAAO,GAAG;QAC7DH,UAAUA,aAAa,QAAQ,OAAOA,aAAa,WAAWA,WAAW;QACzEI,QAAQ,OAAOf,MAAMe,MAAM,KAAK,WAAWf,MAAMe,MAAM,GAAGhH;QAC1DsG,QAAQQ;QACRJ;QACAO,UAAUtG,KAAKqB,IAAI,KAAK,WAAWrB,KAAKqB,IAAI,KAAK,aAAa,UAAU;IAC1E;AACF;AAEA,SAASkE,qBAAqBvF,IAAU,EAAEsF,KAAuB;IAC/D,OAAOtF,KAAKqB,IAAI,KAAK,WAAWiE,OAAOK,WAAW;AACpD;AAEA,SAAS3D,iBAAiB1D,GAAQ,EAAEuD,YAA0B;IAC5D,IAAIvD,IAAI+B,WAAW,EAAE;QACnB,OAAO;YACLd,OAAO;YACPmD,aAAa;YACbzE,QAAQ;QACV;IACF;IAEA,MAAM4F,gBACJvF,IAAIuC,IAAI,KAAK,cACT3D,gCAAgC2E,gBAChC5E,uBAAuBqB;IAE7B,IAAIuD,aAAa5D,MAAM,KAAK,aAAa4F,kBAAkB,WAAW;QACpE,OAAO;YACLtE,OAAO;YACPmD,aAAa;YACbzE,QAAQ4F;QACV;IACF;IAEA,IAAIA,kBAAkB,WAAW;QAC/B,OAAO;YACLtE,OAAO;YACPmD,aAAa;YACbzE,QAAQ4F;QACV;IACF;IAEA,IAAIA,kBAAkB,WAAW;QAC/B,OAAO;YACLtE,OAAO;YACPmD,aAAa;YACbzE,QAAQ4F;QACV;IACF;IAEA,IAAIA,kBAAkB,aAAa;QACjC,OAAO;YACLtE,OAAO;YACPmD,aAAa;YACbzE,QAAQ4F;QACV;IACF;IAEA,IAAIA,kBAAkB,eAAeA,kBAAkB,UAAU;QAC/D,OAAO;YACLtE,OAAO;YACPmD,aAAa,CAAC,WAAW,EAAEb,aAAa0E,QAAQ,CAAC,iCAAiC,CAAC;YACnFtI,QAAQ4F;QACV;IACF;IAEA,OAAOxE;AACT;AAEA,SAASmD,8BAA8BlE,GAAQ;IAC7C,IAAIA,IAAI+B,WAAW,EAAE;QACnB,OAAO;YACLd,OAAO;YACPmD,aAAa;YACbzE,QAAQ;QACV;IACF;IAEA,IAAIK,IAAIuC,IAAI,KAAK,eAAevC,IAAIkI,cAAc,KAAK,aAAa;QAClE,OAAO;YACLjH,OAAO;YACPmD,aAAa;YACbzE,QAAQhB,uBAAuBqB;QACjC;IACF;IAEA,IAAIA,IAAIuC,IAAI,KAAK,eAAevC,IAAIkI,cAAc,KAAK,YAAY;QACjE,OAAO;YACLjH,OAAO;YACPmD,aAAa;YACbzE,QAAQK,IAAIL,MAAM;QACpB;IACF;IAEA,IAAIK,IAAIL,MAAM,KAAK,WAAW;QAC5B,OAAO;YACLsB,OAAO;YACPmD,aAAa;YACbzE,QAAQ;QACV;IACF;IAEA,IAAIK,IAAIL,MAAM,KAAK,WAAW;QAC5B,OAAO;YACLsB,OAAO;YACPmD,aAAa+D,sBAAsBnI,IAAIoI,YAAY;YACnDzI,QAAQ;QACV;IACF;IAEA,IAAIK,IAAIL,MAAM,KAAK,aAAa;QAC9B,OAAO;YACLsB,OAAO;YACPmD,aAAa;YACbzE,QAAQ;QACV;IACF;IAEA,OAAO;QACLsB,OAAO;QACPmD,aAAa;QACbzE,QAAQK,IAAIL,MAAM;IACpB;AACF;AAEA,SAASqC;IACP,qBACE,KAACpE;QACCyE,WAAU;QACV8B,MAAK;QACLlD,OAAM;QACNmD,aAAY;QACZvB,SAAQ;;AAGd;AAEA,SAASsF,sBAAsBd,MAA2B;IACxD,OAAQA;QACN,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;QACL,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;YACH,OAAO;IACX;AACF"}
@@ -1,4 +1,4 @@
1
- import type { Job } from '#core/workflow-run.js';
1
+ import { type Job } from '#core/workflow-run.js';
2
2
  export interface JobExecutionSwitcherProps {
3
3
  job: Job;
4
4
  selectedJobExecution: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"job-execution-switcher.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-view/job-execution-switcher.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAC,GAAG,EAAe,MAAM,uBAAuB,CAAC;AAG7D,MAAM,WAAW,yBAAyB;IACxC,GAAG,EAAE,GAAG,CAAC;IACT,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,4BAA4B,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/D,OAAO,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IAC1C,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,wBAAgB,oBAAoB,CAAC,EACnC,GAAG,EACH,oBAAoB,EACpB,4BAA4B,EAC5B,OAAmB,EACnB,SAAS,GACV,EAAE,yBAAyB,sCA0F3B"}
1
+ {"version":3,"file":"job-execution-switcher.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-view/job-execution-switcher.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAkC,KAAK,GAAG,EAAoB,MAAM,uBAAuB,CAAC;AAGnG,MAAM,WAAW,yBAAyB;IACxC,GAAG,EAAE,GAAG,CAAC;IACT,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,4BAA4B,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/D,OAAO,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IAC1C,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,wBAAgB,oBAAoB,CAAC,EACnC,GAAG,EACH,oBAAoB,EACpB,4BAA4B,EAC5B,OAAmB,EACnB,SAAS,GACV,EAAE,yBAAyB,sCA+F3B"}
@@ -4,8 +4,10 @@ import { Icon } from '@shipfox/react-ui/icon';
4
4
  import { Text } from '@shipfox/react-ui/typography';
5
5
  import { cn } from '@shipfox/react-ui/utils';
6
6
  import { useMemo } from 'react';
7
+ import { getWorkflowStatusVisual } from '#components/workflow-status/status-visuals.js';
7
8
  import { WorkflowStatusIcon } from '#components/workflow-status/workflow-status-icon.js';
8
- import { JobExecutionTimeText } from './job-execution-time-text.js';
9
+ import { deriveJobExecutionDisplayStatus } from '#core/workflow-run.js';
10
+ import { formatJobExecutionTime, JobExecutionTimeText } from './job-execution-time-text.js';
9
11
  export function JobExecutionSwitcher({ job, selectedJobExecution, onSelectedJobExecutionChange, variant = 'compact', className }) {
10
12
  const executions = useMemo(()=>[
11
13
  ...job.jobExecutions
@@ -26,10 +28,9 @@ export function JobExecutionSwitcher({ job, selectedJobExecution, onSelectedJobE
26
28
  children: [
27
29
  /*#__PURE__*/ _jsxs(DropdownMenuTrigger, {
28
30
  className: cn('inline-flex min-w-0 max-w-full items-center rounded-6 text-left transition-colors focus-visible:shadow-border-interactive-with-active focus-visible:outline-none', variant === 'title' ? '-mx-6 -my-4 gap-8 px-6 py-4 hover:bg-background-components-hover' : 'min-h-28 gap-6 px-8 py-4 text-sm leading-20 text-foreground-neutral-subtle hover:bg-background-components-hover', className),
29
- "aria-label": `Switch job execution, currently execution ${selected.sequence}`,
31
+ "aria-label": `Switch job execution, currently execution ${selected.sequence}: ${selected.displayName}`,
30
32
  children: [
31
33
  variant === 'title' ? /*#__PURE__*/ _jsx(TitleExecutionSummary, {
32
- job: job,
33
34
  execution: selected
34
35
  }) : /*#__PURE__*/ _jsx(ExecutionSummary, {
35
36
  execution: selected
@@ -60,10 +61,11 @@ export function JobExecutionSwitcher({ job, selectedJobExecution, onSelectedJobE
60
61
  children: /*#__PURE__*/ _jsxs("button", {
61
62
  type: "button",
62
63
  "aria-current": isSelected ? 'true' : undefined,
64
+ "aria-label": executionAccessibleLabel(jobExecution),
63
65
  className: "w-full text-left",
64
66
  children: [
65
67
  /*#__PURE__*/ _jsx(WorkflowStatusIcon, {
66
- status: jobExecution.status,
68
+ status: deriveJobExecutionDisplayStatus(jobExecution),
67
69
  size: 14,
68
70
  tooltip: false
69
71
  }),
@@ -76,7 +78,7 @@ export function JobExecutionSwitcher({ job, selectedJobExecution, onSelectedJobE
76
78
  }),
77
79
  /*#__PURE__*/ _jsx("span", {
78
80
  className: "min-w-0 truncate text-xs leading-20 text-foreground-neutral-base",
79
- children: job.displayName
81
+ children: jobExecution.displayName
80
82
  }),
81
83
  !isSelected && jobExecution.statusReason ? /*#__PURE__*/ _jsx("span", {
82
84
  className: "min-w-0 flex-1 truncate text-xs leading-20 text-foreground-neutral-muted",
@@ -101,7 +103,16 @@ export function JobExecutionSwitcher({ job, selectedJobExecution, onSelectedJobE
101
103
  ]
102
104
  });
103
105
  }
104
- function TitleExecutionSummary({ job, execution }) {
106
+ function executionAccessibleLabel(execution) {
107
+ const status = getWorkflowStatusVisual(deriveJobExecutionDisplayStatus(execution));
108
+ return [
109
+ `Execution #${execution.sequence}: ${execution.displayName}`,
110
+ status.label,
111
+ execution.statusReason ?? undefined,
112
+ execution.displayDuration ? `duration ${formatJobExecutionTime(execution.displayDuration)}` : undefined
113
+ ].filter((part)=>Boolean(part)).join(', ');
114
+ }
115
+ function TitleExecutionSummary({ execution }) {
105
116
  return /*#__PURE__*/ _jsxs("span", {
106
117
  className: "flex min-w-0 items-center gap-8",
107
118
  children: [
@@ -120,17 +131,18 @@ function TitleExecutionSummary({ job, execution }) {
120
131
  size: "sm",
121
132
  bold: true,
122
133
  className: "min-w-0 truncate text-foreground-neutral-base",
123
- children: job.displayName
134
+ children: execution.displayName
124
135
  })
125
136
  ]
126
137
  });
127
138
  }
128
139
  function ExecutionSummary({ execution }) {
140
+ const displayStatus = deriveJobExecutionDisplayStatus(execution);
129
141
  return /*#__PURE__*/ _jsxs("span", {
130
142
  className: "flex min-w-0 items-center gap-6",
131
143
  children: [
132
144
  /*#__PURE__*/ _jsx(WorkflowStatusIcon, {
133
- status: execution.status,
145
+ status: displayStatus,
134
146
  size: 14,
135
147
  tooltip: false
136
148
  }),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/workflow-run-view/job-execution-switcher.tsx"],"sourcesContent":["import {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {useMemo} from 'react';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport type {Job, JobExecution} from '#core/workflow-run.js';\nimport {JobExecutionTimeText} from './job-execution-time-text.js';\n\nexport interface JobExecutionSwitcherProps {\n job: Job;\n selectedJobExecution: string | null;\n onSelectedJobExecutionChange: (jobExecutionId: string) => void;\n variant?: 'compact' | 'title' | undefined;\n className?: string | undefined;\n}\n\nexport function JobExecutionSwitcher({\n job,\n selectedJobExecution,\n onSelectedJobExecutionChange,\n variant = 'compact',\n className,\n}: JobExecutionSwitcherProps) {\n const executions = useMemo(\n () => [...job.jobExecutions].sort((left, right) => right.sequence - left.sequence),\n [job.jobExecutions],\n );\n const selected =\n executions.find((jobExecution) => jobExecution.id === selectedJobExecution) ??\n executions[0] ??\n null;\n\n if (selected === null) return null;\n\n if (!job.executionCountVisible) {\n return (\n <div\n className={cn(\n 'flex min-w-0 items-center gap-6 text-sm leading-20 text-foreground-neutral-subtle',\n className,\n )}\n >\n <ExecutionSummary execution={selected} />\n </div>\n );\n }\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger\n className={cn(\n 'inline-flex min-w-0 max-w-full items-center rounded-6 text-left transition-colors focus-visible:shadow-border-interactive-with-active focus-visible:outline-none',\n variant === 'title'\n ? '-mx-6 -my-4 gap-8 px-6 py-4 hover:bg-background-components-hover'\n : 'min-h-28 gap-6 px-8 py-4 text-sm leading-20 text-foreground-neutral-subtle hover:bg-background-components-hover',\n className,\n )}\n aria-label={`Switch job execution, currently execution ${selected.sequence}`}\n >\n {variant === 'title' ? (\n <TitleExecutionSummary job={job} execution={selected} />\n ) : (\n <ExecutionSummary execution={selected} />\n )}\n <Icon name=\"arrowDownSLine\" className=\"size-14 shrink-0 text-foreground-neutral-muted\" />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" size=\"lg\" className=\"max-h-[320px] overflow-y-auto\">\n <DropdownMenuLabel>\n {executions.length} execution{executions.length === 1 ? '' : 's'}\n </DropdownMenuLabel>\n {executions.map((jobExecution) => {\n const isSelected = jobExecution.id === selected.id;\n\n return (\n <DropdownMenuItem\n key={jobExecution.id}\n asChild\n onSelect={() => onSelectedJobExecutionChange(jobExecution.id)}\n >\n <button\n type=\"button\"\n aria-current={isSelected ? 'true' : undefined}\n className=\"w-full text-left\"\n >\n <WorkflowStatusIcon status={jobExecution.status} size={14} tooltip={false} />\n <span className=\"font-code text-xs leading-20 text-foreground-neutral-base tabular-nums\">\n #{jobExecution.sequence}\n </span>\n <span className=\"min-w-0 truncate text-xs leading-20 text-foreground-neutral-base\">\n {job.displayName}\n </span>\n {!isSelected && jobExecution.statusReason ? (\n <span className=\"min-w-0 flex-1 truncate text-xs leading-20 text-foreground-neutral-muted\">\n {jobExecution.statusReason}\n </span>\n ) : (\n <span className=\"min-w-0 flex-1\" />\n )}\n {isSelected ? (\n <Icon name=\"check\" className=\"size-14 shrink-0 text-foreground-neutral-base\" />\n ) : null}\n <JobExecutionDuration\n execution={jobExecution}\n className=\"ml-auto shrink-0 font-code text-xs leading-20 text-foreground-neutral-muted tabular-nums\"\n />\n </button>\n </DropdownMenuItem>\n );\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\nfunction TitleExecutionSummary({job, execution}: {job: Job; execution: JobExecution}) {\n return (\n <span className=\"flex min-w-0 items-center gap-8\">\n <Text as=\"span\" size=\"sm\" bold className=\"shrink-0 text-foreground-neutral-base\">\n #{execution.sequence}\n </Text>\n <Text as=\"span\" size=\"sm\" bold className=\"min-w-0 truncate text-foreground-neutral-base\">\n {job.displayName}\n </Text>\n </span>\n );\n}\n\nfunction ExecutionSummary({execution}: {execution: JobExecution}) {\n return (\n <span className=\"flex min-w-0 items-center gap-6\">\n <WorkflowStatusIcon status={execution.status} size={14} tooltip={false} />\n <span className=\"shrink-0 font-code text-xs leading-20 text-foreground-neutral-base tabular-nums\">\n Execution #{execution.sequence}\n </span>\n {execution.statusReason ? (\n <Text as=\"span\" size=\"xs\" className=\"min-w-0 truncate text-foreground-neutral-muted\">\n · {execution.statusReason}\n </Text>\n ) : null}\n <JobExecutionDuration\n execution={execution}\n className=\"shrink-0 font-code text-xs leading-20 text-foreground-neutral-muted tabular-nums\"\n />\n </span>\n );\n}\n\nfunction JobExecutionDuration({\n execution,\n className,\n}: {\n execution: JobExecution;\n className?: string | undefined;\n}) {\n const duration = execution.displayDuration;\n if (!duration) return null;\n\n return (\n <span className={className}>\n <JobExecutionTimeText time={duration} />\n </span>\n );\n}\n"],"names":["DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuLabel","DropdownMenuTrigger","Icon","Text","cn","useMemo","WorkflowStatusIcon","JobExecutionTimeText","JobExecutionSwitcher","job","selectedJobExecution","onSelectedJobExecutionChange","variant","className","executions","jobExecutions","sort","left","right","sequence","selected","find","jobExecution","id","executionCountVisible","div","ExecutionSummary","execution","aria-label","TitleExecutionSummary","name","align","size","length","map","isSelected","asChild","onSelect","button","type","aria-current","undefined","status","tooltip","span","displayName","statusReason","JobExecutionDuration","as","bold","duration","displayDuration","time"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/workflow-run-view/job-execution-switcher.tsx"],"sourcesContent":["import {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {useMemo} from 'react';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {deriveJobExecutionDisplayStatus, type Job, type JobExecution} from '#core/workflow-run.js';\nimport {formatJobExecutionTime, JobExecutionTimeText} from './job-execution-time-text.js';\n\nexport interface JobExecutionSwitcherProps {\n job: Job;\n selectedJobExecution: string | null;\n onSelectedJobExecutionChange: (jobExecutionId: string) => void;\n variant?: 'compact' | 'title' | undefined;\n className?: string | undefined;\n}\n\nexport function JobExecutionSwitcher({\n job,\n selectedJobExecution,\n onSelectedJobExecutionChange,\n variant = 'compact',\n className,\n}: JobExecutionSwitcherProps) {\n const executions = useMemo(\n () => [...job.jobExecutions].sort((left, right) => right.sequence - left.sequence),\n [job.jobExecutions],\n );\n const selected =\n executions.find((jobExecution) => jobExecution.id === selectedJobExecution) ??\n executions[0] ??\n null;\n\n if (selected === null) return null;\n\n if (!job.executionCountVisible) {\n return (\n <div\n className={cn(\n 'flex min-w-0 items-center gap-6 text-sm leading-20 text-foreground-neutral-subtle',\n className,\n )}\n >\n <ExecutionSummary execution={selected} />\n </div>\n );\n }\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger\n className={cn(\n 'inline-flex min-w-0 max-w-full items-center rounded-6 text-left transition-colors focus-visible:shadow-border-interactive-with-active focus-visible:outline-none',\n variant === 'title'\n ? '-mx-6 -my-4 gap-8 px-6 py-4 hover:bg-background-components-hover'\n : 'min-h-28 gap-6 px-8 py-4 text-sm leading-20 text-foreground-neutral-subtle hover:bg-background-components-hover',\n className,\n )}\n aria-label={`Switch job execution, currently execution ${selected.sequence}: ${selected.displayName}`}\n >\n {variant === 'title' ? (\n <TitleExecutionSummary execution={selected} />\n ) : (\n <ExecutionSummary execution={selected} />\n )}\n <Icon name=\"arrowDownSLine\" className=\"size-14 shrink-0 text-foreground-neutral-muted\" />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" size=\"lg\" className=\"max-h-[320px] overflow-y-auto\">\n <DropdownMenuLabel>\n {executions.length} execution{executions.length === 1 ? '' : 's'}\n </DropdownMenuLabel>\n {executions.map((jobExecution) => {\n const isSelected = jobExecution.id === selected.id;\n\n return (\n <DropdownMenuItem\n key={jobExecution.id}\n asChild\n onSelect={() => onSelectedJobExecutionChange(jobExecution.id)}\n >\n <button\n type=\"button\"\n aria-current={isSelected ? 'true' : undefined}\n aria-label={executionAccessibleLabel(jobExecution)}\n className=\"w-full text-left\"\n >\n <WorkflowStatusIcon\n status={deriveJobExecutionDisplayStatus(jobExecution)}\n size={14}\n tooltip={false}\n />\n <span className=\"font-code text-xs leading-20 text-foreground-neutral-base tabular-nums\">\n #{jobExecution.sequence}\n </span>\n <span className=\"min-w-0 truncate text-xs leading-20 text-foreground-neutral-base\">\n {jobExecution.displayName}\n </span>\n {!isSelected && jobExecution.statusReason ? (\n <span className=\"min-w-0 flex-1 truncate text-xs leading-20 text-foreground-neutral-muted\">\n {jobExecution.statusReason}\n </span>\n ) : (\n <span className=\"min-w-0 flex-1\" />\n )}\n {isSelected ? (\n <Icon name=\"check\" className=\"size-14 shrink-0 text-foreground-neutral-base\" />\n ) : null}\n <JobExecutionDuration\n execution={jobExecution}\n className=\"ml-auto shrink-0 font-code text-xs leading-20 text-foreground-neutral-muted tabular-nums\"\n />\n </button>\n </DropdownMenuItem>\n );\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\nfunction executionAccessibleLabel(execution: JobExecution): string {\n const status = getWorkflowStatusVisual(deriveJobExecutionDisplayStatus(execution));\n return [\n `Execution #${execution.sequence}: ${execution.displayName}`,\n status.label,\n execution.statusReason ?? undefined,\n execution.displayDuration\n ? `duration ${formatJobExecutionTime(execution.displayDuration)}`\n : undefined,\n ]\n .filter((part): part is string => Boolean(part))\n .join(', ');\n}\n\nfunction TitleExecutionSummary({execution}: {execution: JobExecution}) {\n return (\n <span className=\"flex min-w-0 items-center gap-8\">\n <Text as=\"span\" size=\"sm\" bold className=\"shrink-0 text-foreground-neutral-base\">\n #{execution.sequence}\n </Text>\n <Text as=\"span\" size=\"sm\" bold className=\"min-w-0 truncate text-foreground-neutral-base\">\n {execution.displayName}\n </Text>\n </span>\n );\n}\n\nfunction ExecutionSummary({execution}: {execution: JobExecution}) {\n const displayStatus = deriveJobExecutionDisplayStatus(execution);\n\n return (\n <span className=\"flex min-w-0 items-center gap-6\">\n <WorkflowStatusIcon status={displayStatus} size={14} tooltip={false} />\n <span className=\"shrink-0 font-code text-xs leading-20 text-foreground-neutral-base tabular-nums\">\n Execution #{execution.sequence}\n </span>\n {execution.statusReason ? (\n <Text as=\"span\" size=\"xs\" className=\"min-w-0 truncate text-foreground-neutral-muted\">\n · {execution.statusReason}\n </Text>\n ) : null}\n <JobExecutionDuration\n execution={execution}\n className=\"shrink-0 font-code text-xs leading-20 text-foreground-neutral-muted tabular-nums\"\n />\n </span>\n );\n}\n\nfunction JobExecutionDuration({\n execution,\n className,\n}: {\n execution: JobExecution;\n className?: string | undefined;\n}) {\n const duration = execution.displayDuration;\n if (!duration) return null;\n\n return (\n <span className={className}>\n <JobExecutionTimeText time={duration} />\n </span>\n );\n}\n"],"names":["DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuLabel","DropdownMenuTrigger","Icon","Text","cn","useMemo","getWorkflowStatusVisual","WorkflowStatusIcon","deriveJobExecutionDisplayStatus","formatJobExecutionTime","JobExecutionTimeText","JobExecutionSwitcher","job","selectedJobExecution","onSelectedJobExecutionChange","variant","className","executions","jobExecutions","sort","left","right","sequence","selected","find","jobExecution","id","executionCountVisible","div","ExecutionSummary","execution","aria-label","displayName","TitleExecutionSummary","name","align","size","length","map","isSelected","asChild","onSelect","button","type","aria-current","undefined","executionAccessibleLabel","status","tooltip","span","statusReason","JobExecutionDuration","label","displayDuration","filter","part","Boolean","join","as","bold","displayStatus","duration","time"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-run-selection.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-view/workflow-run-selection.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,GAAG,EACR,KAAK,YAAY,EAEjB,KAAK,IAAI,EACT,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,iCAAiC,CAAC;AAE/E,MAAM,WAAW,4BAA4B;IAC3C,GAAG,EAAE,GAAG,GAAG,SAAS,CAAC;IACrB,YAAY,EAAE,YAAY,GAAG,SAAS,CAAC;IACvC,IAAI,EAAE,IAAI,GAAG,SAAS,CAAC;IACvB,OAAO,EAAE,WAAW,GAAG,SAAS,CAAC;IACjC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,wBAAgB,2BAA2B,CAAC,EAC1C,GAAG,EACH,SAAS,GACV,EAAE;IACD,GAAG,EAAE,iBAAiB,CAAC;IACvB,SAAS,EAAE,yBAAyB,CAAC;CACtC,GAAG,4BAA4B,CAyB/B"}
1
+ {"version":3,"file":"workflow-run-selection.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-view/workflow-run-selection.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,GAAG,EACR,KAAK,YAAY,EAEjB,KAAK,IAAI,EACT,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,iCAAiC,CAAC;AAE/E,MAAM,WAAW,4BAA4B;IAC3C,GAAG,EAAE,GAAG,GAAG,SAAS,CAAC;IACrB,YAAY,EAAE,YAAY,GAAG,SAAS,CAAC;IACvC,IAAI,EAAE,IAAI,GAAG,SAAS,CAAC;IACvB,OAAO,EAAE,WAAW,GAAG,SAAS,CAAC;IACjC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,wBAAgB,2BAA2B,CAAC,EAC1C,GAAG,EACH,SAAS,GACV,EAAE;IACD,GAAG,EAAE,iBAAiB,CAAC;IACvB,SAAS,EAAE,yBAAyB,CAAC;CACtC,GAAG,4BAA4B,CAwC/B"}
@@ -5,6 +5,8 @@ export function resolveWorkflowRunSelection({ run, selection }) {
5
5
  job
6
6
  ]));
7
7
  const stepMatch = findStep(run, selection.stepId);
8
+ const jobExecutionMatch = findJobExecution(run, selection.jobExecutionId);
9
+ const stepAttemptMatch = findStepAttempt(run, selection.stepAttemptId);
8
10
  if (stepMatch) {
9
11
  const attempt = resolveStepAttempt(stepMatch.step, selection.stepAttemptId);
10
12
  return {
@@ -15,7 +17,16 @@ export function resolveWorkflowRunSelection({ run, selection }) {
15
17
  selectedAttemptId: attempt?.id ?? null
16
18
  };
17
19
  }
18
- const job = (selection.jobId ? jobById.get(selection.jobId) : undefined) ?? run.jobs.at(0);
20
+ if (stepAttemptMatch) {
21
+ return {
22
+ job: stepAttemptMatch.job,
23
+ jobExecution: stepAttemptMatch.jobExecution,
24
+ step: stepAttemptMatch.step,
25
+ attempt: stepAttemptMatch.attempt,
26
+ selectedAttemptId: stepAttemptMatch.attempt.id
27
+ };
28
+ }
29
+ const job = (selection.jobId ? jobById.get(selection.jobId) : undefined) ?? jobExecutionMatch?.job ?? run.jobs.at(0);
19
30
  const jobExecution = job ? resolveJobExecution(job, selection.jobExecutionId) : undefined;
20
31
  return {
21
32
  job,
@@ -25,6 +36,34 @@ export function resolveWorkflowRunSelection({ run, selection }) {
25
36
  selectedAttemptId: null
26
37
  };
27
38
  }
39
+ function findJobExecution(run, jobExecutionId) {
40
+ if (!jobExecutionId) return undefined;
41
+ for (const job of run.jobs){
42
+ const jobExecution = job.jobExecutions.find((candidate)=>candidate.id === jobExecutionId);
43
+ if (jobExecution) return {
44
+ job,
45
+ jobExecution
46
+ };
47
+ }
48
+ return undefined;
49
+ }
50
+ function findStepAttempt(run, stepAttemptId) {
51
+ if (!stepAttemptId) return undefined;
52
+ for (const job of run.jobs){
53
+ for (const jobExecution of job.jobExecutions){
54
+ for (const step of jobExecution.steps){
55
+ const attempt = step.attempts.find((candidate)=>candidate.id === stepAttemptId);
56
+ if (attempt) return {
57
+ job,
58
+ jobExecution,
59
+ step,
60
+ attempt
61
+ };
62
+ }
63
+ }
64
+ }
65
+ return undefined;
66
+ }
28
67
  function findStep(run, stepId) {
29
68
  if (!stepId) return undefined;
30
69
  for (const job of run.jobs){
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/workflow-run-view/workflow-run-selection.ts"],"sourcesContent":["import {\n type Job,\n type JobExecution,\n resolveJobExecution,\n type Step,\n type StepAttempt,\n type WorkflowRunDetail,\n} from '#core/workflow-run.js';\nimport type {WorkflowRunSelectionInput} from '#core/workflow-run-url-state.js';\n\nexport interface ResolvedWorkflowRunSelection {\n job: Job | undefined;\n jobExecution: JobExecution | undefined;\n step: Step | undefined;\n attempt: StepAttempt | undefined;\n selectedAttemptId: string | null;\n}\n\nexport function resolveWorkflowRunSelection({\n run,\n selection,\n}: {\n run: WorkflowRunDetail;\n selection: WorkflowRunSelectionInput;\n}): ResolvedWorkflowRunSelection {\n const jobById = new Map(run.jobs.map((job) => [job.id, job]));\n const stepMatch = findStep(run, selection.stepId);\n\n if (stepMatch) {\n const attempt = resolveStepAttempt(stepMatch.step, selection.stepAttemptId);\n return {\n job: stepMatch.job,\n jobExecution: stepMatch.jobExecution,\n step: stepMatch.step,\n attempt,\n selectedAttemptId: attempt?.id ?? null,\n };\n }\n\n const job = (selection.jobId ? jobById.get(selection.jobId) : undefined) ?? run.jobs.at(0);\n const jobExecution = job ? resolveJobExecution(job, selection.jobExecutionId) : undefined;\n\n return {\n job,\n jobExecution,\n step: undefined,\n attempt: undefined,\n selectedAttemptId: null,\n };\n}\n\nfunction findStep(\n run: WorkflowRunDetail,\n stepId: string | undefined,\n): {job: Job; jobExecution: JobExecution; step: Step} | undefined {\n if (!stepId) return undefined;\n\n for (const job of run.jobs) {\n for (const jobExecution of job.jobExecutions) {\n const step = jobExecution.steps.find((candidate) => candidate.id === stepId);\n if (step) return {job, jobExecution, step};\n }\n }\n\n return undefined;\n}\n\nfunction resolveStepAttempt(step: Step, attemptId: string | undefined): StepAttempt | undefined {\n const attemptById = attemptId\n ? step.attempts.find((attempt) => attempt.id === attemptId)\n : undefined;\n if (attemptById) return attemptById;\n\n const currentAttempt = step.attempts.find((attempt) => attempt.attempt === step.currentAttempt);\n if (currentAttempt) return currentAttempt;\n\n return step.attempts.reduce<StepAttempt | undefined>((latest, attempt) => {\n if (!latest) return attempt;\n return compareAttempts(attempt, latest) > 0 ? attempt : latest;\n }, undefined);\n}\n\nfunction compareAttempts(left: StepAttempt, right: StepAttempt): number {\n return (\n left.attempt - right.attempt ||\n left.executionOrder - right.executionOrder ||\n left.id.localeCompare(right.id)\n );\n}\n"],"names":["resolveJobExecution","resolveWorkflowRunSelection","run","selection","jobById","Map","jobs","map","job","id","stepMatch","findStep","stepId","attempt","resolveStepAttempt","step","stepAttemptId","jobExecution","selectedAttemptId","jobId","get","undefined","at","jobExecutionId","jobExecutions","steps","find","candidate","attemptId","attemptById","attempts","currentAttempt","reduce","latest","compareAttempts","left","right","executionOrder","localeCompare"],"mappings":"AAAA,SAGEA,mBAAmB,QAId,wBAAwB;AAW/B,OAAO,SAASC,4BAA4B,EAC1CC,GAAG,EACHC,SAAS,EAIV;IACC,MAAMC,UAAU,IAAIC,IAAIH,IAAII,IAAI,CAACC,GAAG,CAAC,CAACC,MAAQ;YAACA,IAAIC,EAAE;YAAED;SAAI;IAC3D,MAAME,YAAYC,SAAST,KAAKC,UAAUS,MAAM;IAEhD,IAAIF,WAAW;QACb,MAAMG,UAAUC,mBAAmBJ,UAAUK,IAAI,EAAEZ,UAAUa,aAAa;QAC1E,OAAO;YACLR,KAAKE,UAAUF,GAAG;YAClBS,cAAcP,UAAUO,YAAY;YACpCF,MAAML,UAAUK,IAAI;YACpBF;YACAK,mBAAmBL,SAASJ,MAAM;QACpC;IACF;IAEA,MAAMD,MAAM,AAACL,CAAAA,UAAUgB,KAAK,GAAGf,QAAQgB,GAAG,CAACjB,UAAUgB,KAAK,IAAIE,SAAQ,KAAMnB,IAAII,IAAI,CAACgB,EAAE,CAAC;IACxF,MAAML,eAAeT,MAAMR,oBAAoBQ,KAAKL,UAAUoB,cAAc,IAAIF;IAEhF,OAAO;QACLb;QACAS;QACAF,MAAMM;QACNR,SAASQ;QACTH,mBAAmB;IACrB;AACF;AAEA,SAASP,SACPT,GAAsB,EACtBU,MAA0B;IAE1B,IAAI,CAACA,QAAQ,OAAOS;IAEpB,KAAK,MAAMb,OAAON,IAAII,IAAI,CAAE;QAC1B,KAAK,MAAMW,gBAAgBT,IAAIgB,aAAa,CAAE;YAC5C,MAAMT,OAAOE,aAAaQ,KAAK,CAACC,IAAI,CAAC,CAACC,YAAcA,UAAUlB,EAAE,KAAKG;YACrE,IAAIG,MAAM,OAAO;gBAACP;gBAAKS;gBAAcF;YAAI;QAC3C;IACF;IAEA,OAAOM;AACT;AAEA,SAASP,mBAAmBC,IAAU,EAAEa,SAA6B;IACnE,MAAMC,cAAcD,YAChBb,KAAKe,QAAQ,CAACJ,IAAI,CAAC,CAACb,UAAYA,QAAQJ,EAAE,KAAKmB,aAC/CP;IACJ,IAAIQ,aAAa,OAAOA;IAExB,MAAME,iBAAiBhB,KAAKe,QAAQ,CAACJ,IAAI,CAAC,CAACb,UAAYA,QAAQA,OAAO,KAAKE,KAAKgB,cAAc;IAC9F,IAAIA,gBAAgB,OAAOA;IAE3B,OAAOhB,KAAKe,QAAQ,CAACE,MAAM,CAA0B,CAACC,QAAQpB;QAC5D,IAAI,CAACoB,QAAQ,OAAOpB;QACpB,OAAOqB,gBAAgBrB,SAASoB,UAAU,IAAIpB,UAAUoB;IAC1D,GAAGZ;AACL;AAEA,SAASa,gBAAgBC,IAAiB,EAAEC,KAAkB;IAC5D,OACED,KAAKtB,OAAO,GAAGuB,MAAMvB,OAAO,IAC5BsB,KAAKE,cAAc,GAAGD,MAAMC,cAAc,IAC1CF,KAAK1B,EAAE,CAAC6B,aAAa,CAACF,MAAM3B,EAAE;AAElC"}
1
+ {"version":3,"sources":["../../../src/components/workflow-run-view/workflow-run-selection.ts"],"sourcesContent":["import {\n type Job,\n type JobExecution,\n resolveJobExecution,\n type Step,\n type StepAttempt,\n type WorkflowRunDetail,\n} from '#core/workflow-run.js';\nimport type {WorkflowRunSelectionInput} from '#core/workflow-run-url-state.js';\n\nexport interface ResolvedWorkflowRunSelection {\n job: Job | undefined;\n jobExecution: JobExecution | undefined;\n step: Step | undefined;\n attempt: StepAttempt | undefined;\n selectedAttemptId: string | null;\n}\n\nexport function resolveWorkflowRunSelection({\n run,\n selection,\n}: {\n run: WorkflowRunDetail;\n selection: WorkflowRunSelectionInput;\n}): ResolvedWorkflowRunSelection {\n const jobById = new Map(run.jobs.map((job) => [job.id, job]));\n const stepMatch = findStep(run, selection.stepId);\n const jobExecutionMatch = findJobExecution(run, selection.jobExecutionId);\n const stepAttemptMatch = findStepAttempt(run, selection.stepAttemptId);\n\n if (stepMatch) {\n const attempt = resolveStepAttempt(stepMatch.step, selection.stepAttemptId);\n return {\n job: stepMatch.job,\n jobExecution: stepMatch.jobExecution,\n step: stepMatch.step,\n attempt,\n selectedAttemptId: attempt?.id ?? null,\n };\n }\n\n if (stepAttemptMatch) {\n return {\n job: stepAttemptMatch.job,\n jobExecution: stepAttemptMatch.jobExecution,\n step: stepAttemptMatch.step,\n attempt: stepAttemptMatch.attempt,\n selectedAttemptId: stepAttemptMatch.attempt.id,\n };\n }\n\n const job =\n (selection.jobId ? jobById.get(selection.jobId) : undefined) ??\n jobExecutionMatch?.job ??\n run.jobs.at(0);\n const jobExecution = job ? resolveJobExecution(job, selection.jobExecutionId) : undefined;\n\n return {\n job,\n jobExecution,\n step: undefined,\n attempt: undefined,\n selectedAttemptId: null,\n };\n}\n\nfunction findJobExecution(\n run: WorkflowRunDetail,\n jobExecutionId: string | undefined,\n): {job: Job; jobExecution: JobExecution} | undefined {\n if (!jobExecutionId) return undefined;\n\n for (const job of run.jobs) {\n const jobExecution = job.jobExecutions.find((candidate) => candidate.id === jobExecutionId);\n if (jobExecution) return {job, jobExecution};\n }\n\n return undefined;\n}\n\nfunction findStepAttempt(\n run: WorkflowRunDetail,\n stepAttemptId: string | undefined,\n): {job: Job; jobExecution: JobExecution; step: Step; attempt: StepAttempt} | undefined {\n if (!stepAttemptId) return undefined;\n\n for (const job of run.jobs) {\n for (const jobExecution of job.jobExecutions) {\n for (const step of jobExecution.steps) {\n const attempt = step.attempts.find((candidate) => candidate.id === stepAttemptId);\n if (attempt) return {job, jobExecution, step, attempt};\n }\n }\n }\n\n return undefined;\n}\n\nfunction findStep(\n run: WorkflowRunDetail,\n stepId: string | undefined,\n): {job: Job; jobExecution: JobExecution; step: Step} | undefined {\n if (!stepId) return undefined;\n\n for (const job of run.jobs) {\n for (const jobExecution of job.jobExecutions) {\n const step = jobExecution.steps.find((candidate) => candidate.id === stepId);\n if (step) return {job, jobExecution, step};\n }\n }\n\n return undefined;\n}\n\nfunction resolveStepAttempt(step: Step, attemptId: string | undefined): StepAttempt | undefined {\n const attemptById = attemptId\n ? step.attempts.find((attempt) => attempt.id === attemptId)\n : undefined;\n if (attemptById) return attemptById;\n\n const currentAttempt = step.attempts.find((attempt) => attempt.attempt === step.currentAttempt);\n if (currentAttempt) return currentAttempt;\n\n return step.attempts.reduce<StepAttempt | undefined>((latest, attempt) => {\n if (!latest) return attempt;\n return compareAttempts(attempt, latest) > 0 ? attempt : latest;\n }, undefined);\n}\n\nfunction compareAttempts(left: StepAttempt, right: StepAttempt): number {\n return (\n left.attempt - right.attempt ||\n left.executionOrder - right.executionOrder ||\n left.id.localeCompare(right.id)\n );\n}\n"],"names":["resolveJobExecution","resolveWorkflowRunSelection","run","selection","jobById","Map","jobs","map","job","id","stepMatch","findStep","stepId","jobExecutionMatch","findJobExecution","jobExecutionId","stepAttemptMatch","findStepAttempt","stepAttemptId","attempt","resolveStepAttempt","step","jobExecution","selectedAttemptId","jobId","get","undefined","at","jobExecutions","find","candidate","steps","attempts","attemptId","attemptById","currentAttempt","reduce","latest","compareAttempts","left","right","executionOrder","localeCompare"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import type { QueryLoadErrorQuery } from '@shipfox/client-ui';
2
2
  export declare function WorkflowRunSkeleton(): import("react").JSX.Element;
3
+ export declare function WorkflowRunContentSkeleton(): import("react").JSX.Element;
3
4
  export declare function WorkflowRunNotFound(): import("react").JSX.Element;
4
5
  /**
5
6
  * Slim non-blocking banner shown when a background refetch fails after the run already
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-run-states.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-view/workflow-run-states.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,oBAAoB,CAAC;AAO5D,wBAAgB,mBAAmB,gCAwBlC;AAED,wBAAgB,mBAAmB,gCAQlC;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,EAAC,KAAK,EAAC,EAAE;IAAC,KAAK,EAAE,mBAAmB,CAAA;CAAC,+BAqB1E"}
1
+ {"version":3,"file":"workflow-run-states.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-view/workflow-run-states.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,oBAAoB,CAAC;AAO5D,wBAAgB,mBAAmB,gCAwBlC;AAED,wBAAgB,0BAA0B,gCASzC;AAED,wBAAgB,mBAAmB,gCAQlC;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,EAAC,KAAK,EAAC,EAAE;IAAC,KAAK,EAAE,mBAAmB,CAAA;CAAC,+BAqB1E"}
@@ -7,7 +7,7 @@ import { Text } from '@shipfox/react-ui/typography';
7
7
  export function WorkflowRunSkeleton() {
8
8
  return /*#__PURE__*/ _jsx("section", {
9
9
  "aria-label": "Loading workflow run",
10
- className: "border-b border-border-neutral-base bg-background-subtle-base px-16 py-12",
10
+ className: "bg-background-neutral-background px-16 py-12",
11
11
  children: /*#__PURE__*/ _jsxs("div", {
12
12
  className: "flex min-w-0 flex-wrap items-center gap-x-12 gap-y-8",
13
13
  children: [
@@ -48,6 +48,15 @@ export function WorkflowRunSkeleton() {
48
48
  })
49
49
  });
50
50
  }
51
+ export function WorkflowRunContentSkeleton() {
52
+ return /*#__PURE__*/ _jsx("section", {
53
+ "aria-label": "Loading workflow run content",
54
+ className: "min-h-160 rounded-8 border border-border-neutral-base bg-background-components-base p-16",
55
+ children: /*#__PURE__*/ _jsx(Skeleton, {
56
+ className: "h-160 w-full rounded-6"
57
+ })
58
+ });
59
+ }
51
60
  export function WorkflowRunNotFound() {
52
61
  return /*#__PURE__*/ _jsx(EmptyState, {
53
62
  icon: "pulseLine",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/workflow-run-view/workflow-run-states.tsx"],"sourcesContent":["import type {QueryLoadErrorQuery} from '@shipfox/client-ui';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Text} from '@shipfox/react-ui/typography';\n\nexport function WorkflowRunSkeleton() {\n return (\n <section\n aria-label=\"Loading workflow run\"\n className=\"border-b border-border-neutral-base bg-background-subtle-base px-16 py-12\"\n >\n <div className=\"flex min-w-0 flex-wrap items-center gap-x-12 gap-y-8\">\n <div className=\"flex min-w-0 items-center gap-8\">\n <Skeleton className=\"size-8 rounded-full\" />\n <Skeleton className=\"h-24 w-180 rounded-6\" />\n </div>\n <Skeleton className=\"h-24 w-88 rounded-6\" />\n <span\n aria-hidden=\"true\"\n className=\"hidden h-20 w-px shrink-0 bg-border-neutral-base sm:block\"\n />\n <Skeleton className=\"h-20 w-64 rounded-4\" />\n <Skeleton className=\"h-20 w-112 rounded-4\" />\n <span className=\"min-w-0 flex-1\" />\n <Skeleton className=\"h-20 w-88 rounded-4\" />\n <Skeleton className=\"h-20 w-88 rounded-4\" />\n </div>\n </section>\n );\n}\n\nexport function WorkflowRunNotFound() {\n return (\n <EmptyState\n icon=\"pulseLine\"\n title=\"Run not found\"\n description=\"This run does not exist or is no longer available.\"\n />\n );\n}\n\n/**\n * Slim non-blocking banner shown when a background refetch fails after the run already\n * loaded (active-run polling can hit a transient API error), so the loaded run stays on\n * screen with an inline retry instead of being wiped by a full error placeholder.\n */\nexport function WorkflowRunStaleError({query}: {query: QueryLoadErrorQuery}) {\n return (\n <div className=\"border-b border-border-neutral-base p-8\">\n <Callout role=\"alert\" type=\"error\">\n <div className=\"flex items-center justify-between gap-8\">\n <Text size=\"xs\">Could not refresh this run.</Text>\n <Button\n type=\"button\"\n size=\"2xs\"\n variant=\"secondary\"\n isLoading={query.isFetching}\n onClick={() => {\n void query.refetch();\n }}\n >\n Retry\n </Button>\n </div>\n </Callout>\n </div>\n );\n}\n"],"names":["Button","Callout","EmptyState","Skeleton","Text","WorkflowRunSkeleton","section","aria-label","className","div","span","aria-hidden","WorkflowRunNotFound","icon","title","description","WorkflowRunStaleError","query","role","type","size","variant","isLoading","isFetching","onClick","refetch"],"mappings":";AACA,SAAQA,MAAM,QAAO,2BAA2B;AAChD,SAAQC,OAAO,QAAO,4BAA4B;AAClD,SAAQC,UAAU,QAAO,gCAAgC;AACzD,SAAQC,QAAQ,QAAO,6BAA6B;AACpD,SAAQC,IAAI,QAAO,+BAA+B;AAElD,OAAO,SAASC;IACd,qBACE,KAACC;QACCC,cAAW;QACXC,WAAU;kBAEV,cAAA,MAACC;YAAID,WAAU;;8BACb,MAACC;oBAAID,WAAU;;sCACb,KAACL;4BAASK,WAAU;;sCACpB,KAACL;4BAASK,WAAU;;;;8BAEtB,KAACL;oBAASK,WAAU;;8BACpB,KAACE;oBACCC,eAAY;oBACZH,WAAU;;8BAEZ,KAACL;oBAASK,WAAU;;8BACpB,KAACL;oBAASK,WAAU;;8BACpB,KAACE;oBAAKF,WAAU;;8BAChB,KAACL;oBAASK,WAAU;;8BACpB,KAACL;oBAASK,WAAU;;;;;AAI5B;AAEA,OAAO,SAASI;IACd,qBACE,KAACV;QACCW,MAAK;QACLC,OAAM;QACNC,aAAY;;AAGlB;AAEA;;;;CAIC,GACD,OAAO,SAASC,sBAAsB,EAACC,KAAK,EAA+B;IACzE,qBACE,KAACR;QAAID,WAAU;kBACb,cAAA,KAACP;YAAQiB,MAAK;YAAQC,MAAK;sBACzB,cAAA,MAACV;gBAAID,WAAU;;kCACb,KAACJ;wBAAKgB,MAAK;kCAAK;;kCAChB,KAACpB;wBACCmB,MAAK;wBACLC,MAAK;wBACLC,SAAQ;wBACRC,WAAWL,MAAMM,UAAU;wBAC3BC,SAAS;4BACP,KAAKP,MAAMQ,OAAO;wBACpB;kCACD;;;;;;AAOX"}
1
+ {"version":3,"sources":["../../../src/components/workflow-run-view/workflow-run-states.tsx"],"sourcesContent":["import type {QueryLoadErrorQuery} from '@shipfox/client-ui';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Text} from '@shipfox/react-ui/typography';\n\nexport function WorkflowRunSkeleton() {\n return (\n <section\n aria-label=\"Loading workflow run\"\n className=\"bg-background-neutral-background px-16 py-12\"\n >\n <div className=\"flex min-w-0 flex-wrap items-center gap-x-12 gap-y-8\">\n <div className=\"flex min-w-0 items-center gap-8\">\n <Skeleton className=\"size-8 rounded-full\" />\n <Skeleton className=\"h-24 w-180 rounded-6\" />\n </div>\n <Skeleton className=\"h-24 w-88 rounded-6\" />\n <span\n aria-hidden=\"true\"\n className=\"hidden h-20 w-px shrink-0 bg-border-neutral-base sm:block\"\n />\n <Skeleton className=\"h-20 w-64 rounded-4\" />\n <Skeleton className=\"h-20 w-112 rounded-4\" />\n <span className=\"min-w-0 flex-1\" />\n <Skeleton className=\"h-20 w-88 rounded-4\" />\n <Skeleton className=\"h-20 w-88 rounded-4\" />\n </div>\n </section>\n );\n}\n\nexport function WorkflowRunContentSkeleton() {\n return (\n <section\n aria-label=\"Loading workflow run content\"\n className=\"min-h-160 rounded-8 border border-border-neutral-base bg-background-components-base p-16\"\n >\n <Skeleton className=\"h-160 w-full rounded-6\" />\n </section>\n );\n}\n\nexport function WorkflowRunNotFound() {\n return (\n <EmptyState\n icon=\"pulseLine\"\n title=\"Run not found\"\n description=\"This run does not exist or is no longer available.\"\n />\n );\n}\n\n/**\n * Slim non-blocking banner shown when a background refetch fails after the run already\n * loaded (active-run polling can hit a transient API error), so the loaded run stays on\n * screen with an inline retry instead of being wiped by a full error placeholder.\n */\nexport function WorkflowRunStaleError({query}: {query: QueryLoadErrorQuery}) {\n return (\n <div className=\"border-b border-border-neutral-base p-8\">\n <Callout role=\"alert\" type=\"error\">\n <div className=\"flex items-center justify-between gap-8\">\n <Text size=\"xs\">Could not refresh this run.</Text>\n <Button\n type=\"button\"\n size=\"2xs\"\n variant=\"secondary\"\n isLoading={query.isFetching}\n onClick={() => {\n void query.refetch();\n }}\n >\n Retry\n </Button>\n </div>\n </Callout>\n </div>\n );\n}\n"],"names":["Button","Callout","EmptyState","Skeleton","Text","WorkflowRunSkeleton","section","aria-label","className","div","span","aria-hidden","WorkflowRunContentSkeleton","WorkflowRunNotFound","icon","title","description","WorkflowRunStaleError","query","role","type","size","variant","isLoading","isFetching","onClick","refetch"],"mappings":";AACA,SAAQA,MAAM,QAAO,2BAA2B;AAChD,SAAQC,OAAO,QAAO,4BAA4B;AAClD,SAAQC,UAAU,QAAO,gCAAgC;AACzD,SAAQC,QAAQ,QAAO,6BAA6B;AACpD,SAAQC,IAAI,QAAO,+BAA+B;AAElD,OAAO,SAASC;IACd,qBACE,KAACC;QACCC,cAAW;QACXC,WAAU;kBAEV,cAAA,MAACC;YAAID,WAAU;;8BACb,MAACC;oBAAID,WAAU;;sCACb,KAACL;4BAASK,WAAU;;sCACpB,KAACL;4BAASK,WAAU;;;;8BAEtB,KAACL;oBAASK,WAAU;;8BACpB,KAACE;oBACCC,eAAY;oBACZH,WAAU;;8BAEZ,KAACL;oBAASK,WAAU;;8BACpB,KAACL;oBAASK,WAAU;;8BACpB,KAACE;oBAAKF,WAAU;;8BAChB,KAACL;oBAASK,WAAU;;8BACpB,KAACL;oBAASK,WAAU;;;;;AAI5B;AAEA,OAAO,SAASI;IACd,qBACE,KAACN;QACCC,cAAW;QACXC,WAAU;kBAEV,cAAA,KAACL;YAASK,WAAU;;;AAG1B;AAEA,OAAO,SAASK;IACd,qBACE,KAACX;QACCY,MAAK;QACLC,OAAM;QACNC,aAAY;;AAGlB;AAEA;;;;CAIC,GACD,OAAO,SAASC,sBAAsB,EAACC,KAAK,EAA+B;IACzE,qBACE,KAACT;QAAID,WAAU;kBACb,cAAA,KAACP;YAAQkB,MAAK;YAAQC,MAAK;sBACzB,cAAA,MAACX;gBAAID,WAAU;;kCACb,KAACJ;wBAAKiB,MAAK;kCAAK;;kCAChB,KAACrB;wBACCoB,MAAK;wBACLC,MAAK;wBACLC,SAAQ;wBACRC,WAAWL,MAAMM,UAAU;wBAC3BC,SAAS;4BACP,KAAKP,MAAMQ,OAAO;wBACpB;kCACD;;;;;;AAOX"}
@@ -1,15 +1,20 @@
1
+ import type { RunAnnotationSummary } from '#core/workflow-run-tabs.js';
1
2
  import { type WorkflowRunSelectionInput } from '#core/workflow-run-url-state.js';
3
+ import { type WorkflowRunTab } from '#routes/inputs.js';
2
4
  export interface WorkflowRunViewProps {
3
- workspaceId: string;
4
5
  projectId: string;
6
+ workspaceSlug?: string | undefined;
7
+ projectSlug?: string | undefined;
5
8
  workflowRunId?: string | undefined;
6
9
  selection?: WorkflowRunSelectionInput | undefined;
7
10
  onSelectionChange?: ((selection: WorkflowRunSelectionInput) => void) | undefined;
11
+ tab?: WorkflowRunTab | undefined;
12
+ onTabChange?: ((tab: WorkflowRunTab) => void) | undefined;
13
+ annotationSummary?: RunAnnotationSummary | undefined;
8
14
  }
9
15
  /**
10
- * Renders the run for `workflowRunId`, or a skeleton while `workflowRunId` is still unknown (the page is
11
- * resolving which run to show) or the run is loading, so the page never branches on the
12
- * loading state itself.
16
+ * Renders the run for `workflowRunId`, keeping the local tab strip mounted while the run
17
+ * request is pending so polling never inserts navigation under the user's cursor.
13
18
  */
14
- export declare function WorkflowRunView({ workspaceId, projectId, workflowRunId, selection, onSelectionChange, }: WorkflowRunViewProps): import("react").JSX.Element;
19
+ export declare function WorkflowRunView({ projectId, workspaceSlug, projectSlug, workflowRunId, selection, onSelectionChange, tab, onTabChange, annotationSummary, }: WorkflowRunViewProps): import("react").JSX.Element;
15
20
  //# sourceMappingURL=workflow-run-view.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-run-view.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-view/workflow-run-view.tsx"],"names":[],"mappings":"AAYA,OAAO,EACL,KAAK,yBAAyB,EAE/B,MAAM,iCAAiC,CAAC;AAsBzC,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,SAAS,CAAC,EAAE,yBAAyB,GAAG,SAAS,CAAC;IAClD,iBAAiB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,yBAAyB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CAClF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAC9B,WAAW,EACX,SAAS,EACT,aAAa,EACb,SAAS,EACT,iBAAiB,GAClB,EAAE,oBAAoB,+BAkBtB"}
1
+ {"version":3,"file":"workflow-run-view.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-view/workflow-run-view.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,4BAA4B,CAAC;AACrE,OAAO,EACL,KAAK,yBAAyB,EAE/B,MAAM,iCAAiC,CAAC;AAMzC,OAAO,EAAoB,KAAK,cAAc,EAAC,MAAM,mBAAmB,CAAC;AAwBzE,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,SAAS,CAAC,EAAE,yBAAyB,GAAG,SAAS,CAAC;IAClD,iBAAiB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,yBAAyB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACjF,GAAG,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;IACjC,WAAW,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC1D,iBAAiB,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;CACtD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAC9B,SAAS,EACT,aAAa,EACb,WAAW,EACX,aAAa,EACb,SAAS,EACT,iBAAiB,EACjB,GAAG,EACH,WAAW,EACX,iBAAiB,GAClB,EAAE,oBAAoB,+BAqBtB"}