@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,171 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuTrigger } from '@shipfox/react-ui/dropdown-menu';
3
+ import { Icon } from '@shipfox/react-ui/icon';
4
+ import { Text } from '@shipfox/react-ui/typography';
5
+ import { cn } from '@shipfox/react-ui/utils';
6
+ import { useMemo } from 'react';
7
+ import { WorkflowStatusIcon } from '#components/workflow-status/workflow-status-icon.js';
8
+ import { JobExecutionTimeText } from './job-execution-time-text.js';
9
+ export function JobExecutionSwitcher({ job, selectedJobExecution, onSelectedJobExecutionChange, variant = 'compact', className }) {
10
+ const executions = useMemo(()=>[
11
+ ...job.jobExecutions
12
+ ].sort((left, right)=>right.sequence - left.sequence), [
13
+ job.jobExecutions
14
+ ]);
15
+ const selected = executions.find((jobExecution)=>jobExecution.id === selectedJobExecution) ?? executions[0] ?? null;
16
+ if (selected === null) return null;
17
+ if (!job.executionCountVisible) {
18
+ return /*#__PURE__*/ _jsx("div", {
19
+ className: cn('flex min-w-0 items-center gap-6 text-sm leading-20 text-foreground-neutral-subtle', className),
20
+ children: /*#__PURE__*/ _jsx(ExecutionSummary, {
21
+ execution: selected
22
+ })
23
+ });
24
+ }
25
+ return /*#__PURE__*/ _jsxs(DropdownMenu, {
26
+ children: [
27
+ /*#__PURE__*/ _jsxs(DropdownMenuTrigger, {
28
+ 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}`,
30
+ children: [
31
+ variant === 'title' ? /*#__PURE__*/ _jsx(TitleExecutionSummary, {
32
+ job: job,
33
+ execution: selected
34
+ }) : /*#__PURE__*/ _jsx(ExecutionSummary, {
35
+ execution: selected
36
+ }),
37
+ /*#__PURE__*/ _jsx(Icon, {
38
+ name: "arrowDownSLine",
39
+ className: "size-14 shrink-0 text-foreground-neutral-muted"
40
+ })
41
+ ]
42
+ }),
43
+ /*#__PURE__*/ _jsxs(DropdownMenuContent, {
44
+ align: "start",
45
+ size: "lg",
46
+ className: "max-h-[320px] overflow-y-auto",
47
+ children: [
48
+ /*#__PURE__*/ _jsxs(DropdownMenuLabel, {
49
+ children: [
50
+ executions.length,
51
+ " execution",
52
+ executions.length === 1 ? '' : 's'
53
+ ]
54
+ }),
55
+ executions.map((jobExecution)=>{
56
+ const isSelected = jobExecution.id === selected.id;
57
+ return /*#__PURE__*/ _jsx(DropdownMenuItem, {
58
+ asChild: true,
59
+ onSelect: ()=>onSelectedJobExecutionChange(jobExecution.id),
60
+ children: /*#__PURE__*/ _jsxs("button", {
61
+ type: "button",
62
+ "aria-current": isSelected ? 'true' : undefined,
63
+ className: "w-full text-left",
64
+ children: [
65
+ /*#__PURE__*/ _jsx(WorkflowStatusIcon, {
66
+ status: jobExecution.status,
67
+ size: 14,
68
+ tooltip: false
69
+ }),
70
+ /*#__PURE__*/ _jsxs("span", {
71
+ className: "font-code text-xs leading-20 text-foreground-neutral-base tabular-nums",
72
+ children: [
73
+ "#",
74
+ jobExecution.sequence
75
+ ]
76
+ }),
77
+ /*#__PURE__*/ _jsx("span", {
78
+ className: "min-w-0 truncate text-xs leading-20 text-foreground-neutral-base",
79
+ children: job.displayName
80
+ }),
81
+ !isSelected && jobExecution.statusReason ? /*#__PURE__*/ _jsx("span", {
82
+ className: "min-w-0 flex-1 truncate text-xs leading-20 text-foreground-neutral-muted",
83
+ children: jobExecution.statusReason
84
+ }) : /*#__PURE__*/ _jsx("span", {
85
+ className: "min-w-0 flex-1"
86
+ }),
87
+ isSelected ? /*#__PURE__*/ _jsx(Icon, {
88
+ name: "check",
89
+ className: "size-14 shrink-0 text-foreground-neutral-base"
90
+ }) : null,
91
+ /*#__PURE__*/ _jsx(JobExecutionDuration, {
92
+ execution: jobExecution,
93
+ className: "ml-auto shrink-0 font-code text-xs leading-20 text-foreground-neutral-muted tabular-nums"
94
+ })
95
+ ]
96
+ })
97
+ }, jobExecution.id);
98
+ })
99
+ ]
100
+ })
101
+ ]
102
+ });
103
+ }
104
+ function TitleExecutionSummary({ job, execution }) {
105
+ return /*#__PURE__*/ _jsxs("span", {
106
+ className: "flex min-w-0 items-center gap-8",
107
+ children: [
108
+ /*#__PURE__*/ _jsxs(Text, {
109
+ as: "span",
110
+ size: "sm",
111
+ bold: true,
112
+ className: "shrink-0 text-foreground-neutral-base",
113
+ children: [
114
+ "#",
115
+ execution.sequence
116
+ ]
117
+ }),
118
+ /*#__PURE__*/ _jsx(Text, {
119
+ as: "span",
120
+ size: "sm",
121
+ bold: true,
122
+ className: "min-w-0 truncate text-foreground-neutral-base",
123
+ children: job.displayName
124
+ })
125
+ ]
126
+ });
127
+ }
128
+ function ExecutionSummary({ execution }) {
129
+ return /*#__PURE__*/ _jsxs("span", {
130
+ className: "flex min-w-0 items-center gap-6",
131
+ children: [
132
+ /*#__PURE__*/ _jsx(WorkflowStatusIcon, {
133
+ status: execution.status,
134
+ size: 14,
135
+ tooltip: false
136
+ }),
137
+ /*#__PURE__*/ _jsxs("span", {
138
+ className: "shrink-0 font-code text-xs leading-20 text-foreground-neutral-base tabular-nums",
139
+ children: [
140
+ "Execution #",
141
+ execution.sequence
142
+ ]
143
+ }),
144
+ execution.statusReason ? /*#__PURE__*/ _jsxs(Text, {
145
+ as: "span",
146
+ size: "xs",
147
+ className: "min-w-0 truncate text-foreground-neutral-muted",
148
+ children: [
149
+ "· ",
150
+ execution.statusReason
151
+ ]
152
+ }) : null,
153
+ /*#__PURE__*/ _jsx(JobExecutionDuration, {
154
+ execution: execution,
155
+ className: "shrink-0 font-code text-xs leading-20 text-foreground-neutral-muted tabular-nums"
156
+ })
157
+ ]
158
+ });
159
+ }
160
+ function JobExecutionDuration({ execution, className }) {
161
+ const duration = execution.displayDuration;
162
+ if (!duration) return null;
163
+ return /*#__PURE__*/ _jsx("span", {
164
+ className: className,
165
+ children: /*#__PURE__*/ _jsx(JobExecutionTimeText, {
166
+ time: duration
167
+ })
168
+ });
169
+ }
170
+
171
+ //# sourceMappingURL=job-execution-switcher.js.map
@@ -0,0 +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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"}
@@ -0,0 +1,6 @@
1
+ import type { JobExecutionTime } from '#core/workflow-run.js';
2
+ export declare function JobExecutionTimeText({ time }: {
3
+ time: JobExecutionTime;
4
+ }): string;
5
+ export declare function formatJobExecutionTime(time: JobExecutionTime): string;
6
+ //# sourceMappingURL=job-execution-time-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job-execution-time-text.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-view/job-execution-time-text.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,uBAAuB,CAAC;AAE5D,wBAAgB,oBAAoB,CAAC,EAAC,IAAI,EAAC,EAAE;IAAC,IAAI,EAAE,gBAAgB,CAAA;CAAC,UAIpE;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAMrE"}
@@ -0,0 +1,24 @@
1
+ import { useTimeTick } from '@shipfox/react-ui/time-ticker';
2
+ import { formatDuration } from '@shipfox/react-ui/utils';
3
+ export function JobExecutionTimeText({ time }) {
4
+ useTimeTick();
5
+ return formatJobExecutionTime(time);
6
+ }
7
+ export function formatJobExecutionTime(time) {
8
+ if (time.state === 'live') {
9
+ return formatDuration(Date.now() - Date.parse(time.fromIso));
10
+ }
11
+ return formatElapsedDuration(time.elapsed);
12
+ }
13
+ function formatElapsedDuration(duration) {
14
+ const days = duration.days ?? 0;
15
+ const hours = duration.hours ?? 0;
16
+ const minutes = duration.minutes ?? 0;
17
+ const seconds = duration.seconds ?? 0;
18
+ if (days > 0) return hours > 0 ? `${days}d ${hours}h` : `${days}d`;
19
+ if (hours > 0) return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
20
+ if (minutes > 0) return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
21
+ return `${seconds}s`;
22
+ }
23
+
24
+ //# sourceMappingURL=job-execution-time-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/workflow-run-view/job-execution-time-text.tsx"],"sourcesContent":["import {useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {formatDuration} from '@shipfox/react-ui/utils';\nimport type {JobExecutionTime} from '#core/workflow-run.js';\n\nexport function JobExecutionTimeText({time}: {time: JobExecutionTime}) {\n useTimeTick();\n\n return formatJobExecutionTime(time);\n}\n\nexport function formatJobExecutionTime(time: JobExecutionTime): string {\n if (time.state === 'live') {\n return formatDuration(Date.now() - Date.parse(time.fromIso));\n }\n\n return formatElapsedDuration(time.elapsed);\n}\n\nfunction formatElapsedDuration(duration: {\n days?: number | undefined;\n hours?: number | undefined;\n minutes?: number | undefined;\n seconds?: number | undefined;\n}): string {\n const days = duration.days ?? 0;\n const hours = duration.hours ?? 0;\n const minutes = duration.minutes ?? 0;\n const seconds = duration.seconds ?? 0;\n\n if (days > 0) return hours > 0 ? `${days}d ${hours}h` : `${days}d`;\n if (hours > 0) return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;\n if (minutes > 0) return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;\n return `${seconds}s`;\n}\n"],"names":["useTimeTick","formatDuration","JobExecutionTimeText","time","formatJobExecutionTime","state","Date","now","parse","fromIso","formatElapsedDuration","elapsed","duration","days","hours","minutes","seconds"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,cAAc,QAAO,0BAA0B;AAGvD,OAAO,SAASC,qBAAqB,EAACC,IAAI,EAA2B;IACnEH;IAEA,OAAOI,uBAAuBD;AAChC;AAEA,OAAO,SAASC,uBAAuBD,IAAsB;IAC3D,IAAIA,KAAKE,KAAK,KAAK,QAAQ;QACzB,OAAOJ,eAAeK,KAAKC,GAAG,KAAKD,KAAKE,KAAK,CAACL,KAAKM,OAAO;IAC5D;IAEA,OAAOC,sBAAsBP,KAAKQ,OAAO;AAC3C;AAEA,SAASD,sBAAsBE,QAK9B;IACC,MAAMC,OAAOD,SAASC,IAAI,IAAI;IAC9B,MAAMC,QAAQF,SAASE,KAAK,IAAI;IAChC,MAAMC,UAAUH,SAASG,OAAO,IAAI;IACpC,MAAMC,UAAUJ,SAASI,OAAO,IAAI;IAEpC,IAAIH,OAAO,GAAG,OAAOC,QAAQ,IAAI,GAAGD,KAAK,EAAE,EAAEC,MAAM,CAAC,CAAC,GAAG,GAAGD,KAAK,CAAC,CAAC;IAClE,IAAIC,QAAQ,GAAG,OAAOC,UAAU,IAAI,GAAGD,MAAM,EAAE,EAAEC,QAAQ,CAAC,CAAC,GAAG,GAAGD,MAAM,CAAC,CAAC;IACzE,IAAIC,UAAU,GAAG,OAAOC,UAAU,IAAI,GAAGD,QAAQ,EAAE,EAAEC,QAAQ,CAAC,CAAC,GAAG,GAAGD,QAAQ,CAAC,CAAC;IAC/E,OAAO,GAAGC,QAAQ,CAAC,CAAC;AACtB"}
@@ -0,0 +1,9 @@
1
+ export interface StepAttemptLogPanelProps {
2
+ stepId: string;
3
+ attempt: number;
4
+ attemptStatus: string;
5
+ initialErrorRetryCount?: number | undefined;
6
+ initialErrorRetryDelayMs?: number | undefined;
7
+ }
8
+ export declare function StepAttemptLogPanel({ stepId, attempt, attemptStatus, initialErrorRetryCount, initialErrorRetryDelayMs, }: StepAttemptLogPanelProps): import("react").JSX.Element;
9
+ //# sourceMappingURL=step-attempt-log-panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-attempt-log-panel.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-view/step-attempt-log-panel.tsx"],"names":[],"mappings":"AAgBA,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,wBAAwB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/C;AAED,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,OAAO,EACP,aAAa,EACb,sBAAsD,EACtD,wBAA2D,GAC5D,EAAE,wBAAwB,+BAiF1B"}
@@ -0,0 +1,152 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { isMissingStepLogStreamError, LogView, LogViewSkeleton, useStepAttemptLogsQuery } from '@shipfox/client-logs';
3
+ import { Button } from '@shipfox/react-ui/button';
4
+ import { Callout } from '@shipfox/react-ui/callout';
5
+ import { Text } from '@shipfox/react-ui/typography';
6
+ import { useEffect, useRef } from 'react';
7
+ const TAIL_FOLLOW_THRESHOLD_PX = 24;
8
+ const INITIAL_LOG_ERROR_RETRY_COUNT = 5;
9
+ const INITIAL_LOG_ERROR_RETRY_DELAY_MS = 1_500;
10
+ const logSurfaceClasses = 'max-h-[40vh] rounded-8 md:max-h-[280px]';
11
+ export function StepAttemptLogPanel({ stepId, attempt, attemptStatus, initialErrorRetryCount = INITIAL_LOG_ERROR_RETRY_COUNT, initialErrorRetryDelayMs = INITIAL_LOG_ERROR_RETRY_DELAY_MS }) {
12
+ const panelRef = useRef(null);
13
+ const shouldFollowTailRef = useRef(true);
14
+ const missingStreamRetryCount = attemptStatus === 'running' ? undefined : initialErrorRetryCount;
15
+ const retryMissingStream = attemptStatus === 'running' || isTerminalAttemptStatus(attemptStatus);
16
+ const query = useStepAttemptLogsQuery(stepId, attempt, {
17
+ retryMissingStream,
18
+ missingStreamRetryCount,
19
+ missingStreamRetryDelayMs: initialErrorRetryDelayMs,
20
+ initialErrorRetryCount,
21
+ initialErrorRetryDelayMs
22
+ });
23
+ const records = query.data?.records ?? [];
24
+ const recordCount = records.length;
25
+ const anchorToFailure = attemptStatus === 'failed';
26
+ const missingActiveStream = retryMissingStream && query.data === undefined && isMissingStepLogStreamError(query.error);
27
+ const initialError = query.isError && query.data === undefined && !missingActiveStream;
28
+ const staleError = query.isError && query.data !== undefined;
29
+ useEffect(()=>{
30
+ if (recordCount === 0) return undefined;
31
+ if (anchorToFailure) return undefined;
32
+ if (!shouldFollowTailRef.current) return undefined;
33
+ const frame = scheduleAnimationFrame(()=>{
34
+ const scrollElement = panelRef.current?.querySelector('[data-slot="log-rows"]');
35
+ if (!scrollElement) return;
36
+ scrollElement.scrollTop = scrollElement.scrollHeight;
37
+ });
38
+ return ()=>{
39
+ cancelScheduledFrame(frame);
40
+ };
41
+ }, [
42
+ anchorToFailure,
43
+ recordCount
44
+ ]);
45
+ function handleLogScroll(event) {
46
+ const element = event.currentTarget;
47
+ const distanceFromBottom = element.scrollHeight - element.scrollTop - element.clientHeight;
48
+ shouldFollowTailRef.current = distanceFromBottom <= TAIL_FOLLOW_THRESHOLD_PX;
49
+ }
50
+ if (query.isPending) {
51
+ return /*#__PURE__*/ _jsx(StepLogsLoadingSurface, {
52
+ label: "Loading logs"
53
+ });
54
+ }
55
+ if (missingActiveStream) {
56
+ return /*#__PURE__*/ _jsx(StepLogsLoadingSurface, {
57
+ label: "Waiting for logs"
58
+ });
59
+ }
60
+ if (initialError) {
61
+ return /*#__PURE__*/ _jsx(StepLogsError, {
62
+ retrying: query.isFetching,
63
+ onRetry: ()=>void query.refetch()
64
+ });
65
+ }
66
+ return /*#__PURE__*/ _jsxs("div", {
67
+ ref: panelRef,
68
+ className: "flex min-w-0 flex-col gap-8",
69
+ children: [
70
+ staleError ? /*#__PURE__*/ _jsx(Callout, {
71
+ role: "alert",
72
+ type: "warning",
73
+ className: "px-10 py-8",
74
+ children: /*#__PURE__*/ _jsxs("div", {
75
+ className: "flex min-w-0 flex-1 items-center justify-between gap-8",
76
+ children: [
77
+ /*#__PURE__*/ _jsx(Text, {
78
+ size: "xs",
79
+ children: "Could not refresh logs."
80
+ }),
81
+ /*#__PURE__*/ _jsx(Button, {
82
+ type: "button",
83
+ size: "2xs",
84
+ variant: "secondary",
85
+ isLoading: query.isFetching,
86
+ onClick: ()=>void query.refetch(),
87
+ children: "Retry"
88
+ })
89
+ ]
90
+ })
91
+ }) : null,
92
+ /*#__PURE__*/ _jsx(LogView, {
93
+ records: records,
94
+ emptyState: query.data?.complete ? 'complete' : 'pending',
95
+ anchorToFailure: anchorToFailure,
96
+ className: logSurfaceClasses,
97
+ onScroll: handleLogScroll
98
+ })
99
+ ]
100
+ });
101
+ }
102
+ function StepLogsLoadingSurface({ label }) {
103
+ return /*#__PURE__*/ _jsx("div", {
104
+ role: "status",
105
+ "aria-label": label,
106
+ children: /*#__PURE__*/ _jsx(LogViewSkeleton, {
107
+ className: logSurfaceClasses
108
+ })
109
+ });
110
+ }
111
+ function scheduleAnimationFrame(callback) {
112
+ if (typeof globalThis.requestAnimationFrame === 'function') {
113
+ return globalThis.requestAnimationFrame(callback);
114
+ }
115
+ return window.setTimeout(()=>callback(Date.now()), 0);
116
+ }
117
+ function cancelScheduledFrame(frame) {
118
+ if (typeof globalThis.cancelAnimationFrame === 'function') {
119
+ globalThis.cancelAnimationFrame(frame);
120
+ return;
121
+ }
122
+ window.clearTimeout(frame);
123
+ }
124
+ function isTerminalAttemptStatus(status) {
125
+ return status === 'succeeded' || status === 'failed' || status === 'cancelled';
126
+ }
127
+ function StepLogsError({ retrying, onRetry }) {
128
+ return /*#__PURE__*/ _jsx(Callout, {
129
+ role: "alert",
130
+ type: "error",
131
+ className: "px-10 py-8",
132
+ children: /*#__PURE__*/ _jsxs("div", {
133
+ className: "flex min-w-0 flex-1 items-center justify-between gap-8",
134
+ children: [
135
+ /*#__PURE__*/ _jsx(Text, {
136
+ size: "xs",
137
+ children: "Could not load logs."
138
+ }),
139
+ /*#__PURE__*/ _jsx(Button, {
140
+ type: "button",
141
+ size: "2xs",
142
+ variant: "secondary",
143
+ isLoading: retrying,
144
+ onClick: onRetry,
145
+ children: "Retry"
146
+ })
147
+ ]
148
+ })
149
+ });
150
+ }
151
+
152
+ //# sourceMappingURL=step-attempt-log-panel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/workflow-run-view/step-attempt-log-panel.tsx"],"sourcesContent":["import {\n isMissingStepLogStreamError,\n LogView,\n LogViewSkeleton,\n useStepAttemptLogsQuery,\n} from '@shipfox/client-logs';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {type UIEvent, useEffect, useRef} from 'react';\n\nconst TAIL_FOLLOW_THRESHOLD_PX = 24;\nconst INITIAL_LOG_ERROR_RETRY_COUNT = 5;\nconst INITIAL_LOG_ERROR_RETRY_DELAY_MS = 1_500;\nconst logSurfaceClasses = 'max-h-[40vh] rounded-8 md:max-h-[280px]';\n\nexport interface StepAttemptLogPanelProps {\n stepId: string;\n attempt: number;\n attemptStatus: string;\n initialErrorRetryCount?: number | undefined;\n initialErrorRetryDelayMs?: number | undefined;\n}\n\nexport function StepAttemptLogPanel({\n stepId,\n attempt,\n attemptStatus,\n initialErrorRetryCount = INITIAL_LOG_ERROR_RETRY_COUNT,\n initialErrorRetryDelayMs = INITIAL_LOG_ERROR_RETRY_DELAY_MS,\n}: StepAttemptLogPanelProps) {\n const panelRef = useRef<HTMLDivElement>(null);\n const shouldFollowTailRef = useRef(true);\n const missingStreamRetryCount = attemptStatus === 'running' ? undefined : initialErrorRetryCount;\n const retryMissingStream = attemptStatus === 'running' || isTerminalAttemptStatus(attemptStatus);\n const query = useStepAttemptLogsQuery(stepId, attempt, {\n retryMissingStream,\n missingStreamRetryCount,\n missingStreamRetryDelayMs: initialErrorRetryDelayMs,\n initialErrorRetryCount,\n initialErrorRetryDelayMs,\n });\n const records = query.data?.records ?? [];\n const recordCount = records.length;\n const anchorToFailure = attemptStatus === 'failed';\n const missingActiveStream =\n retryMissingStream && query.data === undefined && isMissingStepLogStreamError(query.error);\n const initialError = query.isError && query.data === undefined && !missingActiveStream;\n const staleError = query.isError && query.data !== undefined;\n\n useEffect(() => {\n if (recordCount === 0) return undefined;\n if (anchorToFailure) return undefined;\n if (!shouldFollowTailRef.current) return undefined;\n\n const frame = scheduleAnimationFrame(() => {\n const scrollElement = panelRef.current?.querySelector<HTMLElement>('[data-slot=\"log-rows\"]');\n if (!scrollElement) return;\n scrollElement.scrollTop = scrollElement.scrollHeight;\n });\n\n return () => {\n cancelScheduledFrame(frame);\n };\n }, [anchorToFailure, recordCount]);\n\n function handleLogScroll(event: UIEvent<HTMLDivElement>) {\n const element = event.currentTarget;\n const distanceFromBottom = element.scrollHeight - element.scrollTop - element.clientHeight;\n shouldFollowTailRef.current = distanceFromBottom <= TAIL_FOLLOW_THRESHOLD_PX;\n }\n\n if (query.isPending) {\n return <StepLogsLoadingSurface label=\"Loading logs\" />;\n }\n\n if (missingActiveStream) {\n return <StepLogsLoadingSurface label=\"Waiting for logs\" />;\n }\n\n if (initialError) {\n return <StepLogsError retrying={query.isFetching} onRetry={() => void query.refetch()} />;\n }\n\n return (\n <div ref={panelRef} className=\"flex min-w-0 flex-col gap-8\">\n {staleError ? (\n <Callout role=\"alert\" type=\"warning\" className=\"px-10 py-8\">\n <div className=\"flex min-w-0 flex-1 items-center justify-between gap-8\">\n <Text size=\"xs\">Could not refresh logs.</Text>\n <Button\n type=\"button\"\n size=\"2xs\"\n variant=\"secondary\"\n isLoading={query.isFetching}\n onClick={() => void query.refetch()}\n >\n Retry\n </Button>\n </div>\n </Callout>\n ) : null}\n <LogView\n records={records}\n emptyState={query.data?.complete ? 'complete' : 'pending'}\n anchorToFailure={anchorToFailure}\n className={logSurfaceClasses}\n onScroll={handleLogScroll}\n />\n </div>\n );\n}\n\nfunction StepLogsLoadingSurface({label}: {label: string}) {\n return (\n <div role=\"status\" aria-label={label}>\n <LogViewSkeleton className={logSurfaceClasses} />\n </div>\n );\n}\n\nfunction scheduleAnimationFrame(callback: FrameRequestCallback): number {\n if (typeof globalThis.requestAnimationFrame === 'function') {\n return globalThis.requestAnimationFrame(callback);\n }\n return window.setTimeout(() => callback(Date.now()), 0);\n}\n\nfunction cancelScheduledFrame(frame: number) {\n if (typeof globalThis.cancelAnimationFrame === 'function') {\n globalThis.cancelAnimationFrame(frame);\n return;\n }\n window.clearTimeout(frame);\n}\n\nfunction isTerminalAttemptStatus(status: string): boolean {\n return status === 'succeeded' || status === 'failed' || status === 'cancelled';\n}\n\nfunction StepLogsError({retrying, onRetry}: {retrying: boolean; onRetry: () => void}) {\n return (\n <Callout role=\"alert\" type=\"error\" className=\"px-10 py-8\">\n <div className=\"flex min-w-0 flex-1 items-center justify-between gap-8\">\n <Text size=\"xs\">Could not load logs.</Text>\n <Button type=\"button\" size=\"2xs\" variant=\"secondary\" isLoading={retrying} onClick={onRetry}>\n Retry\n </Button>\n </div>\n </Callout>\n );\n}\n"],"names":["isMissingStepLogStreamError","LogView","LogViewSkeleton","useStepAttemptLogsQuery","Button","Callout","Text","useEffect","useRef","TAIL_FOLLOW_THRESHOLD_PX","INITIAL_LOG_ERROR_RETRY_COUNT","INITIAL_LOG_ERROR_RETRY_DELAY_MS","logSurfaceClasses","StepAttemptLogPanel","stepId","attempt","attemptStatus","initialErrorRetryCount","initialErrorRetryDelayMs","panelRef","shouldFollowTailRef","missingStreamRetryCount","undefined","retryMissingStream","isTerminalAttemptStatus","query","missingStreamRetryDelayMs","records","data","recordCount","length","anchorToFailure","missingActiveStream","error","initialError","isError","staleError","current","frame","scheduleAnimationFrame","scrollElement","querySelector","scrollTop","scrollHeight","cancelScheduledFrame","handleLogScroll","event","element","currentTarget","distanceFromBottom","clientHeight","isPending","StepLogsLoadingSurface","label","StepLogsError","retrying","isFetching","onRetry","refetch","div","ref","className","role","type","size","variant","isLoading","onClick","emptyState","complete","onScroll","aria-label","callback","globalThis","requestAnimationFrame","window","setTimeout","Date","now","cancelAnimationFrame","clearTimeout","status"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { type Job, type JobExecution, type Step, type StepAttempt, type WorkflowRunDetail } from '#core/workflow-run.js';
2
+ import type { WorkflowRunSelectionInput } from '#core/workflow-run-url-state.js';
3
+ export interface ResolvedWorkflowRunSelection {
4
+ job: Job | undefined;
5
+ jobExecution: JobExecution | undefined;
6
+ step: Step | undefined;
7
+ attempt: StepAttempt | undefined;
8
+ selectedAttemptId: string | null;
9
+ }
10
+ export declare function resolveWorkflowRunSelection({ run, selection, }: {
11
+ run: WorkflowRunDetail;
12
+ selection: WorkflowRunSelectionInput;
13
+ }): ResolvedWorkflowRunSelection;
14
+ //# sourceMappingURL=workflow-run-selection.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,56 @@
1
+ import { resolveJobExecution } from '#core/workflow-run.js';
2
+ export function resolveWorkflowRunSelection({ run, selection }) {
3
+ const jobById = new Map(run.jobs.map((job)=>[
4
+ job.id,
5
+ job
6
+ ]));
7
+ const stepMatch = findStep(run, selection.stepId);
8
+ if (stepMatch) {
9
+ const attempt = resolveStepAttempt(stepMatch.step, selection.stepAttemptId);
10
+ return {
11
+ job: stepMatch.job,
12
+ jobExecution: stepMatch.jobExecution,
13
+ step: stepMatch.step,
14
+ attempt,
15
+ selectedAttemptId: attempt?.id ?? null
16
+ };
17
+ }
18
+ const job = (selection.jobId ? jobById.get(selection.jobId) : undefined) ?? run.jobs.at(0);
19
+ const jobExecution = job ? resolveJobExecution(job, selection.jobExecutionId) : undefined;
20
+ return {
21
+ job,
22
+ jobExecution,
23
+ step: undefined,
24
+ attempt: undefined,
25
+ selectedAttemptId: null
26
+ };
27
+ }
28
+ function findStep(run, stepId) {
29
+ if (!stepId) return undefined;
30
+ for (const job of run.jobs){
31
+ for (const jobExecution of job.jobExecutions){
32
+ const step = jobExecution.steps.find((candidate)=>candidate.id === stepId);
33
+ if (step) return {
34
+ job,
35
+ jobExecution,
36
+ step
37
+ };
38
+ }
39
+ }
40
+ return undefined;
41
+ }
42
+ function resolveStepAttempt(step, attemptId) {
43
+ const attemptById = attemptId ? step.attempts.find((attempt)=>attempt.id === attemptId) : undefined;
44
+ if (attemptById) return attemptById;
45
+ const currentAttempt = step.attempts.find((attempt)=>attempt.attempt === step.currentAttempt);
46
+ if (currentAttempt) return currentAttempt;
47
+ return step.attempts.reduce((latest, attempt)=>{
48
+ if (!latest) return attempt;
49
+ return compareAttempts(attempt, latest) > 0 ? attempt : latest;
50
+ }, undefined);
51
+ }
52
+ function compareAttempts(left, right) {
53
+ return left.attempt - right.attempt || left.executionOrder - right.executionOrder || left.id.localeCompare(right.id);
54
+ }
55
+
56
+ //# sourceMappingURL=workflow-run-selection.js.map
@@ -0,0 +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"}
@@ -0,0 +1,12 @@
1
+ import type { QueryLoadErrorQuery } from '@shipfox/client-ui';
2
+ export declare function WorkflowRunSkeleton(): import("react").JSX.Element;
3
+ export declare function WorkflowRunNotFound(): import("react").JSX.Element;
4
+ /**
5
+ * Slim non-blocking banner shown when a background refetch fails after the run already
6
+ * loaded (active-run polling can hit a transient API error), so the loaded run stays on
7
+ * screen with an inline retry instead of being wiped by a full error placeholder.
8
+ */
9
+ export declare function WorkflowRunStaleError({ query }: {
10
+ query: QueryLoadErrorQuery;
11
+ }): import("react").JSX.Element;
12
+ //# sourceMappingURL=workflow-run-states.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,91 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button } from '@shipfox/react-ui/button';
3
+ import { Callout } from '@shipfox/react-ui/callout';
4
+ import { EmptyState } from '@shipfox/react-ui/empty-state';
5
+ import { Skeleton } from '@shipfox/react-ui/skeleton';
6
+ import { Text } from '@shipfox/react-ui/typography';
7
+ export function WorkflowRunSkeleton() {
8
+ return /*#__PURE__*/ _jsx("section", {
9
+ "aria-label": "Loading workflow run",
10
+ className: "border-b border-border-neutral-base bg-background-subtle-base px-16 py-12",
11
+ children: /*#__PURE__*/ _jsxs("div", {
12
+ className: "flex min-w-0 flex-wrap items-center gap-x-12 gap-y-8",
13
+ children: [
14
+ /*#__PURE__*/ _jsxs("div", {
15
+ className: "flex min-w-0 items-center gap-8",
16
+ children: [
17
+ /*#__PURE__*/ _jsx(Skeleton, {
18
+ className: "size-8 rounded-full"
19
+ }),
20
+ /*#__PURE__*/ _jsx(Skeleton, {
21
+ className: "h-24 w-180 rounded-6"
22
+ })
23
+ ]
24
+ }),
25
+ /*#__PURE__*/ _jsx(Skeleton, {
26
+ className: "h-24 w-88 rounded-6"
27
+ }),
28
+ /*#__PURE__*/ _jsx("span", {
29
+ "aria-hidden": "true",
30
+ className: "hidden h-20 w-px shrink-0 bg-border-neutral-base sm:block"
31
+ }),
32
+ /*#__PURE__*/ _jsx(Skeleton, {
33
+ className: "h-20 w-64 rounded-4"
34
+ }),
35
+ /*#__PURE__*/ _jsx(Skeleton, {
36
+ className: "h-20 w-112 rounded-4"
37
+ }),
38
+ /*#__PURE__*/ _jsx("span", {
39
+ className: "min-w-0 flex-1"
40
+ }),
41
+ /*#__PURE__*/ _jsx(Skeleton, {
42
+ className: "h-20 w-88 rounded-4"
43
+ }),
44
+ /*#__PURE__*/ _jsx(Skeleton, {
45
+ className: "h-20 w-88 rounded-4"
46
+ })
47
+ ]
48
+ })
49
+ });
50
+ }
51
+ export function WorkflowRunNotFound() {
52
+ return /*#__PURE__*/ _jsx(EmptyState, {
53
+ icon: "pulseLine",
54
+ title: "Run not found",
55
+ description: "This run does not exist or is no longer available."
56
+ });
57
+ }
58
+ /**
59
+ * Slim non-blocking banner shown when a background refetch fails after the run already
60
+ * loaded (active-run polling can hit a transient API error), so the loaded run stays on
61
+ * screen with an inline retry instead of being wiped by a full error placeholder.
62
+ */ export function WorkflowRunStaleError({ query }) {
63
+ return /*#__PURE__*/ _jsx("div", {
64
+ className: "border-b border-border-neutral-base p-8",
65
+ children: /*#__PURE__*/ _jsx(Callout, {
66
+ role: "alert",
67
+ type: "error",
68
+ children: /*#__PURE__*/ _jsxs("div", {
69
+ className: "flex items-center justify-between gap-8",
70
+ children: [
71
+ /*#__PURE__*/ _jsx(Text, {
72
+ size: "xs",
73
+ children: "Could not refresh this run."
74
+ }),
75
+ /*#__PURE__*/ _jsx(Button, {
76
+ type: "button",
77
+ size: "2xs",
78
+ variant: "secondary",
79
+ isLoading: query.isFetching,
80
+ onClick: ()=>{
81
+ void query.refetch();
82
+ },
83
+ children: "Retry"
84
+ })
85
+ ]
86
+ })
87
+ })
88
+ });
89
+ }
90
+
91
+ //# sourceMappingURL=workflow-run-states.js.map