@shipfox/client-workflows 0.2.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 (327) hide show
  1. package/.storybook/main.ts +1 -0
  2. package/.storybook/preview.css +10 -0
  3. package/.storybook/preview.tsx +97 -0
  4. package/.swcrc +42 -0
  5. package/.turbo/turbo-build.log +2 -0
  6. package/.turbo/turbo-type$colon$emit.log +1 -0
  7. package/.turbo/turbo-type.log +1 -0
  8. package/CHANGELOG.md +162 -0
  9. package/LICENSE +21 -0
  10. package/dist/components/identifier/identifier.d.ts +6 -0
  11. package/dist/components/identifier/identifier.d.ts.map +1 -0
  12. package/dist/components/identifier/identifier.js +144 -0
  13. package/dist/components/identifier/identifier.js.map +1 -0
  14. package/dist/components/identifier/index.d.ts +2 -0
  15. package/dist/components/identifier/index.d.ts.map +1 -0
  16. package/dist/components/identifier/index.js +3 -0
  17. package/dist/components/identifier/index.js.map +1 -0
  18. package/dist/components/job-graph/graph-model.d.ts +27 -0
  19. package/dist/components/job-graph/graph-model.d.ts.map +1 -0
  20. package/dist/components/job-graph/graph-model.js +107 -0
  21. package/dist/components/job-graph/graph-model.js.map +1 -0
  22. package/dist/components/job-graph/index.d.ts +3 -0
  23. package/dist/components/job-graph/index.d.ts.map +1 -0
  24. package/dist/components/job-graph/index.js +3 -0
  25. package/dist/components/job-graph/index.js.map +1 -0
  26. package/dist/components/job-graph/job-duration-format.d.ts +4 -0
  27. package/dist/components/job-graph/job-duration-format.d.ts.map +1 -0
  28. package/dist/components/job-graph/job-duration-format.js +41 -0
  29. package/dist/components/job-graph/job-duration-format.js.map +1 -0
  30. package/dist/components/job-graph/job-duration-label.d.ts +5 -0
  31. package/dist/components/job-graph/job-duration-label.d.ts.map +1 -0
  32. package/dist/components/job-graph/job-duration-label.js +33 -0
  33. package/dist/components/job-graph/job-duration-label.js.map +1 -0
  34. package/dist/components/job-graph/job-graph-content.d.ts +9 -0
  35. package/dist/components/job-graph/job-graph-content.d.ts.map +1 -0
  36. package/dist/components/job-graph/job-graph-content.js +181 -0
  37. package/dist/components/job-graph/job-graph-content.js.map +1 -0
  38. package/dist/components/job-graph/job-graph-view.d.ts +11 -0
  39. package/dist/components/job-graph/job-graph-view.d.ts.map +1 -0
  40. package/dist/components/job-graph/job-graph-view.js +24 -0
  41. package/dist/components/job-graph/job-graph-view.js.map +1 -0
  42. package/dist/components/job-graph/job-graph.d.ts +3 -0
  43. package/dist/components/job-graph/job-graph.d.ts.map +1 -0
  44. package/dist/components/job-graph/job-graph.js +21 -0
  45. package/dist/components/job-graph/job-graph.js.map +1 -0
  46. package/dist/components/job-graph/job-node.d.ts +16 -0
  47. package/dist/components/job-graph/job-node.d.ts.map +1 -0
  48. package/dist/components/job-graph/job-node.js +196 -0
  49. package/dist/components/job-graph/job-node.js.map +1 -0
  50. package/dist/components/job-graph/types.d.ts +9 -0
  51. package/dist/components/job-graph/types.d.ts.map +1 -0
  52. package/dist/components/job-graph/types.js +3 -0
  53. package/dist/components/job-graph/types.js.map +1 -0
  54. package/dist/components/step-list/index.d.ts +3 -0
  55. package/dist/components/step-list/index.d.ts.map +1 -0
  56. package/dist/components/step-list/index.js +3 -0
  57. package/dist/components/step-list/index.js.map +1 -0
  58. package/dist/components/step-list/step-list-model.d.ts +36 -0
  59. package/dist/components/step-list/step-list-model.d.ts.map +1 -0
  60. package/dist/components/step-list/step-list-model.js +135 -0
  61. package/dist/components/step-list/step-list-model.js.map +1 -0
  62. package/dist/components/step-list/step-list.d.ts +32 -0
  63. package/dist/components/step-list/step-list.d.ts.map +1 -0
  64. package/dist/components/step-list/step-list.js +409 -0
  65. package/dist/components/step-list/step-list.js.map +1 -0
  66. package/dist/components/workflow-run-duration-label.d.ts +8 -0
  67. package/dist/components/workflow-run-duration-label.d.ts.map +1 -0
  68. package/dist/components/workflow-run-duration-label.js +84 -0
  69. package/dist/components/workflow-run-duration-label.js.map +1 -0
  70. package/dist/components/workflow-run-list/index.d.ts +2 -0
  71. package/dist/components/workflow-run-list/index.d.ts.map +1 -0
  72. package/dist/components/workflow-run-list/index.js +3 -0
  73. package/dist/components/workflow-run-list/index.js.map +1 -0
  74. package/dist/components/workflow-run-list/run-display.d.ts +5 -0
  75. package/dist/components/workflow-run-list/run-display.d.ts.map +1 -0
  76. package/dist/components/workflow-run-list/run-display.js +19 -0
  77. package/dist/components/workflow-run-list/run-display.js.map +1 -0
  78. package/dist/components/workflow-run-list/types.d.ts +21 -0
  79. package/dist/components/workflow-run-list/types.d.ts.map +1 -0
  80. package/dist/components/workflow-run-list/types.js +3 -0
  81. package/dist/components/workflow-run-list/types.js.map +1 -0
  82. package/dist/components/workflow-run-list/workflow-run-list-content.d.ts +14 -0
  83. package/dist/components/workflow-run-list/workflow-run-list-content.d.ts.map +1 -0
  84. package/dist/components/workflow-run-list/workflow-run-list-content.js +37 -0
  85. package/dist/components/workflow-run-list/workflow-run-list-content.js.map +1 -0
  86. package/dist/components/workflow-run-list/workflow-run-list-header.d.ts +10 -0
  87. package/dist/components/workflow-run-list/workflow-run-list-header.d.ts.map +1 -0
  88. package/dist/components/workflow-run-list/workflow-run-list-header.js +55 -0
  89. package/dist/components/workflow-run-list/workflow-run-list-header.js.map +1 -0
  90. package/dist/components/workflow-run-list/workflow-run-list-states.d.ts +15 -0
  91. package/dist/components/workflow-run-list/workflow-run-list-states.d.ts.map +1 -0
  92. package/dist/components/workflow-run-list/workflow-run-list-states.js +103 -0
  93. package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -0
  94. package/dist/components/workflow-run-list/workflow-run-list-view.d.ts +3 -0
  95. package/dist/components/workflow-run-list/workflow-run-list-view.d.ts.map +1 -0
  96. package/dist/components/workflow-run-list/workflow-run-list-view.js +46 -0
  97. package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -0
  98. package/dist/components/workflow-run-list/workflow-run-list.d.ts +3 -0
  99. package/dist/components/workflow-run-list/workflow-run-list.d.ts.map +1 -0
  100. package/dist/components/workflow-run-list/workflow-run-list.js +20 -0
  101. package/dist/components/workflow-run-list/workflow-run-list.js.map +1 -0
  102. package/dist/components/workflow-run-list/workflow-run-row.d.ts +14 -0
  103. package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -0
  104. package/dist/components/workflow-run-list/workflow-run-row.js +172 -0
  105. package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -0
  106. package/dist/components/workflow-run-summary/index.d.ts +2 -0
  107. package/dist/components/workflow-run-summary/index.d.ts.map +1 -0
  108. package/dist/components/workflow-run-summary/index.js +3 -0
  109. package/dist/components/workflow-run-summary/index.js.map +1 -0
  110. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.d.ts +9 -0
  111. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.d.ts.map +1 -0
  112. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +138 -0
  113. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js.map +1 -0
  114. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts +20 -0
  115. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -0
  116. package/dist/components/workflow-run-summary/workflow-run-summary.js +252 -0
  117. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -0
  118. package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts +7 -0
  119. package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts.map +1 -0
  120. package/dist/components/workflow-run-view/agent-config-failure-callout.js +84 -0
  121. package/dist/components/workflow-run-view/agent-config-failure-callout.js.map +1 -0
  122. package/dist/components/workflow-run-view/index.d.ts +3 -0
  123. package/dist/components/workflow-run-view/index.d.ts.map +1 -0
  124. package/dist/components/workflow-run-view/index.js +4 -0
  125. package/dist/components/workflow-run-view/index.js.map +1 -0
  126. package/dist/components/workflow-run-view/job-card.d.ts +17 -0
  127. package/dist/components/workflow-run-view/job-card.d.ts.map +1 -0
  128. package/dist/components/workflow-run-view/job-card.js +476 -0
  129. package/dist/components/workflow-run-view/job-card.js.map +1 -0
  130. package/dist/components/workflow-run-view/job-execution-switcher.d.ts +10 -0
  131. package/dist/components/workflow-run-view/job-execution-switcher.d.ts.map +1 -0
  132. package/dist/components/workflow-run-view/job-execution-switcher.js +171 -0
  133. package/dist/components/workflow-run-view/job-execution-switcher.js.map +1 -0
  134. package/dist/components/workflow-run-view/job-execution-time-text.d.ts +6 -0
  135. package/dist/components/workflow-run-view/job-execution-time-text.d.ts.map +1 -0
  136. package/dist/components/workflow-run-view/job-execution-time-text.js +24 -0
  137. package/dist/components/workflow-run-view/job-execution-time-text.js.map +1 -0
  138. package/dist/components/workflow-run-view/step-attempt-log-panel.d.ts +9 -0
  139. package/dist/components/workflow-run-view/step-attempt-log-panel.d.ts.map +1 -0
  140. package/dist/components/workflow-run-view/step-attempt-log-panel.js +152 -0
  141. package/dist/components/workflow-run-view/step-attempt-log-panel.js.map +1 -0
  142. package/dist/components/workflow-run-view/workflow-run-selection.d.ts +14 -0
  143. package/dist/components/workflow-run-view/workflow-run-selection.d.ts.map +1 -0
  144. package/dist/components/workflow-run-view/workflow-run-selection.js +56 -0
  145. package/dist/components/workflow-run-view/workflow-run-selection.js.map +1 -0
  146. package/dist/components/workflow-run-view/workflow-run-states.d.ts +12 -0
  147. package/dist/components/workflow-run-view/workflow-run-states.d.ts.map +1 -0
  148. package/dist/components/workflow-run-view/workflow-run-states.js +91 -0
  149. package/dist/components/workflow-run-view/workflow-run-states.js.map +1 -0
  150. package/dist/components/workflow-run-view/workflow-run-view.d.ts +15 -0
  151. package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -0
  152. package/dist/components/workflow-run-view/workflow-run-view.js +285 -0
  153. package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -0
  154. package/dist/components/workflow-source-panel/index.d.ts +2 -0
  155. package/dist/components/workflow-source-panel/index.d.ts.map +1 -0
  156. package/dist/components/workflow-source-panel/index.js +3 -0
  157. package/dist/components/workflow-source-panel/index.js.map +1 -0
  158. package/dist/components/workflow-source-panel/workflow-source-panel.d.ts +13 -0
  159. package/dist/components/workflow-source-panel/workflow-source-panel.d.ts.map +1 -0
  160. package/dist/components/workflow-source-panel/workflow-source-panel.js +91 -0
  161. package/dist/components/workflow-source-panel/workflow-source-panel.js.map +1 -0
  162. package/dist/components/workflow-status/status-visuals.d.ts +12 -0
  163. package/dist/components/workflow-status/status-visuals.d.ts.map +1 -0
  164. package/dist/components/workflow-status/status-visuals.js +54 -0
  165. package/dist/components/workflow-status/status-visuals.js.map +1 -0
  166. package/dist/components/workflow-status/workflow-status-icon.d.ts +22 -0
  167. package/dist/components/workflow-status/workflow-status-icon.d.ts.map +1 -0
  168. package/dist/components/workflow-status/workflow-status-icon.js +100 -0
  169. package/dist/components/workflow-status/workflow-status-icon.js.map +1 -0
  170. package/dist/core/entities/job-execution.d.ts +69 -0
  171. package/dist/core/entities/job-execution.d.ts.map +1 -0
  172. package/dist/core/entities/job-execution.js +83 -0
  173. package/dist/core/entities/job-execution.js.map +1 -0
  174. package/dist/core/entities/job.d.ts +56 -0
  175. package/dist/core/entities/job.d.ts.map +1 -0
  176. package/dist/core/entities/job.js +71 -0
  177. package/dist/core/entities/job.js.map +1 -0
  178. package/dist/core/entities/step-attempt.d.ts +49 -0
  179. package/dist/core/entities/step-attempt.d.ts.map +1 -0
  180. package/dist/core/entities/step-attempt.js +48 -0
  181. package/dist/core/entities/step-attempt.js.map +1 -0
  182. package/dist/core/entities/step.d.ts +41 -0
  183. package/dist/core/entities/step.d.ts.map +1 -0
  184. package/dist/core/entities/step.js +51 -0
  185. package/dist/core/entities/step.js.map +1 -0
  186. package/dist/core/entities/workflow-run-attempt.d.ts +44 -0
  187. package/dist/core/entities/workflow-run-attempt.d.ts.map +1 -0
  188. package/dist/core/entities/workflow-run-attempt.js +51 -0
  189. package/dist/core/entities/workflow-run-attempt.js.map +1 -0
  190. package/dist/core/entities/workflow-run.d.ts +61 -0
  191. package/dist/core/entities/workflow-run.d.ts.map +1 -0
  192. package/dist/core/entities/workflow-run.js +104 -0
  193. package/dist/core/entities/workflow-run.js.map +1 -0
  194. package/dist/core/workflow-run-url-state.d.ts +13 -0
  195. package/dist/core/workflow-run-url-state.d.ts.map +1 -0
  196. package/dist/core/workflow-run-url-state.js +43 -0
  197. package/dist/core/workflow-run-url-state.js.map +1 -0
  198. package/dist/core/workflow-run.d.ts +13 -0
  199. package/dist/core/workflow-run.d.ts.map +1 -0
  200. package/dist/core/workflow-run.js +8 -0
  201. package/dist/core/workflow-run.js.map +1 -0
  202. package/dist/env.d.js +2 -0
  203. package/dist/env.d.js.map +1 -0
  204. package/dist/feature.d.ts +20 -0
  205. package/dist/feature.d.ts.map +1 -0
  206. package/dist/feature.js +27 -0
  207. package/dist/feature.js.map +1 -0
  208. package/dist/hooks/api/workflow-runs.d.ts +109 -0
  209. package/dist/hooks/api/workflow-runs.d.ts.map +1 -0
  210. package/dist/hooks/api/workflow-runs.js +394 -0
  211. package/dist/hooks/api/workflow-runs.js.map +1 -0
  212. package/dist/index.d.ts +5 -0
  213. package/dist/index.d.ts.map +1 -0
  214. package/dist/index.js +5 -0
  215. package/dist/index.js.map +1 -0
  216. package/dist/pages/workflow-run-first-time-use.d.ts +6 -0
  217. package/dist/pages/workflow-run-first-time-use.d.ts.map +1 -0
  218. package/dist/pages/workflow-run-first-time-use.js +17 -0
  219. package/dist/pages/workflow-run-first-time-use.js.map +1 -0
  220. package/dist/pages/workflow-run-page.d.ts +8 -0
  221. package/dist/pages/workflow-run-page.d.ts.map +1 -0
  222. package/dist/pages/workflow-run-page.js +83 -0
  223. package/dist/pages/workflow-run-page.js.map +1 -0
  224. package/dist/routes/run-detail.d.ts +8 -0
  225. package/dist/routes/run-detail.d.ts.map +1 -0
  226. package/dist/routes/run-detail.js +21 -0
  227. package/dist/routes/run-detail.js.map +1 -0
  228. package/dist/routes/runs.d.ts +8 -0
  229. package/dist/routes/runs.d.ts.map +1 -0
  230. package/dist/routes/runs.js +20 -0
  231. package/dist/routes/runs.js.map +1 -0
  232. package/dist/tsconfig.test.tsbuildinfo +1 -0
  233. package/package.json +114 -0
  234. package/src/components/identifier/identifier.tsx +178 -0
  235. package/src/components/identifier/index.ts +1 -0
  236. package/src/components/job-graph/graph-model.test.ts +189 -0
  237. package/src/components/job-graph/graph-model.ts +178 -0
  238. package/src/components/job-graph/index.ts +2 -0
  239. package/src/components/job-graph/job-duration-format.ts +54 -0
  240. package/src/components/job-graph/job-duration-label.tsx +29 -0
  241. package/src/components/job-graph/job-graph-content.tsx +225 -0
  242. package/src/components/job-graph/job-graph-view.test.tsx +298 -0
  243. package/src/components/job-graph/job-graph-view.tsx +45 -0
  244. package/src/components/job-graph/job-graph.stories.tsx +182 -0
  245. package/src/components/job-graph/job-graph.tsx +24 -0
  246. package/src/components/job-graph/job-node.stories.tsx +364 -0
  247. package/src/components/job-graph/job-node.test.tsx +347 -0
  248. package/src/components/job-graph/job-node.tsx +202 -0
  249. package/src/components/job-graph/types.ts +9 -0
  250. package/src/components/step-list/index.ts +6 -0
  251. package/src/components/step-list/step-list-model.test.ts +369 -0
  252. package/src/components/step-list/step-list-model.ts +205 -0
  253. package/src/components/step-list/step-list.stories.tsx +419 -0
  254. package/src/components/step-list/step-list.test.tsx +566 -0
  255. package/src/components/step-list/step-list.tsx +494 -0
  256. package/src/components/workflow-run-duration-label.tsx +118 -0
  257. package/src/components/workflow-run-list/index.ts +1 -0
  258. package/src/components/workflow-run-list/run-display.test.ts +53 -0
  259. package/src/components/workflow-run-list/run-display.ts +22 -0
  260. package/src/components/workflow-run-list/types.ts +22 -0
  261. package/src/components/workflow-run-list/workflow-run-list-content.tsx +58 -0
  262. package/src/components/workflow-run-list/workflow-run-list-header.tsx +53 -0
  263. package/src/components/workflow-run-list/workflow-run-list-states.tsx +86 -0
  264. package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +178 -0
  265. package/src/components/workflow-run-list/workflow-run-list-view.tsx +57 -0
  266. package/src/components/workflow-run-list/workflow-run-list.stories.tsx +111 -0
  267. package/src/components/workflow-run-list/workflow-run-list.tsx +29 -0
  268. package/src/components/workflow-run-list/workflow-run-row.tsx +176 -0
  269. package/src/components/workflow-run-summary/index.ts +1 -0
  270. package/src/components/workflow-run-summary/workflow-run-attempt-switcher.test.tsx +230 -0
  271. package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +146 -0
  272. package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +414 -0
  273. package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +354 -0
  274. package/src/components/workflow-run-summary/workflow-run-summary.tsx +295 -0
  275. package/src/components/workflow-run-view/agent-config-failure-callout.stories.tsx +140 -0
  276. package/src/components/workflow-run-view/agent-config-failure-callout.tsx +93 -0
  277. package/src/components/workflow-run-view/index.ts +2 -0
  278. package/src/components/workflow-run-view/job-card.stories.tsx +788 -0
  279. package/src/components/workflow-run-view/job-card.tsx +566 -0
  280. package/src/components/workflow-run-view/job-execution-switcher.tsx +170 -0
  281. package/src/components/workflow-run-view/job-execution-time-text.tsx +34 -0
  282. package/src/components/workflow-run-view/step-attempt-log-panel.test.tsx +290 -0
  283. package/src/components/workflow-run-view/step-attempt-log-panel.tsx +152 -0
  284. package/src/components/workflow-run-view/workflow-run-selection.test.ts +280 -0
  285. package/src/components/workflow-run-view/workflow-run-selection.ts +89 -0
  286. package/src/components/workflow-run-view/workflow-run-states.tsx +70 -0
  287. package/src/components/workflow-run-view/workflow-run-view.test.tsx +1214 -0
  288. package/src/components/workflow-run-view/workflow-run-view.tsx +341 -0
  289. package/src/components/workflow-source-panel/index.ts +1 -0
  290. package/src/components/workflow-source-panel/workflow-source-panel.stories.tsx +110 -0
  291. package/src/components/workflow-source-panel/workflow-source-panel.test.tsx +96 -0
  292. package/src/components/workflow-source-panel/workflow-source-panel.tsx +136 -0
  293. package/src/components/workflow-status/status-visuals.test.ts +38 -0
  294. package/src/components/workflow-status/status-visuals.ts +36 -0
  295. package/src/components/workflow-status/workflow-status-icon.stories.tsx +90 -0
  296. package/src/components/workflow-status/workflow-status-icon.tsx +135 -0
  297. package/src/core/entities/job-execution.ts +139 -0
  298. package/src/core/entities/job.ts +149 -0
  299. package/src/core/entities/step-attempt.ts +89 -0
  300. package/src/core/entities/step.ts +105 -0
  301. package/src/core/entities/workflow-run-attempt.ts +87 -0
  302. package/src/core/entities/workflow-run.ts +183 -0
  303. package/src/core/workflow-run-url-state.ts +61 -0
  304. package/src/core/workflow-run.test.ts +640 -0
  305. package/src/core/workflow-run.ts +66 -0
  306. package/src/env.d.ts +8 -0
  307. package/src/feature.ts +26 -0
  308. package/src/hooks/api/workflow-runs.test.tsx +410 -0
  309. package/src/hooks/api/workflow-runs.ts +485 -0
  310. package/src/index.ts +45 -0
  311. package/src/page-harness-budget.test.ts +33 -0
  312. package/src/pages/workflow-run-first-time-use.tsx +17 -0
  313. package/src/pages/workflow-run-page.test.tsx +517 -0
  314. package/src/pages/workflow-run-page.tsx +89 -0
  315. package/src/routes/run-detail.tsx +15 -0
  316. package/src/routes/runs.tsx +11 -0
  317. package/test/fixtures/logs.ts +13 -0
  318. package/test/fixtures/workflow-run.ts +311 -0
  319. package/test/pages.tsx +79 -0
  320. package/test/render.tsx +40 -0
  321. package/test/setup.ts +3 -0
  322. package/tsconfig.build.json +9 -0
  323. package/tsconfig.build.tsbuildinfo +1 -0
  324. package/tsconfig.json +3 -0
  325. package/tsconfig.test.json +8 -0
  326. package/vercel.json +8 -0
  327. package/vitest.config.ts +70 -0
@@ -0,0 +1,409 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@shipfox/react-ui/accordion';
3
+ import { Badge } from '@shipfox/react-ui/badge';
4
+ import { Dot } from '@shipfox/react-ui/dot';
5
+ import { EmptyState } from '@shipfox/react-ui/empty-state';
6
+ import { Icon } from '@shipfox/react-ui/icon';
7
+ import { TimeTickerProvider, useTimeTick } from '@shipfox/react-ui/time-ticker';
8
+ import { Tooltip, TooltipContent, TooltipTrigger } from '@shipfox/react-ui/tooltip';
9
+ import { Code, Text } from '@shipfox/react-ui/typography';
10
+ import { cn, humanDuration } from '@shipfox/react-ui/utils';
11
+ import { useEffect, useId, useMemo, useRef, useState } from 'react';
12
+ import { WorkflowStatusIcon } from '#components/workflow-status/workflow-status-icon.js';
13
+ import { isWorkflowStatus } from '#core/workflow-run.js';
14
+ import { formatJobExecutionTimeLabel } from '../job-graph/job-duration-format.js';
15
+ import { buildStepListModel, defaultStepListJobExecution, humanizeStatus } from './step-list-model.js';
16
+ export function StepList({ job, jobExecution, selectedAttemptId, defaultSelectedAttemptId, onSelectedAttemptChange, autoSelectActiveAttempt = false, emptyState, renderExpandedStep, showHeader = true, className }) {
17
+ const selectedJobExecution = jobExecution ?? defaultStepListJobExecution(job);
18
+ const model = useMemo(()=>buildStepListModel({
19
+ job,
20
+ jobExecution: selectedJobExecution
21
+ }), [
22
+ job,
23
+ selectedJobExecution
24
+ ]);
25
+ return /*#__PURE__*/ _jsx(StepListContent, {
26
+ model: model,
27
+ selectedAttemptId: selectedAttemptId,
28
+ defaultSelectedAttemptId: defaultSelectedAttemptId,
29
+ onSelectedAttemptChange: onSelectedAttemptChange,
30
+ autoSelectActiveAttempt: autoSelectActiveAttempt,
31
+ emptyState: emptyState,
32
+ renderExpandedStep: renderExpandedStep,
33
+ showHeader: showHeader,
34
+ className: className
35
+ }, model.jobExecutionId);
36
+ }
37
+ function StepListContent({ model, selectedAttemptId, defaultSelectedAttemptId, onSelectedAttemptChange, autoSelectActiveAttempt, emptyState, renderExpandedStep, showHeader, className }) {
38
+ const titleId = useId();
39
+ const [localSelectedAttemptIds, setLocalSelectedAttemptIds] = useState(()=>selectedAttemptId ? [
40
+ selectedAttemptId
41
+ ] : defaultSelectedAttemptId ? [
42
+ defaultSelectedAttemptId
43
+ ] : []);
44
+ const [userSelectedAttempt, setUserSelectedAttempt] = useState(false);
45
+ const lastNotifiedSelectedAttemptId = useRef(null);
46
+ const shouldUseControlledCollapsedState = selectedAttemptId === null && lastNotifiedSelectedAttemptId.current === null;
47
+ const autoSelectedAttemptIds = selectedAttemptId === undefined && autoSelectActiveAttempt && !userSelectedAttempt && model.activeEntryId ? [
48
+ model.activeEntryId
49
+ ] : [];
50
+ const selectedAttemptIds = shouldUseControlledCollapsedState ? [] : localSelectedAttemptIds.length > 0 ? localSelectedAttemptIds : autoSelectedAttemptIds;
51
+ const hasExpandedContent = renderExpandedStep !== undefined;
52
+ useEffect(()=>{
53
+ if (selectedAttemptId !== undefined) return;
54
+ setLocalSelectedAttemptIds(defaultSelectedAttemptId ? [
55
+ defaultSelectedAttemptId
56
+ ] : []);
57
+ setUserSelectedAttempt(false);
58
+ }, [
59
+ defaultSelectedAttemptId,
60
+ selectedAttemptId
61
+ ]);
62
+ useEffect(()=>{
63
+ if (selectedAttemptId === undefined) return;
64
+ const nextSelectedAttemptId = selectedAttemptId ?? null;
65
+ if (lastNotifiedSelectedAttemptId.current === nextSelectedAttemptId) {
66
+ lastNotifiedSelectedAttemptId.current = null;
67
+ return;
68
+ }
69
+ setLocalSelectedAttemptIds(selectedAttemptId ? [
70
+ selectedAttemptId
71
+ ] : []);
72
+ setUserSelectedAttempt(true);
73
+ }, [
74
+ selectedAttemptId
75
+ ]);
76
+ function selectAttempt(nextAttemptIds) {
77
+ const nextAttemptId = nextSelectedAttemptId(selectedAttemptIds, nextAttemptIds);
78
+ setUserSelectedAttempt(true);
79
+ setLocalSelectedAttemptIds(nextAttemptIds);
80
+ lastNotifiedSelectedAttemptId.current = nextAttemptId ?? null;
81
+ onSelectedAttemptChange?.(nextAttemptId);
82
+ }
83
+ return /*#__PURE__*/ _jsx(TimeTickerProvider, {
84
+ intervalMs: 1000,
85
+ reducedMotionIntervalMs: 10_000,
86
+ children: /*#__PURE__*/ _jsxs("section", {
87
+ "aria-labelledby": showHeader ? titleId : undefined,
88
+ className: cn('flex min-h-0 flex-col rounded-8 border border-border-neutral-base bg-background-components-base', className),
89
+ children: [
90
+ showHeader ? /*#__PURE__*/ _jsx("div", {
91
+ className: "flex min-h-40 items-center border-b border-border-neutral-base px-16 py-8",
92
+ children: /*#__PURE__*/ _jsx(Text, {
93
+ as: "h2",
94
+ id: titleId,
95
+ size: "sm",
96
+ bold: true,
97
+ className: "text-foreground-neutral-base",
98
+ children: model.jobName
99
+ })
100
+ }) : null,
101
+ model.entries.length === 0 ? /*#__PURE__*/ _jsx(StepListEmptyStateView, {
102
+ emptyState: emptyState
103
+ }) : /*#__PURE__*/ _jsx(Accordion, {
104
+ type: "multiple",
105
+ value: selectedAttemptIds,
106
+ onValueChange: selectAttempt,
107
+ asChild: true,
108
+ children: /*#__PURE__*/ _jsx("ol", {
109
+ children: model.entries.map((entry)=>{
110
+ const selected = selectedAttemptIds.includes(entry.id);
111
+ return /*#__PURE__*/ _jsx(StepRow, {
112
+ entry: entry,
113
+ selected: selected,
114
+ hasExpandedContent: hasExpandedContent,
115
+ onSelect: ()=>{
116
+ selectAttempt(hasExpandedContent ? toggleAttemptId(selectedAttemptIds, entry.id) : selected ? [] : [
117
+ entry.id
118
+ ]);
119
+ },
120
+ expandedContent: selected ? renderExpandedStep?.({
121
+ step: entry.step,
122
+ stepId: entry.step.id,
123
+ stepLabel: entry.step.label,
124
+ sourceLocation: entry.step.sourceLocation,
125
+ attempt: entry.attempt,
126
+ attemptId: entry.id,
127
+ attemptError: entry.error,
128
+ attemptStatus: entry.statusVisual.kind,
129
+ carriedOver: entry.carriedOver
130
+ }) : null
131
+ }, entry.id);
132
+ })
133
+ })
134
+ })
135
+ ]
136
+ })
137
+ });
138
+ }
139
+ function StepListEmptyStateView({ emptyState = {
140
+ title: 'No steps recorded',
141
+ description: 'This job has not recorded any steps.'
142
+ } }) {
143
+ if (!emptyState.status) {
144
+ return /*#__PURE__*/ _jsx(EmptyState, {
145
+ className: "min-h-120 px-16 py-20",
146
+ icon: "componentLine",
147
+ title: emptyState.title,
148
+ description: emptyState.description,
149
+ variant: "compact"
150
+ });
151
+ }
152
+ return /*#__PURE__*/ _jsxs("div", {
153
+ className: "flex min-h-120 flex-col items-center justify-center gap-10 px-16 py-20",
154
+ children: [
155
+ /*#__PURE__*/ _jsx(StepListEmptyStateIcon, {
156
+ status: emptyState.status
157
+ }),
158
+ /*#__PURE__*/ _jsxs("div", {
159
+ className: "text-center",
160
+ children: [
161
+ /*#__PURE__*/ _jsx(Text, {
162
+ size: "sm",
163
+ className: "text-foreground-neutral-subtle",
164
+ children: emptyState.title
165
+ }),
166
+ /*#__PURE__*/ _jsx(Text, {
167
+ size: "xs",
168
+ className: "text-foreground-neutral-muted",
169
+ children: emptyState.description
170
+ })
171
+ ]
172
+ })
173
+ ]
174
+ });
175
+ }
176
+ function StepListEmptyStateIcon({ status }) {
177
+ if (status !== 'running') {
178
+ return /*#__PURE__*/ _jsx("div", {
179
+ className: "flex size-32 items-center justify-center rounded-6 border border-border-neutral-strong bg-background-neutral-base p-8",
180
+ children: /*#__PURE__*/ _jsx(WorkflowStatusIcon, {
181
+ status: status,
182
+ size: 20,
183
+ tooltip: false
184
+ })
185
+ });
186
+ }
187
+ return /*#__PURE__*/ _jsx("div", {
188
+ className: "flex size-32 items-center justify-center rounded-6 border border-border-neutral-strong bg-background-neutral-base p-8 text-foreground-neutral-muted",
189
+ children: /*#__PURE__*/ _jsx(Icon, {
190
+ name: "timerLine",
191
+ size: 18,
192
+ "aria-hidden": "true"
193
+ })
194
+ });
195
+ }
196
+ function StepRow({ entry, selected, hasExpandedContent, onSelect, expandedContent }) {
197
+ const shouldShowLabelTooltip = entry.step.label.length > 32;
198
+ const rowContent = /*#__PURE__*/ _jsxs(_Fragment, {
199
+ children: [
200
+ /*#__PURE__*/ _jsx(Icon, {
201
+ name: "chevronRight",
202
+ "aria-hidden": "true",
203
+ className: cn('size-14 shrink-0 text-foreground-neutral-muted transition-transform', selected && 'rotate-90')
204
+ }),
205
+ /*#__PURE__*/ _jsx(StepStatusIcon, {
206
+ entry: entry
207
+ }),
208
+ /*#__PURE__*/ _jsx("div", {
209
+ className: "min-w-0 flex-1",
210
+ children: /*#__PURE__*/ _jsxs("div", {
211
+ className: "flex min-w-0 items-center gap-8",
212
+ children: [
213
+ /*#__PURE__*/ _jsx(Text, {
214
+ size: "sm",
215
+ bold: true,
216
+ className: "truncate text-foreground-neutral-base",
217
+ children: entry.step.label
218
+ }),
219
+ entry.step.attempts.length > 1 ? /*#__PURE__*/ _jsx(StepAttemptChip, {
220
+ attempt: entry
221
+ }) : null,
222
+ entry.carriedOver ? /*#__PURE__*/ _jsx(CarriedOverBadge, {}) : null
223
+ ]
224
+ })
225
+ }),
226
+ /*#__PURE__*/ _jsx(StepAttemptDurationLabel, {
227
+ attempt: entry
228
+ })
229
+ ]
230
+ });
231
+ const rowClasses = cn('group grid min-h-44 w-full grid-cols-[14px_14px_minmax(0,1fr)_auto] items-center gap-x-8 px-12 py-6 text-left transition-colors hover:bg-background-components-hover focus-visible:shadow-border-interactive-with-active focus-visible:outline-none', selected && 'bg-background-components-hover', entry.carriedOver && 'opacity-[0.55]');
232
+ const button = hasExpandedContent ? /*#__PURE__*/ _jsx(AccordionTrigger, {
233
+ showIcon: false,
234
+ "aria-label": entryAccessibleLabel(entry),
235
+ className: rowClasses,
236
+ children: rowContent
237
+ }) : /*#__PURE__*/ _jsx("button", {
238
+ type: "button",
239
+ "aria-expanded": false,
240
+ "aria-label": entryAccessibleLabel(entry),
241
+ onClick: onSelect,
242
+ className: rowClasses,
243
+ children: rowContent
244
+ });
245
+ const triggerNode = shouldShowLabelTooltip ? /*#__PURE__*/ _jsxs(Tooltip, {
246
+ children: [
247
+ /*#__PURE__*/ _jsx(TooltipTrigger, {
248
+ asChild: true,
249
+ children: button
250
+ }),
251
+ /*#__PURE__*/ _jsx(TooltipContent, {
252
+ children: /*#__PURE__*/ _jsx("span", {
253
+ className: "block max-w-320 break-words",
254
+ children: entry.step.label
255
+ })
256
+ })
257
+ ]
258
+ }) : button;
259
+ const row = /*#__PURE__*/ _jsxs(_Fragment, {
260
+ children: [
261
+ triggerNode,
262
+ selected && expandedContent ? /*#__PURE__*/ _jsx(AccordionContent, {
263
+ className: "border-t border-border-neutral-base bg-background-neutral-base px-12 py-12",
264
+ children: /*#__PURE__*/ _jsxs("div", {
265
+ className: "grid grid-cols-[14px_14px_minmax(0,1fr)_auto] gap-x-8",
266
+ children: [
267
+ /*#__PURE__*/ _jsx("span", {
268
+ "aria-hidden": "true"
269
+ }),
270
+ /*#__PURE__*/ _jsx("span", {
271
+ "aria-hidden": "true"
272
+ }),
273
+ /*#__PURE__*/ _jsx("div", {
274
+ className: "min-w-0",
275
+ children: expandedContent
276
+ }),
277
+ /*#__PURE__*/ _jsx("span", {
278
+ "aria-hidden": "true"
279
+ })
280
+ ]
281
+ })
282
+ }) : null
283
+ ]
284
+ });
285
+ if (hasExpandedContent) {
286
+ return /*#__PURE__*/ _jsx(AccordionItem, {
287
+ value: entry.id,
288
+ asChild: true,
289
+ children: /*#__PURE__*/ _jsx("li", {
290
+ children: row
291
+ })
292
+ });
293
+ }
294
+ return /*#__PURE__*/ _jsx("li", {
295
+ className: "border-b border-border-neutral-base last:border-b-0",
296
+ children: row
297
+ });
298
+ }
299
+ function CarriedOverBadge() {
300
+ return /*#__PURE__*/ _jsxs(Tooltip, {
301
+ children: [
302
+ /*#__PURE__*/ _jsx(TooltipTrigger, {
303
+ asChild: true,
304
+ children: /*#__PURE__*/ _jsx("span", {
305
+ className: "shrink-0",
306
+ children: /*#__PURE__*/ _jsx(Badge, {
307
+ variant: "neutral",
308
+ size: "2xs",
309
+ children: "reused"
310
+ })
311
+ })
312
+ }),
313
+ /*#__PURE__*/ _jsx(TooltipContent, {
314
+ children: "Carried over from a previous attempt; did not run in this attempt."
315
+ })
316
+ ]
317
+ });
318
+ }
319
+ function toggleAttemptId(selectedAttemptIds, attemptId) {
320
+ if (selectedAttemptIds.includes(attemptId)) {
321
+ return selectedAttemptIds.filter((selectedAttemptId)=>selectedAttemptId !== attemptId);
322
+ }
323
+ return [
324
+ ...selectedAttemptIds,
325
+ attemptId
326
+ ];
327
+ }
328
+ function nextSelectedAttemptId(selectedAttemptIds, nextAttemptIds) {
329
+ if (nextAttemptIds.length === 0) return undefined;
330
+ const openedAttemptId = nextAttemptIds.find((attemptId)=>!selectedAttemptIds.includes(attemptId));
331
+ return openedAttemptId ?? nextAttemptIds.at(-1);
332
+ }
333
+ function StepStatusIcon({ entry }) {
334
+ if (isWorkflowStatus(entry.statusVisual.kind)) {
335
+ return /*#__PURE__*/ _jsx(WorkflowStatusIcon, {
336
+ status: entry.statusVisual.kind,
337
+ ripple: entry.statusVisual.ripple,
338
+ size: 14
339
+ });
340
+ }
341
+ return /*#__PURE__*/ _jsx("span", {
342
+ role: "img",
343
+ "aria-label": entry.statusVisual.label,
344
+ className: "inline-flex shrink-0",
345
+ children: /*#__PURE__*/ _jsx(Dot, {
346
+ variant: entry.statusVisual.dot,
347
+ ripple: entry.statusVisual.ripple,
348
+ className: "size-12 shrink-0"
349
+ })
350
+ });
351
+ }
352
+ const attemptChipClasses = {
353
+ neutral: 'bg-tag-neutral-bg border-tag-neutral-border',
354
+ info: 'bg-tag-blue-bg border-tag-blue-border',
355
+ feature: 'bg-tag-purple-bg border-tag-purple-border',
356
+ success: 'bg-tag-success-bg border-tag-success-border',
357
+ warning: 'bg-tag-warning-bg border-tag-warning-border',
358
+ error: 'bg-tag-error-bg border-tag-error-border'
359
+ };
360
+ function StepAttemptChip({ attempt }) {
361
+ return /*#__PURE__*/ _jsx("div", {
362
+ className: "flex shrink-0 items-center gap-4",
363
+ "aria-hidden": "true",
364
+ children: /*#__PURE__*/ _jsxs("span", {
365
+ className: cn('inline-flex h-18 min-w-24 items-center justify-center rounded-4 border px-5 font-code text-xs leading-16 text-foreground-neutral-base', attemptChipClasses[attempt.statusVisual.badge ?? 'neutral']),
366
+ children: [
367
+ "#",
368
+ attempt.attempt
369
+ ]
370
+ })
371
+ });
372
+ }
373
+ function entryAccessibleLabel(entry) {
374
+ const parts = [
375
+ entry.step.label,
376
+ entry.statusVisual.label,
377
+ `attempt ${entry.attempt}`
378
+ ];
379
+ if (entry.step.error?.category) parts.push(humanizeStatus(entry.step.error.category));
380
+ return parts.join(', ');
381
+ }
382
+ function StepAttemptDurationLabel({ attempt }) {
383
+ const duration = attempt.displayDuration;
384
+ if (duration === null) return null;
385
+ if (duration.state === 'live') {
386
+ return /*#__PURE__*/ _jsx(LiveDurationText, {
387
+ fromIso: duration.fromIso
388
+ });
389
+ }
390
+ return /*#__PURE__*/ _jsx(DurationText, {
391
+ children: formatJobExecutionTimeLabel(duration)
392
+ });
393
+ }
394
+ function LiveDurationText({ fromIso }) {
395
+ useTimeTick();
396
+ return /*#__PURE__*/ _jsx(DurationText, {
397
+ children: humanDuration(fromIso)
398
+ });
399
+ }
400
+ function DurationText({ children }) {
401
+ return /*#__PURE__*/ _jsx(Code, {
402
+ as: "span",
403
+ variant: "label",
404
+ className: "shrink-0 tabular-nums text-foreground-neutral-muted",
405
+ children: children
406
+ });
407
+ }
408
+
409
+ //# sourceMappingURL=step-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/step-list/step-list.tsx"],"sourcesContent":["import {\n Accordion,\n AccordionContent,\n AccordionItem,\n AccordionTrigger,\n} from '@shipfox/react-ui/accordion';\nimport {Badge, type BadgeVariant} from '@shipfox/react-ui/badge';\nimport {Dot} from '@shipfox/react-ui/dot';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {TimeTickerProvider, useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn, humanDuration} from '@shipfox/react-ui/utils';\nimport type {ReactNode} from 'react';\nimport {useEffect, useId, useMemo, useRef, useState} from 'react';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {\n isWorkflowStatus,\n type Job,\n type JobExecution,\n type Step,\n type StepSourceLocation,\n} from '#core/workflow-run.js';\nimport {formatJobExecutionTimeLabel} from '../job-graph/job-duration-format.js';\nimport {\n buildStepListModel,\n defaultStepListJobExecution,\n humanizeStatus,\n type StepAttemptModel,\n type StepListEntryModel,\n type StepListModel,\n} from './step-list-model.js';\n\nexport interface StepExpandedContext {\n step: Step;\n stepId: string;\n stepLabel: string;\n sourceLocation: StepSourceLocation | null;\n attempt: number;\n attemptId: string;\n attemptError: Record<string, unknown> | null;\n attemptStatus: string;\n carriedOver: boolean;\n}\n\nexport interface StepListEmptyState {\n title: string;\n description: string;\n status?: Job['status'] | undefined;\n}\n\nexport interface StepListProps {\n job: Job;\n jobExecution?: JobExecution | undefined;\n selectedAttemptId?: string | null | undefined;\n defaultSelectedAttemptId?: string | undefined;\n onSelectedAttemptChange?: ((attemptId: string | undefined) => void) | undefined;\n autoSelectActiveAttempt?: boolean | undefined;\n emptyState?: StepListEmptyState | undefined;\n renderExpandedStep?: ((context: StepExpandedContext) => ReactNode) | undefined;\n showHeader?: boolean | undefined;\n className?: string | undefined;\n}\n\nexport function StepList({\n job,\n jobExecution,\n selectedAttemptId,\n defaultSelectedAttemptId,\n onSelectedAttemptChange,\n autoSelectActiveAttempt = false,\n emptyState,\n renderExpandedStep,\n showHeader = true,\n className,\n}: StepListProps) {\n const selectedJobExecution = jobExecution ?? defaultStepListJobExecution(job);\n const model = useMemo(\n () => buildStepListModel({job, jobExecution: selectedJobExecution}),\n [job, selectedJobExecution],\n );\n\n return (\n <StepListContent\n key={model.jobExecutionId}\n model={model}\n selectedAttemptId={selectedAttemptId}\n defaultSelectedAttemptId={defaultSelectedAttemptId}\n onSelectedAttemptChange={onSelectedAttemptChange}\n autoSelectActiveAttempt={autoSelectActiveAttempt}\n emptyState={emptyState}\n renderExpandedStep={renderExpandedStep}\n showHeader={showHeader}\n className={className}\n />\n );\n}\n\nfunction StepListContent({\n model,\n selectedAttemptId,\n defaultSelectedAttemptId,\n onSelectedAttemptChange,\n autoSelectActiveAttempt,\n emptyState,\n renderExpandedStep,\n showHeader,\n className,\n}: Omit<StepListProps, 'job' | 'jobExecution'> & {model: StepListModel}) {\n const titleId = useId();\n const [localSelectedAttemptIds, setLocalSelectedAttemptIds] = useState<string[]>(() =>\n selectedAttemptId\n ? [selectedAttemptId]\n : defaultSelectedAttemptId\n ? [defaultSelectedAttemptId]\n : [],\n );\n const [userSelectedAttempt, setUserSelectedAttempt] = useState(false);\n const lastNotifiedSelectedAttemptId = useRef<string | null>(null);\n const shouldUseControlledCollapsedState =\n selectedAttemptId === null && lastNotifiedSelectedAttemptId.current === null;\n const autoSelectedAttemptIds =\n selectedAttemptId === undefined &&\n autoSelectActiveAttempt &&\n !userSelectedAttempt &&\n model.activeEntryId\n ? [model.activeEntryId]\n : [];\n const selectedAttemptIds = shouldUseControlledCollapsedState\n ? []\n : localSelectedAttemptIds.length > 0\n ? localSelectedAttemptIds\n : autoSelectedAttemptIds;\n const hasExpandedContent = renderExpandedStep !== undefined;\n\n useEffect(() => {\n if (selectedAttemptId !== undefined) return;\n\n setLocalSelectedAttemptIds(defaultSelectedAttemptId ? [defaultSelectedAttemptId] : []);\n setUserSelectedAttempt(false);\n }, [defaultSelectedAttemptId, selectedAttemptId]);\n\n useEffect(() => {\n if (selectedAttemptId === undefined) return;\n\n const nextSelectedAttemptId = selectedAttemptId ?? null;\n if (lastNotifiedSelectedAttemptId.current === nextSelectedAttemptId) {\n lastNotifiedSelectedAttemptId.current = null;\n return;\n }\n\n setLocalSelectedAttemptIds(selectedAttemptId ? [selectedAttemptId] : []);\n setUserSelectedAttempt(true);\n }, [selectedAttemptId]);\n\n function selectAttempt(nextAttemptIds: string[]) {\n const nextAttemptId = nextSelectedAttemptId(selectedAttemptIds, nextAttemptIds);\n setUserSelectedAttempt(true);\n setLocalSelectedAttemptIds(nextAttemptIds);\n lastNotifiedSelectedAttemptId.current = nextAttemptId ?? null;\n onSelectedAttemptChange?.(nextAttemptId);\n }\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <section\n aria-labelledby={showHeader ? titleId : undefined}\n className={cn(\n 'flex min-h-0 flex-col rounded-8 border border-border-neutral-base bg-background-components-base',\n className,\n )}\n >\n {showHeader ? (\n <div className=\"flex min-h-40 items-center border-b border-border-neutral-base px-16 py-8\">\n <Text as=\"h2\" id={titleId} size=\"sm\" bold className=\"text-foreground-neutral-base\">\n {model.jobName}\n </Text>\n </div>\n ) : null}\n\n {model.entries.length === 0 ? (\n <StepListEmptyStateView emptyState={emptyState} />\n ) : (\n <Accordion\n type=\"multiple\"\n value={selectedAttemptIds}\n onValueChange={selectAttempt}\n asChild\n >\n <ol>\n {model.entries.map((entry) => {\n const selected = selectedAttemptIds.includes(entry.id);\n return (\n <StepRow\n key={entry.id}\n entry={entry}\n selected={selected}\n hasExpandedContent={hasExpandedContent}\n onSelect={() => {\n selectAttempt(\n hasExpandedContent\n ? toggleAttemptId(selectedAttemptIds, entry.id)\n : selected\n ? []\n : [entry.id],\n );\n }}\n expandedContent={\n selected\n ? renderExpandedStep?.({\n step: entry.step,\n stepId: entry.step.id,\n stepLabel: entry.step.label,\n sourceLocation: entry.step.sourceLocation,\n attempt: entry.attempt,\n attemptId: entry.id,\n attemptError: entry.error,\n attemptStatus: entry.statusVisual.kind,\n carriedOver: entry.carriedOver,\n })\n : null\n }\n />\n );\n })}\n </ol>\n </Accordion>\n )}\n </section>\n </TimeTickerProvider>\n );\n}\n\nfunction StepListEmptyStateView({\n emptyState = {\n title: 'No steps recorded',\n description: 'This job has not recorded any steps.',\n },\n}: {\n emptyState?: StepListEmptyState | undefined;\n}) {\n if (!emptyState.status) {\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\n return (\n <div className=\"flex min-h-120 flex-col items-center justify-center gap-10 px-16 py-20\">\n <StepListEmptyStateIcon status={emptyState.status} />\n <div className=\"text-center\">\n <Text size=\"sm\" className=\"text-foreground-neutral-subtle\">\n {emptyState.title}\n </Text>\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n {emptyState.description}\n </Text>\n </div>\n </div>\n );\n}\n\nfunction StepListEmptyStateIcon({status}: {status: Job['status']}) {\n if (status !== 'running') {\n return (\n <div className=\"flex size-32 items-center justify-center rounded-6 border border-border-neutral-strong bg-background-neutral-base p-8\">\n <WorkflowStatusIcon status={status} size={20} tooltip={false} />\n </div>\n );\n }\n\n return (\n <div className=\"flex size-32 items-center justify-center rounded-6 border border-border-neutral-strong bg-background-neutral-base p-8 text-foreground-neutral-muted\">\n <Icon name=\"timerLine\" size={18} aria-hidden=\"true\" />\n </div>\n );\n}\n\nfunction StepRow({\n entry,\n selected,\n hasExpandedContent,\n onSelect,\n expandedContent,\n}: {\n entry: StepListEntryModel;\n selected: boolean;\n hasExpandedContent: boolean;\n onSelect: () => void;\n expandedContent: ReactNode;\n}) {\n const shouldShowLabelTooltip = entry.step.label.length > 32;\n const rowContent = (\n <>\n <Icon\n name=\"chevronRight\"\n aria-hidden=\"true\"\n className={cn(\n 'size-14 shrink-0 text-foreground-neutral-muted transition-transform',\n selected && 'rotate-90',\n )}\n />\n <StepStatusIcon entry={entry} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex min-w-0 items-center gap-8\">\n <Text size=\"sm\" bold className=\"truncate text-foreground-neutral-base\">\n {entry.step.label}\n </Text>\n {entry.step.attempts.length > 1 ? <StepAttemptChip attempt={entry} /> : null}\n {entry.carriedOver ? <CarriedOverBadge /> : null}\n </div>\n </div>\n <StepAttemptDurationLabel attempt={entry} />\n </>\n );\n const rowClasses = cn(\n 'group grid min-h-44 w-full grid-cols-[14px_14px_minmax(0,1fr)_auto] items-center gap-x-8 px-12 py-6 text-left transition-colors hover:bg-background-components-hover focus-visible:shadow-border-interactive-with-active focus-visible:outline-none',\n selected && 'bg-background-components-hover',\n entry.carriedOver && 'opacity-[0.55]',\n );\n const button = hasExpandedContent ? (\n <AccordionTrigger\n showIcon={false}\n aria-label={entryAccessibleLabel(entry)}\n className={rowClasses}\n >\n {rowContent}\n </AccordionTrigger>\n ) : (\n <button\n type=\"button\"\n aria-expanded={false}\n aria-label={entryAccessibleLabel(entry)}\n onClick={onSelect}\n className={rowClasses}\n >\n {rowContent}\n </button>\n );\n const triggerNode = shouldShowLabelTooltip ? (\n <Tooltip>\n <TooltipTrigger asChild>{button}</TooltipTrigger>\n <TooltipContent>\n <span className=\"block max-w-320 break-words\">{entry.step.label}</span>\n </TooltipContent>\n </Tooltip>\n ) : (\n button\n );\n const row = (\n <>\n {triggerNode}\n {selected && expandedContent ? (\n <AccordionContent className=\"border-t border-border-neutral-base bg-background-neutral-base px-12 py-12\">\n <div className=\"grid grid-cols-[14px_14px_minmax(0,1fr)_auto] gap-x-8\">\n <span aria-hidden=\"true\" />\n <span aria-hidden=\"true\" />\n <div className=\"min-w-0\">{expandedContent}</div>\n <span aria-hidden=\"true\" />\n </div>\n </AccordionContent>\n ) : null}\n </>\n );\n\n if (hasExpandedContent) {\n return (\n <AccordionItem value={entry.id} asChild>\n <li>{row}</li>\n </AccordionItem>\n );\n }\n\n return <li className=\"border-b border-border-neutral-base last:border-b-0\">{row}</li>;\n}\n\nfunction CarriedOverBadge() {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"shrink-0\">\n <Badge variant=\"neutral\" size=\"2xs\">\n reused\n </Badge>\n </span>\n </TooltipTrigger>\n <TooltipContent>\n Carried over from a previous attempt; did not run in this attempt.\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction toggleAttemptId(selectedAttemptIds: readonly string[], attemptId: string): string[] {\n if (selectedAttemptIds.includes(attemptId)) {\n return selectedAttemptIds.filter((selectedAttemptId) => selectedAttemptId !== attemptId);\n }\n return [...selectedAttemptIds, attemptId];\n}\n\nfunction nextSelectedAttemptId(\n selectedAttemptIds: readonly string[],\n nextAttemptIds: readonly string[],\n): string | undefined {\n if (nextAttemptIds.length === 0) return undefined;\n\n const openedAttemptId = nextAttemptIds.find(\n (attemptId) => !selectedAttemptIds.includes(attemptId),\n );\n return openedAttemptId ?? nextAttemptIds.at(-1);\n}\n\nfunction StepStatusIcon({entry}: {entry: StepListEntryModel}) {\n if (isWorkflowStatus(entry.statusVisual.kind)) {\n return (\n <WorkflowStatusIcon\n status={entry.statusVisual.kind}\n ripple={entry.statusVisual.ripple}\n size={14}\n />\n );\n }\n\n return (\n <span role=\"img\" aria-label={entry.statusVisual.label} className=\"inline-flex shrink-0\">\n <Dot\n variant={entry.statusVisual.dot}\n ripple={entry.statusVisual.ripple}\n className=\"size-12 shrink-0\"\n />\n </span>\n );\n}\n\nconst attemptChipClasses: Record<NonNullable<BadgeVariant>, string> = {\n neutral: 'bg-tag-neutral-bg border-tag-neutral-border',\n info: 'bg-tag-blue-bg border-tag-blue-border',\n feature: 'bg-tag-purple-bg border-tag-purple-border',\n success: 'bg-tag-success-bg border-tag-success-border',\n warning: 'bg-tag-warning-bg border-tag-warning-border',\n error: 'bg-tag-error-bg border-tag-error-border',\n};\n\nfunction StepAttemptChip({attempt}: {attempt: StepAttemptModel}) {\n return (\n <div className=\"flex shrink-0 items-center gap-4\" aria-hidden=\"true\">\n <span\n className={cn(\n 'inline-flex h-18 min-w-24 items-center justify-center rounded-4 border px-5 font-code text-xs leading-16 text-foreground-neutral-base',\n attemptChipClasses[attempt.statusVisual.badge ?? 'neutral'],\n )}\n >\n #{attempt.attempt}\n </span>\n </div>\n );\n}\n\nfunction entryAccessibleLabel(entry: StepListEntryModel): string {\n const parts = [entry.step.label, entry.statusVisual.label, `attempt ${entry.attempt}`];\n if (entry.step.error?.category) parts.push(humanizeStatus(entry.step.error.category));\n return parts.join(', ');\n}\n\nfunction StepAttemptDurationLabel({attempt}: {attempt: StepAttemptModel}) {\n const duration = attempt.displayDuration;\n if (duration === null) return null;\n\n if (duration.state === 'live') {\n return <LiveDurationText fromIso={duration.fromIso} />;\n }\n\n return <DurationText>{formatJobExecutionTimeLabel(duration)}</DurationText>;\n}\n\nfunction LiveDurationText({fromIso}: {fromIso: string}) {\n useTimeTick();\n return <DurationText>{humanDuration(fromIso)}</DurationText>;\n}\n\nfunction DurationText({children}: {children: string}) {\n return (\n <Code as=\"span\" variant=\"label\" className=\"shrink-0 tabular-nums text-foreground-neutral-muted\">\n {children}\n </Code>\n );\n}\n"],"names":["Accordion","AccordionContent","AccordionItem","AccordionTrigger","Badge","Dot","EmptyState","Icon","TimeTickerProvider","useTimeTick","Tooltip","TooltipContent","TooltipTrigger","Code","Text","cn","humanDuration","useEffect","useId","useMemo","useRef","useState","WorkflowStatusIcon","isWorkflowStatus","formatJobExecutionTimeLabel","buildStepListModel","defaultStepListJobExecution","humanizeStatus","StepList","job","jobExecution","selectedAttemptId","defaultSelectedAttemptId","onSelectedAttemptChange","autoSelectActiveAttempt","emptyState","renderExpandedStep","showHeader","className","selectedJobExecution","model","StepListContent","jobExecutionId","titleId","localSelectedAttemptIds","setLocalSelectedAttemptIds","userSelectedAttempt","setUserSelectedAttempt","lastNotifiedSelectedAttemptId","shouldUseControlledCollapsedState","current","autoSelectedAttemptIds","undefined","activeEntryId","selectedAttemptIds","length","hasExpandedContent","nextSelectedAttemptId","selectAttempt","nextAttemptIds","nextAttemptId","intervalMs","reducedMotionIntervalMs","section","aria-labelledby","div","as","id","size","bold","jobName","entries","StepListEmptyStateView","type","value","onValueChange","asChild","ol","map","entry","selected","includes","StepRow","onSelect","toggleAttemptId","expandedContent","step","stepId","stepLabel","label","sourceLocation","attempt","attemptId","attemptError","error","attemptStatus","statusVisual","kind","carriedOver","title","description","status","icon","variant","StepListEmptyStateIcon","tooltip","name","aria-hidden","shouldShowLabelTooltip","rowContent","StepStatusIcon","attempts","StepAttemptChip","CarriedOverBadge","StepAttemptDurationLabel","rowClasses","button","showIcon","aria-label","entryAccessibleLabel","aria-expanded","onClick","triggerNode","span","row","li","filter","openedAttemptId","find","at","ripple","role","dot","attemptChipClasses","neutral","info","feature","success","warning","badge","parts","category","push","join","duration","displayDuration","state","LiveDurationText","fromIso","DurationText","children"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { WorkflowRunAttemptDisplayDuration } from '#core/workflow-run.js';
2
+ export declare function WorkflowRunDurationLabel({ duration, className, }: {
3
+ duration: WorkflowRunAttemptDisplayDuration | null;
4
+ className?: string | undefined;
5
+ }): import("react").JSX.Element | null;
6
+ export declare function useWorkflowRunDurationAccessibleLabel(duration: WorkflowRunAttemptDisplayDuration | null): string | undefined;
7
+ export declare function workflowRunDurationAccessibleLabel(duration: WorkflowRunAttemptDisplayDuration | null): string | undefined;
8
+ //# sourceMappingURL=workflow-run-duration-label.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-run-duration-label.d.ts","sourceRoot":"","sources":["../../src/components/workflow-run-duration-label.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,iCAAiC,EAAC,MAAM,uBAAuB,CAAC;AAE7E,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,SAAS,GACV,EAAE;IACD,QAAQ,EAAE,iCAAiC,GAAG,IAAI,CAAC;IACnD,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,sCAmBA;AAED,wBAAgB,qCAAqC,CACnD,QAAQ,EAAE,iCAAiC,GAAG,IAAI,GACjD,MAAM,GAAG,SAAS,CAGpB;AAED,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,iCAAiC,GAAG,IAAI,GACjD,MAAM,GAAG,SAAS,CAapB"}
@@ -0,0 +1,84 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Icon } from '@shipfox/react-ui/icon';
3
+ import { useTimeTick } from '@shipfox/react-ui/time-ticker';
4
+ import { Code } from '@shipfox/react-ui/typography';
5
+ import { cn, humanDuration } from '@shipfox/react-ui/utils';
6
+ export function WorkflowRunDurationLabel({ duration, className }) {
7
+ if (duration === null) return null;
8
+ switch(duration.state){
9
+ case 'fixed':
10
+ {
11
+ const display = formatFixedDurationLabel(duration.elapsed);
12
+ return /*#__PURE__*/ _jsx(DurationText, {
13
+ className: className,
14
+ ariaLabel: `ran ${display}`,
15
+ children: display
16
+ });
17
+ }
18
+ case 'live':
19
+ return /*#__PURE__*/ _jsx(LiveDurationText, {
20
+ duration: duration,
21
+ className: className
22
+ });
23
+ default:
24
+ {
25
+ const exhaustive = duration;
26
+ return exhaustive;
27
+ }
28
+ }
29
+ }
30
+ export function useWorkflowRunDurationAccessibleLabel(duration) {
31
+ useTimeTick();
32
+ return workflowRunDurationAccessibleLabel(duration);
33
+ }
34
+ export function workflowRunDurationAccessibleLabel(duration) {
35
+ if (duration === null) return undefined;
36
+ switch(duration.state){
37
+ case 'live':
38
+ return `running ${humanDuration(duration.fromIso)}`;
39
+ case 'fixed':
40
+ return `ran ${formatFixedDurationLabel(duration.elapsed)}`;
41
+ default:
42
+ {
43
+ const exhaustive = duration;
44
+ return exhaustive;
45
+ }
46
+ }
47
+ }
48
+ function LiveDurationText({ duration, className }) {
49
+ useTimeTick();
50
+ const display = humanDuration(duration.fromIso);
51
+ return /*#__PURE__*/ _jsx(DurationText, {
52
+ className: className,
53
+ ariaLabel: `running ${display}`,
54
+ children: display
55
+ });
56
+ }
57
+ function DurationText({ children, className, ariaLabel }) {
58
+ return /*#__PURE__*/ _jsxs(Code, {
59
+ as: "span",
60
+ variant: "label",
61
+ "aria-label": ariaLabel,
62
+ className: cn('inline-flex shrink-0 items-center gap-4 tabular-nums text-foreground-neutral-muted', className),
63
+ children: [
64
+ /*#__PURE__*/ _jsx(Icon, {
65
+ name: "timerLine",
66
+ className: "size-12 shrink-0",
67
+ "aria-hidden": "true"
68
+ }),
69
+ children
70
+ ]
71
+ });
72
+ }
73
+ function formatFixedDurationLabel({ years = 0, months = 0, weeks = 0, days = 0, hours = 0, minutes = 0, seconds = 0 }) {
74
+ const totalDays = years * 365 + months * 30 + weeks * 7 + days;
75
+ const totalHours = totalDays * 24 + hours;
76
+ if (totalHours > 0) return `${totalHours}h ${pad2(minutes)}m`;
77
+ if (minutes > 0) return `${minutes}m ${pad2(seconds)}s`;
78
+ return `${seconds}s`;
79
+ }
80
+ function pad2(value) {
81
+ return value.toString().padStart(2, '0');
82
+ }
83
+
84
+ //# sourceMappingURL=workflow-run-duration-label.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/workflow-run-duration-label.tsx"],"sourcesContent":["import {Icon} from '@shipfox/react-ui/icon';\nimport {useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Code} from '@shipfox/react-ui/typography';\nimport {cn, humanDuration} from '@shipfox/react-ui/utils';\nimport type {WorkflowRunAttemptDisplayDuration} from '#core/workflow-run.js';\n\nexport function WorkflowRunDurationLabel({\n duration,\n className,\n}: {\n duration: WorkflowRunAttemptDisplayDuration | null;\n className?: string | undefined;\n}) {\n if (duration === null) return null;\n\n switch (duration.state) {\n case 'fixed': {\n const display = formatFixedDurationLabel(duration.elapsed);\n return (\n <DurationText className={className} ariaLabel={`ran ${display}`}>\n {display}\n </DurationText>\n );\n }\n case 'live':\n return <LiveDurationText duration={duration} className={className} />;\n default: {\n const exhaustive: never = duration;\n return exhaustive;\n }\n }\n}\n\nexport function useWorkflowRunDurationAccessibleLabel(\n duration: WorkflowRunAttemptDisplayDuration | null,\n): string | undefined {\n useTimeTick();\n return workflowRunDurationAccessibleLabel(duration);\n}\n\nexport function workflowRunDurationAccessibleLabel(\n duration: WorkflowRunAttemptDisplayDuration | null,\n): string | undefined {\n if (duration === null) return undefined;\n\n switch (duration.state) {\n case 'live':\n return `running ${humanDuration(duration.fromIso)}`;\n case 'fixed':\n return `ran ${formatFixedDurationLabel(duration.elapsed)}`;\n default: {\n const exhaustive: never = duration;\n return exhaustive;\n }\n }\n}\n\nfunction LiveDurationText({\n duration,\n className,\n}: {\n duration: Extract<WorkflowRunAttemptDisplayDuration, {state: 'live'}>;\n className?: string | undefined;\n}) {\n useTimeTick();\n const display = humanDuration(duration.fromIso);\n return (\n <DurationText className={className} ariaLabel={`running ${display}`}>\n {display}\n </DurationText>\n );\n}\n\nfunction DurationText({\n children,\n className,\n ariaLabel,\n}: {\n children: string;\n className?: string | undefined;\n ariaLabel?: string | undefined;\n}) {\n return (\n <Code\n as=\"span\"\n variant=\"label\"\n aria-label={ariaLabel}\n className={cn(\n 'inline-flex shrink-0 items-center gap-4 tabular-nums text-foreground-neutral-muted',\n className,\n )}\n >\n <Icon name=\"timerLine\" className=\"size-12 shrink-0\" aria-hidden=\"true\" />\n {children}\n </Code>\n );\n}\n\nfunction formatFixedDurationLabel({\n years = 0,\n months = 0,\n weeks = 0,\n days = 0,\n hours = 0,\n minutes = 0,\n seconds = 0,\n}: Extract<WorkflowRunAttemptDisplayDuration, {state: 'fixed'}>['elapsed']): string {\n const totalDays = years * 365 + months * 30 + weeks * 7 + days;\n const totalHours = totalDays * 24 + hours;\n\n if (totalHours > 0) return `${totalHours}h ${pad2(minutes)}m`;\n if (minutes > 0) return `${minutes}m ${pad2(seconds)}s`;\n return `${seconds}s`;\n}\n\nfunction pad2(value: number): string {\n return value.toString().padStart(2, '0');\n}\n"],"names":["Icon","useTimeTick","Code","cn","humanDuration","WorkflowRunDurationLabel","duration","className","state","display","formatFixedDurationLabel","elapsed","DurationText","ariaLabel","LiveDurationText","exhaustive","useWorkflowRunDurationAccessibleLabel","workflowRunDurationAccessibleLabel","undefined","fromIso","children","as","variant","aria-label","name","aria-hidden","years","months","weeks","days","hours","minutes","seconds","totalDays","totalHours","pad2","value","toString","padStart"],"mappings":";AAAA,SAAQA,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,EAAE,EAAEC,aAAa,QAAO,0BAA0B;AAG1D,OAAO,SAASC,yBAAyB,EACvCC,QAAQ,EACRC,SAAS,EAIV;IACC,IAAID,aAAa,MAAM,OAAO;IAE9B,OAAQA,SAASE,KAAK;QACpB,KAAK;YAAS;gBACZ,MAAMC,UAAUC,yBAAyBJ,SAASK,OAAO;gBACzD,qBACE,KAACC;oBAAaL,WAAWA;oBAAWM,WAAW,CAAC,IAAI,EAAEJ,SAAS;8BAC5DA;;YAGP;QACA,KAAK;YACH,qBAAO,KAACK;gBAAiBR,UAAUA;gBAAUC,WAAWA;;QAC1D;YAAS;gBACP,MAAMQ,aAAoBT;gBAC1B,OAAOS;YACT;IACF;AACF;AAEA,OAAO,SAASC,sCACdV,QAAkD;IAElDL;IACA,OAAOgB,mCAAmCX;AAC5C;AAEA,OAAO,SAASW,mCACdX,QAAkD;IAElD,IAAIA,aAAa,MAAM,OAAOY;IAE9B,OAAQZ,SAASE,KAAK;QACpB,KAAK;YACH,OAAO,CAAC,QAAQ,EAAEJ,cAAcE,SAASa,OAAO,GAAG;QACrD,KAAK;YACH,OAAO,CAAC,IAAI,EAAET,yBAAyBJ,SAASK,OAAO,GAAG;QAC5D;YAAS;gBACP,MAAMI,aAAoBT;gBAC1B,OAAOS;YACT;IACF;AACF;AAEA,SAASD,iBAAiB,EACxBR,QAAQ,EACRC,SAAS,EAIV;IACCN;IACA,MAAMQ,UAAUL,cAAcE,SAASa,OAAO;IAC9C,qBACE,KAACP;QAAaL,WAAWA;QAAWM,WAAW,CAAC,QAAQ,EAAEJ,SAAS;kBAChEA;;AAGP;AAEA,SAASG,aAAa,EACpBQ,QAAQ,EACRb,SAAS,EACTM,SAAS,EAKV;IACC,qBACE,MAACX;QACCmB,IAAG;QACHC,SAAQ;QACRC,cAAYV;QACZN,WAAWJ,GACT,sFACAI;;0BAGF,KAACP;gBAAKwB,MAAK;gBAAYjB,WAAU;gBAAmBkB,eAAY;;YAC/DL;;;AAGP;AAEA,SAASV,yBAAyB,EAChCgB,QAAQ,CAAC,EACTC,SAAS,CAAC,EACVC,QAAQ,CAAC,EACTC,OAAO,CAAC,EACRC,QAAQ,CAAC,EACTC,UAAU,CAAC,EACXC,UAAU,CAAC,EAC6D;IACxE,MAAMC,YAAYP,QAAQ,MAAMC,SAAS,KAAKC,QAAQ,IAAIC;IAC1D,MAAMK,aAAaD,YAAY,KAAKH;IAEpC,IAAII,aAAa,GAAG,OAAO,GAAGA,WAAW,EAAE,EAAEC,KAAKJ,SAAS,CAAC,CAAC;IAC7D,IAAIA,UAAU,GAAG,OAAO,GAAGA,QAAQ,EAAE,EAAEI,KAAKH,SAAS,CAAC,CAAC;IACvD,OAAO,GAAGA,QAAQ,CAAC,CAAC;AACtB;AAEA,SAASG,KAAKC,KAAa;IACzB,OAAOA,MAAMC,QAAQ,GAAGC,QAAQ,CAAC,GAAG;AACtC"}
@@ -0,0 +1,2 @@
1
+ export { WorkflowRunList } from './workflow-run-list.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-list/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { WorkflowRunList } from './workflow-run-list.js';
2
+
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/workflow-run-list/index.ts"],"sourcesContent":["export {WorkflowRunList} from './workflow-run-list.js';\n"],"names":["WorkflowRunList"],"mappings":"AAAA,SAAQA,eAAe,QAAO,yBAAyB"}
@@ -0,0 +1,5 @@
1
+ import type { WorkflowRunListItem, WorkflowRunStatus } from '#core/workflow-run.js';
2
+ import type { WorkflowRunListStatusFilter } from './types.js';
3
+ export declare function runMatchesSearch(run: WorkflowRunListItem, query: string): boolean;
4
+ export declare function runMatchesStatusFilter(status: WorkflowRunStatus, filter: WorkflowRunListStatusFilter): boolean;
5
+ //# sourceMappingURL=run-display.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-display.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-list/run-display.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,mBAAmB,EAAE,iBAAiB,EAAC,MAAM,uBAAuB,CAAC;AAClF,OAAO,KAAK,EAAC,2BAA2B,EAAC,MAAM,YAAY,CAAC;AAE5D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAKjF;AAID,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,iBAAiB,EACzB,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAMT"}
@@ -0,0 +1,19 @@
1
+ export function runMatchesSearch(run, query) {
2
+ const needle = query.trim().toLowerCase();
3
+ if (needle === '') return true;
4
+ const haystack = `${run.id} ${run.name} ${run.status} ${run.triggerLabel}`.toLowerCase();
5
+ return haystack.includes(needle);
6
+ }
7
+ const IN_PROGRESS_STATUSES = new Set([
8
+ 'pending',
9
+ 'running'
10
+ ]);
11
+ export function runMatchesStatusFilter(status, filter) {
12
+ if (filter === 'all') return true;
13
+ // "Running" reads as in-progress: it covers freshly-queued `pending` runs (including the
14
+ // optimistic manual run inserted on fire) so they are not hidden the moment the filter is on.
15
+ if (filter === 'running') return IN_PROGRESS_STATUSES.has(status);
16
+ return status === filter;
17
+ }
18
+
19
+ //# sourceMappingURL=run-display.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/workflow-run-list/run-display.ts"],"sourcesContent":["import type {WorkflowRunListItem, WorkflowRunStatus} from '#core/workflow-run.js';\nimport type {WorkflowRunListStatusFilter} from './types.js';\n\nexport function runMatchesSearch(run: WorkflowRunListItem, query: string): boolean {\n const needle = query.trim().toLowerCase();\n if (needle === '') return true;\n const haystack = `${run.id} ${run.name} ${run.status} ${run.triggerLabel}`.toLowerCase();\n return haystack.includes(needle);\n}\n\nconst IN_PROGRESS_STATUSES: ReadonlySet<WorkflowRunStatus> = new Set(['pending', 'running']);\n\nexport function runMatchesStatusFilter(\n status: WorkflowRunStatus,\n filter: WorkflowRunListStatusFilter,\n): boolean {\n if (filter === 'all') return true;\n // \"Running\" reads as in-progress: it covers freshly-queued `pending` runs (including the\n // optimistic manual run inserted on fire) so they are not hidden the moment the filter is on.\n if (filter === 'running') return IN_PROGRESS_STATUSES.has(status);\n return status === filter;\n}\n"],"names":["runMatchesSearch","run","query","needle","trim","toLowerCase","haystack","id","name","status","triggerLabel","includes","IN_PROGRESS_STATUSES","Set","runMatchesStatusFilter","filter","has"],"mappings":"AAGA,OAAO,SAASA,iBAAiBC,GAAwB,EAAEC,KAAa;IACtE,MAAMC,SAASD,MAAME,IAAI,GAAGC,WAAW;IACvC,IAAIF,WAAW,IAAI,OAAO;IAC1B,MAAMG,WAAW,GAAGL,IAAIM,EAAE,CAAC,CAAC,EAAEN,IAAIO,IAAI,CAAC,CAAC,EAAEP,IAAIQ,MAAM,CAAC,CAAC,EAAER,IAAIS,YAAY,EAAE,CAACL,WAAW;IACtF,OAAOC,SAASK,QAAQ,CAACR;AAC3B;AAEA,MAAMS,uBAAuD,IAAIC,IAAI;IAAC;IAAW;CAAU;AAE3F,OAAO,SAASC,uBACdL,MAAyB,EACzBM,MAAmC;IAEnC,IAAIA,WAAW,OAAO,OAAO;IAC7B,yFAAyF;IACzF,8FAA8F;IAC9F,IAAIA,WAAW,WAAW,OAAOH,qBAAqBI,GAAG,CAACP;IAC1D,OAAOA,WAAWM;AACpB"}