@shipfox/client-workflows 14.0.1 → 16.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/.storybook/preview.css +4 -0
  2. package/.storybook/preview.tsx +7 -2
  3. package/.turbo/turbo-build.log +1 -1
  4. package/CHANGELOG.md +42 -0
  5. package/dist/components/job-detail/agent-config-failure-callout.d.ts.map +1 -0
  6. package/dist/components/{workflow-run-view → job-detail}/agent-config-failure-callout.js +2 -2
  7. package/dist/components/job-detail/agent-config-failure-callout.js.map +1 -0
  8. package/dist/components/job-detail/job-detail-header.d.ts +15 -0
  9. package/dist/components/job-detail/job-detail-header.d.ts.map +1 -0
  10. package/dist/components/job-detail/job-detail-header.js +124 -0
  11. package/dist/components/job-detail/job-detail-header.js.map +1 -0
  12. package/dist/components/job-detail/job-detail-view.d.ts +16 -0
  13. package/dist/components/job-detail/job-detail-view.d.ts.map +1 -0
  14. package/dist/components/job-detail/job-detail-view.js +420 -0
  15. package/dist/components/job-detail/job-detail-view.js.map +1 -0
  16. package/dist/components/job-detail/job-empty-states.d.ts +10 -0
  17. package/dist/components/job-detail/job-empty-states.d.ts.map +1 -0
  18. package/dist/components/job-detail/job-empty-states.js +195 -0
  19. package/dist/components/job-detail/job-empty-states.js.map +1 -0
  20. package/dist/components/job-detail/job-execution-switcher.d.ts.map +1 -0
  21. package/dist/components/job-detail/job-execution-switcher.js.map +1 -0
  22. package/dist/components/job-detail/job-execution-time-text.d.ts.map +1 -0
  23. package/dist/components/job-detail/job-execution-time-text.js.map +1 -0
  24. package/dist/components/job-detail/job-selection.d.ts +24 -0
  25. package/dist/components/job-detail/job-selection.d.ts.map +1 -0
  26. package/dist/components/job-detail/job-selection.js +66 -0
  27. package/dist/components/job-detail/job-selection.js.map +1 -0
  28. package/dist/components/job-detail/step-attempt-log-panel.d.ts +15 -0
  29. package/dist/components/job-detail/step-attempt-log-panel.d.ts.map +1 -0
  30. package/dist/components/{workflow-run-view → job-detail}/step-attempt-log-panel.js +61 -16
  31. package/dist/components/job-detail/step-attempt-log-panel.js.map +1 -0
  32. package/dist/components/job-graph/job-graph-content.js +1 -1
  33. package/dist/components/job-graph/job-graph-content.js.map +1 -1
  34. package/dist/components/step-list/step-list-model.js +2 -2
  35. package/dist/components/step-list/step-list-model.js.map +1 -1
  36. package/dist/components/step-list/step-list.d.ts +1 -0
  37. package/dist/components/step-list/step-list.d.ts.map +1 -1
  38. package/dist/components/step-list/step-list.js +29 -4
  39. package/dist/components/step-list/step-list.js.map +1 -1
  40. package/dist/components/workflow-run-duration-label.d.ts +9 -4
  41. package/dist/components/workflow-run-duration-label.d.ts.map +1 -1
  42. package/dist/components/workflow-run-duration-label.js +27 -7
  43. package/dist/components/workflow-run-duration-label.js.map +1 -1
  44. package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
  45. package/dist/components/workflow-run-list/workflow-run-row.js +6 -5
  46. package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
  47. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
  48. package/dist/components/workflow-run-summary/workflow-run-summary.js +69 -64
  49. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
  50. package/dist/components/workflow-run-tabs/index.d.ts +3 -3
  51. package/dist/components/workflow-run-tabs/index.d.ts.map +1 -1
  52. package/dist/components/workflow-run-tabs/index.js +3 -3
  53. package/dist/components/workflow-run-tabs/index.js.map +1 -1
  54. package/dist/components/workflow-run-tabs/run-annotation-count-chip.d.ts +16 -0
  55. package/dist/components/workflow-run-tabs/run-annotation-count-chip.d.ts.map +1 -0
  56. package/dist/components/workflow-run-tabs/run-annotation-count-chip.js +50 -0
  57. package/dist/components/workflow-run-tabs/run-annotation-count-chip.js.map +1 -0
  58. package/dist/components/workflow-run-tabs/run-annotation-item.d.ts +20 -0
  59. package/dist/components/workflow-run-tabs/run-annotation-item.d.ts.map +1 -0
  60. package/dist/components/workflow-run-tabs/run-annotation-item.js +102 -0
  61. package/dist/components/workflow-run-tabs/run-annotation-item.js.map +1 -0
  62. package/dist/components/workflow-run-tabs/run-annotation-list.d.ts +32 -0
  63. package/dist/components/workflow-run-tabs/run-annotation-list.d.ts.map +1 -0
  64. package/dist/components/workflow-run-tabs/run-annotation-list.js +202 -0
  65. package/dist/components/workflow-run-tabs/run-annotation-list.js.map +1 -0
  66. package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts +1 -1
  67. package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts.map +1 -1
  68. package/dist/components/workflow-run-tabs/run-annotation-summary-line.js +87 -22
  69. package/dist/components/workflow-run-tabs/run-annotation-summary-line.js.map +1 -1
  70. package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts +3 -1
  71. package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts.map +1 -1
  72. package/dist/components/workflow-run-tabs/run-annotations-empty.js +2 -2
  73. package/dist/components/workflow-run-tabs/run-annotations-empty.js.map +1 -1
  74. package/dist/components/workflow-run-tabs/severity-visuals.d.ts +20 -0
  75. package/dist/components/workflow-run-tabs/severity-visuals.d.ts.map +1 -0
  76. package/dist/components/workflow-run-tabs/severity-visuals.js +31 -0
  77. package/dist/components/workflow-run-tabs/severity-visuals.js.map +1 -0
  78. package/dist/components/workflow-run-view/index.d.ts +0 -1
  79. package/dist/components/workflow-run-view/index.d.ts.map +1 -1
  80. package/dist/components/workflow-run-view/index.js +0 -1
  81. package/dist/components/workflow-run-view/index.js.map +1 -1
  82. package/dist/components/workflow-run-view/run-workspace-nav.d.ts +20 -0
  83. package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -0
  84. package/dist/components/workflow-run-view/run-workspace-nav.js +277 -0
  85. package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -0
  86. package/dist/components/workflow-run-view/workflow-run-selection.d.ts.map +1 -1
  87. package/dist/components/workflow-run-view/workflow-run-selection.js +1 -14
  88. package/dist/components/workflow-run-view/workflow-run-selection.js.map +1 -1
  89. package/dist/components/workflow-run-view/workflow-run-view.d.ts +10 -10
  90. package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
  91. package/dist/components/workflow-run-view/workflow-run-view.js +351 -239
  92. package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
  93. package/dist/components/workflow-status/status-visuals.d.ts.map +1 -1
  94. package/dist/components/workflow-status/status-visuals.js +10 -2
  95. package/dist/components/workflow-status/status-visuals.js.map +1 -1
  96. package/dist/components/workflow-status/workflow-status-icon.d.ts +3 -1
  97. package/dist/components/workflow-status/workflow-status-icon.d.ts.map +1 -1
  98. package/dist/components/workflow-status/workflow-status-icon.js +13 -2
  99. package/dist/components/workflow-status/workflow-status-icon.js.map +1 -1
  100. package/dist/core/entities/job-execution.d.ts +9 -0
  101. package/dist/core/entities/job-execution.d.ts.map +1 -1
  102. package/dist/core/entities/job-execution.js +13 -3
  103. package/dist/core/entities/job-execution.js.map +1 -1
  104. package/dist/core/entities/job.d.ts.map +1 -1
  105. package/dist/core/entities/step-attempt.d.ts.map +1 -1
  106. package/dist/core/entities/step.d.ts +2 -0
  107. package/dist/core/entities/step.d.ts.map +1 -1
  108. package/dist/core/entities/step.js +13 -0
  109. package/dist/core/entities/step.js.map +1 -1
  110. package/dist/core/entities/workflow-run-attempt.d.ts.map +1 -1
  111. package/dist/core/entities/workflow-run.d.ts +56 -1
  112. package/dist/core/entities/workflow-run.d.ts.map +1 -1
  113. package/dist/core/entities/workflow-run.js +98 -0
  114. package/dist/core/entities/workflow-run.js.map +1 -1
  115. package/dist/core/run-annotation.d.ts +73 -0
  116. package/dist/core/run-annotation.d.ts.map +1 -0
  117. package/dist/core/run-annotation.js +125 -0
  118. package/dist/core/run-annotation.js.map +1 -0
  119. package/dist/core/workflow-run.d.ts +4 -4
  120. package/dist/core/workflow-run.d.ts.map +1 -1
  121. package/dist/core/workflow-run.js +3 -3
  122. package/dist/core/workflow-run.js.map +1 -1
  123. package/dist/feature.d.ts +20 -16
  124. package/dist/feature.d.ts.map +1 -1
  125. package/dist/feature.js +5 -0
  126. package/dist/feature.js.map +1 -1
  127. package/dist/hooks/api/run-annotation-mapper.d.ts +10 -0
  128. package/dist/hooks/api/run-annotation-mapper.d.ts.map +1 -0
  129. package/dist/hooks/api/run-annotation-mapper.js +22 -0
  130. package/dist/hooks/api/run-annotation-mapper.js.map +1 -0
  131. package/dist/hooks/api/run-annotations.d.ts +25 -0
  132. package/dist/hooks/api/run-annotations.d.ts.map +1 -0
  133. package/dist/hooks/api/run-annotations.js +78 -0
  134. package/dist/hooks/api/run-annotations.js.map +1 -0
  135. package/dist/hooks/api/workflow-runs.d.ts +5 -3
  136. package/dist/hooks/api/workflow-runs.d.ts.map +1 -1
  137. package/dist/hooks/api/workflow-runs.js +5 -4
  138. package/dist/hooks/api/workflow-runs.js.map +1 -1
  139. package/dist/index.d.ts +1 -0
  140. package/dist/index.d.ts.map +1 -1
  141. package/dist/index.js +1 -0
  142. package/dist/index.js.map +1 -1
  143. package/dist/pages/workflow-job-detail-page.d.ts +11 -0
  144. package/dist/pages/workflow-job-detail-page.d.ts.map +1 -0
  145. package/dist/pages/workflow-job-detail-page.js +66 -0
  146. package/dist/pages/workflow-job-detail-page.js.map +1 -0
  147. package/dist/pages/workflow-run-detail-page.d.ts.map +1 -1
  148. package/dist/pages/workflow-run-detail-page.js +4 -37
  149. package/dist/pages/workflow-run-detail-page.js.map +1 -1
  150. package/dist/routes/inputs.d.ts +52 -34
  151. package/dist/routes/inputs.d.ts.map +1 -1
  152. package/dist/routes/inputs.js +74 -43
  153. package/dist/routes/inputs.js.map +1 -1
  154. package/dist/routes/job-detail.d.ts +10 -0
  155. package/dist/routes/job-detail.d.ts.map +1 -0
  156. package/dist/routes/job-detail.js +27 -0
  157. package/dist/routes/job-detail.js.map +1 -0
  158. package/dist/routes/project-route.d.ts +1 -1
  159. package/dist/routes/run-detail.d.ts.map +1 -1
  160. package/dist/routes/runs.d.ts.map +1 -1
  161. package/dist/routes/workflows.d.ts.map +1 -1
  162. package/dist/tsconfig.test.tsbuildinfo +1 -1
  163. package/package.json +10 -9
  164. package/src/components/{workflow-run-view → job-detail}/agent-config-failure-callout.stories.tsx +1 -1
  165. package/src/components/{workflow-run-view → job-detail}/agent-config-failure-callout.tsx +2 -2
  166. package/src/components/job-detail/job-detail-header.test.tsx +34 -0
  167. package/src/components/job-detail/job-detail-header.tsx +136 -0
  168. package/src/components/job-detail/job-detail-view.stories.tsx +531 -0
  169. package/src/components/job-detail/job-detail-view.tsx +467 -0
  170. package/src/components/job-detail/job-empty-states.test.ts +27 -0
  171. package/src/components/job-detail/job-empty-states.tsx +248 -0
  172. package/src/components/job-detail/job-selection.test.ts +188 -0
  173. package/src/components/job-detail/job-selection.ts +96 -0
  174. package/src/components/{workflow-run-view → job-detail}/step-attempt-log-panel.test.tsx +32 -9
  175. package/src/components/{workflow-run-view → job-detail}/step-attempt-log-panel.tsx +52 -18
  176. package/src/components/job-graph/job-graph-content.tsx +1 -1
  177. package/src/components/step-list/step-list-model.test.ts +1 -1
  178. package/src/components/step-list/step-list-model.ts +2 -2
  179. package/src/components/step-list/step-list.stories.tsx +1 -1
  180. package/src/components/step-list/step-list.test.tsx +33 -0
  181. package/src/components/step-list/step-list.tsx +40 -9
  182. package/src/components/workflow-run-duration-label.tsx +50 -12
  183. package/src/components/workflow-run-list/workflow-run-row.tsx +6 -4
  184. package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +38 -0
  185. package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +79 -5
  186. package/src/components/workflow-run-summary/workflow-run-summary.tsx +56 -60
  187. package/src/components/workflow-run-tabs/index.ts +3 -3
  188. package/src/components/workflow-run-tabs/run-annotation-count-chip.tsx +63 -0
  189. package/src/components/workflow-run-tabs/run-annotation-item.tsx +123 -0
  190. package/src/components/workflow-run-tabs/run-annotation-list.tsx +258 -0
  191. package/src/components/workflow-run-tabs/run-annotation-summary-line.test.tsx +77 -3
  192. package/src/components/workflow-run-tabs/run-annotation-summary-line.tsx +92 -17
  193. package/src/components/workflow-run-tabs/run-annotations-empty.tsx +6 -2
  194. package/src/components/workflow-run-tabs/run-annotations.stories.tsx +293 -0
  195. package/src/components/workflow-run-tabs/severity-visuals.ts +37 -0
  196. package/src/components/workflow-run-view/index.ts +0 -1
  197. package/src/components/workflow-run-view/run-workspace-nav.test.tsx +286 -0
  198. package/src/components/workflow-run-view/run-workspace-nav.tsx +314 -0
  199. package/src/components/workflow-run-view/workflow-run-selection.ts +1 -23
  200. package/src/components/workflow-run-view/workflow-run-view.test.tsx +237 -1085
  201. package/src/components/workflow-run-view/workflow-run-view.tsx +450 -323
  202. package/src/components/workflow-status/status-visuals.ts +5 -2
  203. package/src/components/workflow-status/workflow-status-icon.stories.tsx +1 -0
  204. package/src/components/workflow-status/workflow-status-icon.tsx +26 -1
  205. package/src/core/entities/job-execution.ts +17 -2
  206. package/src/core/entities/step.ts +26 -0
  207. package/src/core/entities/workflow-run-display.test.ts +159 -0
  208. package/src/core/entities/workflow-run.ts +139 -1
  209. package/src/core/run-annotation.test.ts +315 -0
  210. package/src/core/run-annotation.ts +238 -0
  211. package/src/core/workflow-run.ts +16 -1
  212. package/src/feature.ts +5 -0
  213. package/src/hooks/api/run-annotation-mapper.ts +30 -0
  214. package/src/hooks/api/run-annotations.ts +137 -0
  215. package/src/hooks/api/workflow-runs.ts +6 -2
  216. package/src/index.ts +1 -0
  217. package/src/page-harness-budget.test.ts +1 -0
  218. package/src/pages/workflow-job-detail-page.test.tsx +605 -0
  219. package/src/pages/workflow-job-detail-page.tsx +84 -0
  220. package/src/pages/workflow-run-detail-page.tsx +3 -43
  221. package/src/pages/workflow-run-pages.test.tsx +77 -332
  222. package/src/routes/inputs.test.ts +56 -6
  223. package/src/routes/inputs.ts +71 -19
  224. package/src/routes/job-detail.tsx +29 -0
  225. package/test/pages.tsx +25 -3
  226. package/test/render.tsx +6 -1
  227. package/tsconfig.build.tsbuildinfo +1 -1
  228. package/dist/components/workflow-run-tabs/run-jobs-list.d.ts +0 -18
  229. package/dist/components/workflow-run-tabs/run-jobs-list.d.ts.map +0 -1
  230. package/dist/components/workflow-run-tabs/run-jobs-list.js +0 -107
  231. package/dist/components/workflow-run-tabs/run-jobs-list.js.map +0 -1
  232. package/dist/components/workflow-run-tabs/run-tab-count.d.ts +0 -6
  233. package/dist/components/workflow-run-tabs/run-tab-count.d.ts.map +0 -1
  234. package/dist/components/workflow-run-tabs/run-tab-count.js +0 -27
  235. package/dist/components/workflow-run-tabs/run-tab-count.js.map +0 -1
  236. package/dist/components/workflow-run-tabs/run-tab-strip.d.ts +0 -8
  237. package/dist/components/workflow-run-tabs/run-tab-strip.d.ts.map +0 -1
  238. package/dist/components/workflow-run-tabs/run-tab-strip.js +0 -82
  239. package/dist/components/workflow-run-tabs/run-tab-strip.js.map +0 -1
  240. package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts.map +0 -1
  241. package/dist/components/workflow-run-view/agent-config-failure-callout.js.map +0 -1
  242. package/dist/components/workflow-run-view/job-card.d.ts +0 -17
  243. package/dist/components/workflow-run-view/job-card.d.ts.map +0 -1
  244. package/dist/components/workflow-run-view/job-card.js +0 -496
  245. package/dist/components/workflow-run-view/job-card.js.map +0 -1
  246. package/dist/components/workflow-run-view/job-execution-switcher.d.ts.map +0 -1
  247. package/dist/components/workflow-run-view/job-execution-switcher.js.map +0 -1
  248. package/dist/components/workflow-run-view/job-execution-time-text.d.ts.map +0 -1
  249. package/dist/components/workflow-run-view/job-execution-time-text.js.map +0 -1
  250. package/dist/components/workflow-run-view/step-attempt-log-panel.d.ts +0 -9
  251. package/dist/components/workflow-run-view/step-attempt-log-panel.d.ts.map +0 -1
  252. package/dist/components/workflow-run-view/step-attempt-log-panel.js.map +0 -1
  253. package/dist/core/workflow-run-tabs.d.ts +0 -9
  254. package/dist/core/workflow-run-tabs.d.ts.map +0 -1
  255. package/dist/core/workflow-run-tabs.js +0 -3
  256. package/dist/core/workflow-run-tabs.js.map +0 -1
  257. package/src/components/workflow-run-tabs/run-jobs-list.tsx +0 -151
  258. package/src/components/workflow-run-tabs/run-tab-count.tsx +0 -28
  259. package/src/components/workflow-run-tabs/run-tab-strip.stories.tsx +0 -52
  260. package/src/components/workflow-run-tabs/run-tab-strip.test.tsx +0 -117
  261. package/src/components/workflow-run-tabs/run-tab-strip.tsx +0 -97
  262. package/src/components/workflow-run-view/job-card.stories.tsx +0 -789
  263. package/src/components/workflow-run-view/job-card.test.tsx +0 -115
  264. package/src/components/workflow-run-view/job-card.tsx +0 -614
  265. package/src/core/workflow-run-tabs.ts +0 -8
  266. /package/dist/components/{workflow-run-view → job-detail}/agent-config-failure-callout.d.ts +0 -0
  267. /package/dist/components/{workflow-run-view → job-detail}/job-execution-switcher.d.ts +0 -0
  268. /package/dist/components/{workflow-run-view → job-detail}/job-execution-switcher.js +0 -0
  269. /package/dist/components/{workflow-run-view → job-detail}/job-execution-time-text.d.ts +0 -0
  270. /package/dist/components/{workflow-run-view → job-detail}/job-execution-time-text.js +0 -0
  271. /package/src/components/{workflow-run-view → job-detail}/job-execution-switcher.tsx +0 -0
  272. /package/src/components/{workflow-run-view → job-detail}/job-execution-time-text.tsx +0 -0
@@ -1,31 +1,47 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ApiError } from '@shipfox/client-api';
3
3
  import { QueryLoadError } from '@shipfox/client-ui';
4
4
  import { EmptyState } from '@shipfox/react-ui/empty-state';
5
5
  import { RelativeTimeProvider } from '@shipfox/react-ui/relative-time';
6
- import { Tabs, TabsContent, TabsContents } from '@shipfox/react-ui/tabs';
6
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@shipfox/react-ui/select';
7
7
  import { toast } from '@shipfox/react-ui/toast';
8
+ import { Text } from '@shipfox/react-ui/typography';
8
9
  import { useNavigate } from '@tanstack/react-router';
9
- import { useEffect, useId, useRef, useState } from 'react';
10
- import { resolveJobExecution } from '#core/workflow-run.js';
10
+ import { useEffect, useMemo } from 'react';
11
+ import { buildRunAnnotationList } from '#core/run-annotation.js';
12
+ import { isWorkflowRunTerminal } from '#core/workflow-run.js';
11
13
  import { withoutWorkflowRunSelectionSearch } from '#core/workflow-run-url-state.js';
14
+ import { useRunAnnotationsQuery } from '#hooks/api/run-annotations.js';
12
15
  import { useCancelWorkflowRunMutation, useRerunWorkflowRunMutation, useWorkflowRunAttemptQuery } from '#hooks/api/workflow-runs.js';
13
- import { WORKFLOW_RUN_TABS } from '#routes/inputs.js';
16
+ import { workflowJobSearchParams, workflowRunSearchParams } from '#routes/inputs.js';
14
17
  import { JobGraph } from '../job-graph/index.js';
15
18
  import { WorkflowRunSummary } from '../workflow-run-summary/index.js';
16
- import { RunAnnotationSummaryLine, RunAnnotationsEmpty, RunJobsList, RunTabStrip } from '../workflow-run-tabs/index.js';
17
- import { WorkflowSourceContent, WorkflowSourcePanel } from '../workflow-source-panel/index.js';
19
+ import { RunAnnotationList, RunAnnotationSummaryLine } from '../workflow-run-tabs/index.js';
20
+ import { WorkflowSourceContent } from '../workflow-source-panel/index.js';
21
+ import { RunWorkspaceNav } from './run-workspace-nav.js';
18
22
  import { resolveWorkflowRunSelection } from './workflow-run-selection.js';
19
23
  import { WorkflowRunContentSkeleton, WorkflowRunNotFound, WorkflowRunSkeleton, WorkflowRunStaleError } from './workflow-run-states.js';
20
24
  /**
21
- * Renders the run for `workflowRunId`, keeping the local tab strip mounted while the run
22
- * request is pending so polling never inserts navigation under the user's cursor.
23
- */ export function WorkflowRunView({ projectId, workspaceSlug, projectSlug, workflowRunId, selection, onSelectionChange, tab, onTabChange, annotationSummary }) {
25
+ * The persistent run workspace. Run-level sections and dedicated job routes share the same
26
+ * header and job index, while the all-jobs Summary always opens on the dependency graph.
27
+ */ export function WorkflowRunView({ projectId, workspaceSlug, projectSlug, workflowRunId, runAttempt, selection, tab, activeJobId, jobSearch, jobContent }) {
24
28
  const runQuery = useWorkflowRunAttemptQuery({
25
29
  workflowRunId,
26
- runAttempt: selection?.runAttempt
30
+ runAttempt: selection?.runAttempt ?? runAttempt
27
31
  });
28
32
  const rerunMutation = useRerunWorkflowRunMutation(projectId);
33
+ // Annotations are read on their own cadence rather than hydrated into the run, so the two
34
+ // modules stay decoupled. The key is shared, so the job page's count chip costs no extra fetch.
35
+ //
36
+ // Polling is scoped to the surface that renders bodies. The read has no counts-only mode, so
37
+ // every poll transfers whole Markdown bodies; doing that every four seconds behind Summary,
38
+ // Source, and the job log spends megabytes to keep one rail number warm. Off that surface the
39
+ // counts still refresh on navigation and on window focus.
40
+ const annotationsQuery = useRunAnnotationsQuery({
41
+ workflowRunId,
42
+ runAttempt: runQuery.data?.runAttempt.attempt,
43
+ live: runWorkspaceSection(tab) === 'annotations' && !activeJobId && Boolean(runQuery.data) && !isWorkflowRunTerminal(runQuery.data?.runAttempt.status ?? 'pending')
44
+ });
29
45
  return /*#__PURE__*/ _jsx(RelativeTimeProvider, {
30
46
  children: /*#__PURE__*/ _jsx("div", {
31
47
  className: "flex min-h-0 min-w-0 flex-1 overflow-hidden",
@@ -33,80 +49,61 @@ import { WorkflowRunContentSkeleton, WorkflowRunNotFound, WorkflowRunSkeleton, W
33
49
  workspaceSlug: workspaceSlug,
34
50
  projectSlug: projectSlug,
35
51
  query: runQuery,
52
+ annotations: annotationsQuery,
36
53
  rerunMutation: rerunMutation,
54
+ runAttempt: runAttempt,
37
55
  selection: selection,
38
- onSelectionChange: onSelectionChange,
39
56
  tab: tab,
40
- onTabChange: onTabChange,
41
- annotationSummary: annotationSummary
57
+ activeJobId: activeJobId,
58
+ jobSearch: jobSearch,
59
+ jobContent: jobContent
42
60
  })
43
61
  })
44
62
  });
45
63
  }
46
- function RunViewContent({ workspaceSlug, projectSlug, query, rerunMutation, selection, onSelectionChange, tab, onTabChange, annotationSummary }) {
64
+ function RunViewContent({ workspaceSlug, projectSlug, query, annotations, rerunMutation, runAttempt, selection, tab, activeJobId, jobSearch, jobContent }) {
47
65
  const navigate = useNavigate();
48
- const [selectedJobId, setSelectedJobId] = useState();
49
- const [selectedJobExecutionId, setSelectedJobExecutionId] = useState();
50
- const [localTab, setLocalTab] = useState(tab ?? 'summary');
51
- const [sourcePanelOpen, setSourcePanelOpen] = useState(false);
52
- const [sourceFocus, setSourceFocus] = useState(null);
53
- const sourcePanelId = useId();
54
- // The step Source button that last opened the panel, so Escape / Close returns focus to it.
55
- const lastSourceTriggerRef = useRef(null);
56
- const selectionControlled = selection !== undefined;
57
- const tabControlled = onTabChange !== undefined;
58
- const activeTab = tabControlled ? tab ?? 'summary' : localTab;
59
- const sourceAvailable = query.data?.sourceSnapshot !== null && query.data?.sourceSnapshot !== undefined;
60
- const cancelMutation = useCancelWorkflowRunMutation(query.data);
61
- useEffect(()=>{
62
- if (!sourceAvailable) {
63
- setSourcePanelOpen(false);
64
- setSourceFocus(null);
65
- }
66
- }, [
67
- sourceAvailable
68
- ]);
69
- // If a refetch drops the focused step or its location, close the panel so it never points at
70
- // an unmounted Source button.
71
- useEffect(()=>{
72
- if (!sourceFocus) return;
73
- const stillLocated = query.data?.jobs.some((job)=>job.jobExecutions.some((execution)=>execution.steps.some((step)=>step.id === sourceFocus.stepId && step.sourceLocation)));
74
- if (!stillLocated) {
75
- setSourceFocus(null);
76
- setSourcePanelOpen(false);
77
- }
78
- }, [
79
- sourceFocus,
80
- query.data
81
- ]);
82
66
  const runData = query.data;
83
- const resolvedSelection = selectionControlled && runData ? resolveWorkflowRunSelection({
67
+ const annotationSummary = annotations.summary;
68
+ const activeSection = runWorkspaceSection(tab);
69
+ const cancelMutation = useCancelWorkflowRunMutation(runData);
70
+ const sourceSnapshot = runData?.sourceSnapshot ?? null;
71
+ const resolvedSelection = runData && selection ? resolveWorkflowRunSelection({
84
72
  run: runData,
85
- selection: selection
73
+ selection
86
74
  }) : undefined;
87
- const hasExplicitJobSelection = Boolean(selection?.jobId || selection?.jobExecutionId || selection?.stepId || selection?.stepAttemptId);
88
- const selectedJob = selectionControlled ? hasExplicitJobSelection ? resolvedSelection?.job : undefined : runData?.jobs.find((job)=>job.id === selectedJobId) ?? runData?.jobs.at(0);
89
- const selectedJobExecution = selectionControlled ? resolvedSelection?.jobExecution : selectedJob ? resolveJobExecution(selectedJob, selectedJobExecutionId) : undefined;
90
- const selectedAttemptId = selectionControlled ? resolvedSelection?.selectedAttemptId ?? null : undefined;
91
- // Explicit per-step focus wins; fall back to the URL-selected step so deep links still
92
- // pre-highlight source in the Source tab.
93
- const highlightedLineRange = sourceFocus?.location ?? resolvedSelection?.step?.sourceLocation ?? null;
94
- const sourceSnapshot = runData?.sourceSnapshot ?? null;
95
- function changeTab(nextTab) {
96
- if (!tabControlled) {
97
- setLocalTab(nextTab);
98
- } else if (activeTab !== nextTab) {
99
- onTabChange?.(nextTab);
100
- }
101
- if (nextTab !== 'jobs') {
102
- setSourcePanelOpen(false);
103
- setSourceFocus(null);
75
+ const highlightedLineRange = resolvedSelection?.step?.sourceLocation ?? null;
76
+ useEffect(()=>{
77
+ const hasLegacyJobSelection = Boolean(selection?.jobId || selection?.jobExecutionId || selection?.stepId || selection?.stepAttemptId);
78
+ if (activeJobId || activeSection !== 'summary' || !hasLegacyJobSelection || !resolvedSelection?.job || !runData || !workspaceSlug || !projectSlug) {
79
+ return;
104
80
  }
105
- }
106
- function handleTabChange(nextTab) {
107
- if (!WORKFLOW_RUN_TABS.some((value)=>value === nextTab)) return;
108
- changeTab(nextTab);
109
- }
81
+ void navigate({
82
+ to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId',
83
+ params: {
84
+ workspaceSlug,
85
+ projectSlug,
86
+ workflowRunId: runData.id,
87
+ jobId: resolvedSelection.job.id
88
+ },
89
+ search: workflowJobSearchParams({
90
+ jobExecutionId: selection?.jobExecutionId,
91
+ stepId: selection?.stepId,
92
+ stepAttemptId: selection?.stepAttemptId,
93
+ runAttempt: selection?.runAttempt
94
+ }),
95
+ replace: true
96
+ });
97
+ }, [
98
+ activeJobId,
99
+ activeSection,
100
+ navigate,
101
+ projectSlug,
102
+ resolvedSelection?.job,
103
+ runData,
104
+ selection,
105
+ workspaceSlug
106
+ ]);
110
107
  async function rerun(mode) {
111
108
  if (!runData || !workspaceSlug || !projectSlug) {
112
109
  toast.error('Could not start re-run from this route.');
@@ -131,206 +128,321 @@ function RunViewContent({ workspaceSlug, projectSlug, query, rerunMutation, sele
131
128
  toast.error(error instanceof ApiError ? error.message : 'Could not start re-run');
132
129
  }
133
130
  }
134
- function selectJob(jobId, source = 'pointer') {
135
- if (source === 'pointer') changeTab('jobs');
136
- if (!selectionControlled) {
137
- setSelectedJobId(jobId);
138
- setSelectedJobExecutionId(undefined);
139
- return;
140
- }
141
- // Keyboard roving focus is an interaction within the Summary graph. Keep that panel mounted
142
- // and let JobGraphView own the visual selection so focus is not moved to a different tab.
143
- if (source === 'keyboard') return;
144
- onSelectionChange?.(jobId ? {
145
- jobId,
146
- runAttempt: selection?.runAttempt
147
- } : {
148
- runAttempt: selection?.runAttempt
149
- });
150
- }
151
- function selectJobExecution(jobExecutionId) {
152
- if (!selectionControlled) {
153
- setSelectedJobExecutionId(jobExecutionId);
154
- return;
155
- }
156
- if (!selectedJob) return;
157
- onSelectionChange?.({
158
- jobId: selectedJob.id,
159
- jobExecutionId,
160
- runAttempt: selection?.runAttempt
131
+ function selectGraphJob(jobId, source = 'pointer') {
132
+ if (source !== 'pointer' || !jobId || !runData || !workspaceSlug || !projectSlug) return;
133
+ void navigate({
134
+ to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId',
135
+ params: {
136
+ workspaceSlug,
137
+ projectSlug,
138
+ workflowRunId: runData.id,
139
+ jobId
140
+ },
141
+ search: workflowJobSearchParams({
142
+ runAttempt: selection?.runAttempt ?? runAttempt ?? runData.runAttempt.attempt
143
+ })
161
144
  });
162
145
  }
163
- function selectAttempt(attemptId) {
164
- if (!selectionControlled || !selectedJob || !selectedJobExecution) return;
165
- if (!attemptId) {
166
- onSelectionChange?.({
167
- jobId: selectedJob.id,
168
- jobExecutionId: selectedJobExecution.id,
169
- runAttempt: selection?.runAttempt
170
- });
171
- return;
172
- }
173
- const match = findAttemptSelection(selectedJobExecution, attemptId);
174
- if (!match) return;
175
- onSelectionChange?.({
176
- jobId: selectedJob.id,
177
- jobExecutionId: selectedJobExecution.id,
178
- stepId: match.stepId,
179
- stepAttemptId: match.attemptId,
180
- runAttempt: selection?.runAttempt
146
+ function selectAnnotationJob(jobId) {
147
+ if (!runData || !workspaceSlug || !projectSlug) return;
148
+ const nextSearch = {
149
+ ...selection,
150
+ tab: 'annotations'
151
+ };
152
+ if (jobId) nextSearch.jobId = jobId;
153
+ else delete nextSearch.jobId;
154
+ delete nextSearch.jobExecutionId;
155
+ delete nextSearch.stepId;
156
+ delete nextSearch.stepAttemptId;
157
+ void navigate({
158
+ to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',
159
+ params: {
160
+ workspaceSlug,
161
+ projectSlug,
162
+ workflowRunId: runData.id
163
+ },
164
+ search: workflowRunSearchParams(nextSearch, nextSearch)
181
165
  });
182
166
  }
183
- function openStepSource(stepId, location, trigger) {
184
- setSourceFocus({
185
- stepId,
186
- location
167
+ function clearAnnotationFilters() {
168
+ if (!runData || !workspaceSlug || !projectSlug) return;
169
+ const nextSearch = {
170
+ ...selection,
171
+ tab: 'annotations'
172
+ };
173
+ delete nextSearch.jobId;
174
+ delete nextSearch.jobExecutionId;
175
+ delete nextSearch.stepId;
176
+ delete nextSearch.stepAttemptId;
177
+ delete nextSearch.severity;
178
+ delete nextSearch.annotation;
179
+ void navigate({
180
+ to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',
181
+ params: {
182
+ workspaceSlug,
183
+ projectSlug,
184
+ workflowRunId: runData.id
185
+ },
186
+ search: workflowRunSearchParams(nextSearch, nextSearch),
187
+ replace: true
187
188
  });
188
- lastSourceTriggerRef.current = trigger;
189
- setSourcePanelOpen(true);
190
- }
191
- function closeSourcePanel() {
192
- const trigger = lastSourceTriggerRef.current;
193
- setSourcePanelOpen(false);
194
- // Defer so focus lands after the panel unmounts; clear the focus only after focusing so the
195
- // opener button is still expanded (force-visible) on return.
196
- window.setTimeout(()=>{
197
- if (trigger?.isConnected) trigger.focus();
198
- setSourceFocus(null);
199
- }, 0);
200
189
  }
201
190
  function cancelRun() {
202
191
  cancelMutation.mutate(undefined, {
203
- onError: (error)=>{
204
- toast.error(cancelErrorMessage(error));
205
- }
192
+ onError: (error)=>toast.error(cancelErrorMessage(error))
206
193
  });
207
194
  }
208
195
  const fatalError = query.isError && runData === undefined;
209
- const tabState = fatalError ? query.error instanceof ApiError && query.error.status === 404 ? /*#__PURE__*/ _jsx(WorkflowRunNotFound, {}) : /*#__PURE__*/ _jsx(QueryLoadError, {
196
+ const loadingOrError = fatalError ? query.error instanceof ApiError && query.error.status === 404 ? /*#__PURE__*/ _jsx(WorkflowRunNotFound, {}) : /*#__PURE__*/ _jsx(QueryLoadError, {
210
197
  query: query,
211
198
  subject: "workflow run",
212
199
  icon: "pulseLine"
213
200
  }) : query.isPending || runData === undefined ? /*#__PURE__*/ _jsx(WorkflowRunContentSkeleton, {}) : null;
214
- return /*#__PURE__*/ _jsxs(_Fragment, {
201
+ return /*#__PURE__*/ _jsxs("div", {
202
+ className: "flex min-h-0 min-w-0 flex-1 flex-col",
215
203
  children: [
216
- /*#__PURE__*/ _jsxs(Tabs, {
217
- value: activeTab,
218
- onValueChange: handleTabChange,
219
- className: "flex min-w-0 flex-1 flex-col gap-0",
204
+ runData ? /*#__PURE__*/ _jsx(WorkflowRunSummary, {
205
+ workspaceSlug: workspaceSlug,
206
+ projectSlug: projectSlug,
207
+ run: runData,
208
+ cancelling: cancelMutation.isPending,
209
+ onCancel: cancelRun,
210
+ rerunPending: rerunMutation.isPending,
211
+ onRerun: (mode)=>void rerun(mode),
212
+ latestAttempt: runData.latestAttempt
213
+ }) : /*#__PURE__*/ _jsx(WorkflowRunSkeleton, {}),
214
+ runData && query.isError ? /*#__PURE__*/ _jsx(WorkflowRunStaleError, {
215
+ query: query
216
+ }) : null,
217
+ /*#__PURE__*/ _jsxs("div", {
218
+ "data-run-workspace-layout": true,
219
+ className: "flex min-h-0 min-w-0 flex-1 flex-col border-t border-border-neutral-base min-[768px]:flex-row",
220
220
  children: [
221
- runData ? /*#__PURE__*/ _jsx(WorkflowRunSummary, {
221
+ runData && workspaceSlug && projectSlug ? /*#__PURE__*/ _jsx(RunWorkspaceNav, {
222
222
  workspaceSlug: workspaceSlug,
223
223
  projectSlug: projectSlug,
224
224
  run: runData,
225
- cancelling: cancelMutation.isPending,
226
- onCancel: cancelRun,
227
- rerunPending: rerunMutation.isPending,
228
- onRerun: (mode)=>void rerun(mode),
229
- latestAttempt: runData.latestAttempt
230
- }) : /*#__PURE__*/ _jsx(WorkflowRunSkeleton, {}),
231
- runData && query.isError ? /*#__PURE__*/ _jsx(WorkflowRunStaleError, {
232
- query: query
233
- }) : null,
234
- /*#__PURE__*/ _jsx(RunTabStrip, {
235
- jobCount: runData?.jobs.length,
236
- jobsFailed: runData?.jobs.filter((job)=>job.status === 'failed').length,
225
+ activeSection: activeSection,
226
+ currentJobId: activeJobId,
227
+ jobSearch: jobSearch,
237
228
  annotationSummary: annotationSummary
229
+ }) : /*#__PURE__*/ _jsx(RunWorkspaceNavSkeleton, {}),
230
+ /*#__PURE__*/ _jsx("div", {
231
+ "data-run-workspace-content": true,
232
+ className: "flex min-h-0 min-w-0 flex-1 flex-col bg-background-neutral-base",
233
+ children: runData && jobContent ? jobContent : runData ? /*#__PURE__*/ _jsx(RunSectionContent, {
234
+ section: activeSection,
235
+ run: runData,
236
+ annotations: annotations,
237
+ annotationSummary: annotationSummary,
238
+ workspaceSlug: workspaceSlug,
239
+ projectSlug: projectSlug,
240
+ selection: selection,
241
+ selectedJobId: selection?.jobId,
242
+ onSelectGraphJob: selectGraphJob,
243
+ onSelectAnnotationJob: selectAnnotationJob,
244
+ onClearAnnotationFilters: clearAnnotationFilters,
245
+ sourceSnapshot: sourceSnapshot,
246
+ highlightedLineRange: highlightedLineRange
247
+ }) : /*#__PURE__*/ _jsx("div", {
248
+ className: "min-h-0 flex-1 overflow-auto p-24",
249
+ children: loadingOrError
250
+ })
251
+ })
252
+ ]
253
+ })
254
+ ]
255
+ });
256
+ }
257
+ function RunSectionContent({ section, run, annotations, annotationSummary, workspaceSlug, projectSlug, selection, selectedJobId, onSelectGraphJob, onSelectAnnotationJob, onClearAnnotationFilters, sourceSnapshot, highlightedLineRange }) {
258
+ if (section === 'summary') {
259
+ return /*#__PURE__*/ _jsx("section", {
260
+ "aria-label": "All jobs summary",
261
+ className: "min-h-0 flex-1 overflow-auto pb-24 pt-16",
262
+ children: /*#__PURE__*/ _jsxs("div", {
263
+ className: "mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24",
264
+ children: [
265
+ /*#__PURE__*/ _jsx(Text, {
266
+ as: "h2",
267
+ className: "sr-only",
268
+ children: "All jobs summary"
238
269
  }),
239
- /*#__PURE__*/ _jsxs(TabsContents, {
240
- className: "min-h-0 flex-1 overflow-auto bg-background-neutral-background pb-24 pt-16",
241
- children: [
242
- /*#__PURE__*/ _jsx(TabsContent, {
243
- value: "summary",
244
- tabIndex: -1,
245
- className: "mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24 outline-none",
246
- children: runData ? /*#__PURE__*/ _jsx(JobGraph, {
247
- run: runData,
248
- selectedJobId: selectedJob?.id,
249
- onSelectedJobChange: selectJob
250
- }) : tabState
251
- }),
252
- /*#__PURE__*/ _jsx(TabsContent, {
253
- value: "jobs",
254
- tabIndex: -1,
255
- className: "mx-auto w-full max-w-[1120px] px-24 outline-none",
256
- children: runData ? /*#__PURE__*/ _jsx(RunJobsList, {
257
- jobs: runData.jobs,
258
- selectedJobId: selectedJob?.id,
259
- onSelectedJobChange: selectJob,
260
- workspaceSlug: workspaceSlug,
261
- selectedJobExecution: selectedJobExecution,
262
- selectedAttemptId: selectedAttemptId,
263
- onSelectedJobExecutionChange: selectJobExecution,
264
- onSelectedAttemptChange: selectionControlled ? selectAttempt : undefined,
265
- sourcePanelId: sourcePanelId,
266
- sourceAvailable: sourceAvailable,
267
- focusedSourceStepId: sourceFocus?.stepId ?? null,
268
- onOpenStepSource: openStepSource
269
- }) : tabState
270
- }),
271
- /*#__PURE__*/ _jsx(TabsContent, {
272
- value: "annotations",
273
- tabIndex: -1,
274
- className: "mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24 outline-none",
275
- children: runData ? /*#__PURE__*/ _jsxs(_Fragment, {
276
- children: [
277
- /*#__PURE__*/ _jsx(RunAnnotationSummaryLine, {
278
- summary: annotationSummary,
279
- workspaceSlug: workspaceSlug,
280
- projectSlug: projectSlug,
281
- workflowRunId: runData.id,
282
- search: selection
283
- }),
284
- /*#__PURE__*/ _jsx(RunAnnotationsEmpty, {})
285
- ]
286
- }) : tabState
287
- }),
288
- /*#__PURE__*/ _jsx(TabsContent, {
289
- value: "source",
290
- keepMounted: sourceSnapshot !== null,
291
- tabIndex: -1,
292
- className: "mx-auto flex min-h-full w-full max-w-[1120px] px-24 outline-none",
293
- children: runData ? sourceSnapshot ? /*#__PURE__*/ _jsx(WorkflowSourceContent, {
294
- source: sourceSnapshot,
295
- highlightedLineRange: highlightedLineRange,
296
- scrollHighlightedIntoView: true
297
- }) : /*#__PURE__*/ _jsx(EmptyState, {
298
- className: "min-h-160 w-full",
299
- icon: "fileDamageLine",
300
- title: "Source snapshot unavailable",
301
- description: runData.isTemporary ? 'Temporary runs do not capture workflow source.' : 'This run was created before workflow source snapshots were captured.'
302
- }) : tabState
303
- })
304
- ]
270
+ /*#__PURE__*/ _jsx(JobGraph, {
271
+ run: run,
272
+ selectedJobId: selectedJobId,
273
+ onSelectedJobChange: onSelectGraphJob,
274
+ className: "min-h-160 overflow-hidden"
305
275
  })
306
276
  ]
277
+ })
278
+ });
279
+ }
280
+ if (section === 'annotations') {
281
+ return /*#__PURE__*/ _jsx(RunAnnotationsSection, {
282
+ run: run,
283
+ annotations: annotations,
284
+ annotationSummary: annotationSummary,
285
+ workspaceSlug: workspaceSlug,
286
+ projectSlug: projectSlug,
287
+ selection: selection,
288
+ selectedJobId: selectedJobId,
289
+ onSelectAnnotationJob: onSelectAnnotationJob,
290
+ onClearAnnotationFilters: onClearAnnotationFilters
291
+ });
292
+ }
293
+ return /*#__PURE__*/ _jsx("section", {
294
+ "aria-label": "Workflow source",
295
+ className: "min-h-0 flex-1 overflow-auto pb-24 pt-16",
296
+ children: /*#__PURE__*/ _jsxs("div", {
297
+ className: "mx-auto flex min-h-full w-full max-w-[1120px] flex-col px-24",
298
+ children: [
299
+ /*#__PURE__*/ _jsx(Text, {
300
+ as: "h2",
301
+ className: "sr-only",
302
+ children: "Workflow source"
303
+ }),
304
+ sourceSnapshot ? /*#__PURE__*/ _jsx(WorkflowSourceContent, {
305
+ source: sourceSnapshot,
306
+ highlightedLineRange: highlightedLineRange,
307
+ scrollHighlightedIntoView: true
308
+ }) : /*#__PURE__*/ _jsx(EmptyState, {
309
+ className: "min-h-160 w-full",
310
+ icon: "fileDamageLine",
311
+ title: "Source snapshot unavailable",
312
+ description: run.isTemporary ? 'Temporary runs do not capture workflow source.' : 'This run was created before workflow source snapshots were captured.'
313
+ })
314
+ ]
315
+ })
316
+ });
317
+ }
318
+ function RunAnnotationsSection({ run, annotations, annotationSummary, workspaceSlug, projectSlug, selection, selectedJobId, onSelectAnnotationJob, onClearAnnotationFilters }) {
319
+ const selectedJob = run.jobs.find((job)=>job.id === selectedJobId);
320
+ const severity = selection?.severity;
321
+ const records = annotations.annotations;
322
+ const entries = useMemo(()=>records ? buildRunAnnotationList({
323
+ annotations: records,
324
+ jobs: run.jobs,
325
+ severity,
326
+ jobId: selectedJob?.id
327
+ }) : undefined, [
328
+ records,
329
+ run.jobs,
330
+ selectedJob?.id,
331
+ severity
332
+ ]);
333
+ return /*#__PURE__*/ _jsx("section", {
334
+ "aria-label": "Run annotations",
335
+ className: "min-h-0 flex-1 overflow-auto pb-24 pt-16",
336
+ children: /*#__PURE__*/ _jsxs("div", {
337
+ className: "mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24",
338
+ children: [
339
+ /*#__PURE__*/ _jsx(Text, {
340
+ as: "h2",
341
+ className: "sr-only",
342
+ children: "Annotations"
343
+ }),
344
+ /*#__PURE__*/ _jsxs("div", {
345
+ className: "flex flex-wrap items-center justify-between gap-8",
346
+ children: [
347
+ /*#__PURE__*/ _jsx(RunAnnotationSummaryLine, {
348
+ summary: annotationSummary,
349
+ workspaceSlug: workspaceSlug,
350
+ projectSlug: projectSlug,
351
+ workflowRunId: run.id,
352
+ search: selection
353
+ }),
354
+ /*#__PURE__*/ _jsx(AnnotationJobFilter, {
355
+ jobs: run.jobs,
356
+ selectedJobId: selectedJob?.id,
357
+ onSelect: onSelectAnnotationJob
358
+ })
359
+ ]
360
+ }),
361
+ /*#__PURE__*/ _jsx(RunAnnotationList, {
362
+ query: annotations.query,
363
+ entries: entries,
364
+ workspaceSlug: workspaceSlug,
365
+ projectSlug: projectSlug,
366
+ workflowRunId: run.id,
367
+ runAttempt: run.runAttempt.attempt,
368
+ // A run with no annotations at all offers no filter to clear, whatever the URL says.
369
+ filtered: Boolean((severity || selectedJob) && (annotationSummary?.total ?? 0) > 0),
370
+ filteredJobName: selectedJob?.displayName,
371
+ filteredSeverity: severity,
372
+ onClearFilters: onClearAnnotationFilters,
373
+ selectedAnnotationId: selection?.annotation
374
+ }, `${run.id}:${run.runAttempt.attempt}:${severity ?? 'all'}:${selectedJob?.id ?? 'all'}`)
375
+ ]
376
+ })
377
+ });
378
+ }
379
+ const ALL_ANNOTATION_JOBS = 'all-jobs';
380
+ function AnnotationJobFilter({ jobs, selectedJobId, onSelect }) {
381
+ const sortedJobs = [
382
+ ...jobs
383
+ ].sort((left, right)=>left.position - right.position || left.id.localeCompare(right.id));
384
+ return /*#__PURE__*/ _jsxs(Select, {
385
+ value: selectedJobId ?? ALL_ANNOTATION_JOBS,
386
+ onValueChange: (value)=>onSelect(value === ALL_ANNOTATION_JOBS ? undefined : value),
387
+ children: [
388
+ /*#__PURE__*/ _jsx(SelectTrigger, {
389
+ size: "small",
390
+ "aria-label": "Filter annotations by job",
391
+ className: "w-full min-[480px]:w-240",
392
+ children: /*#__PURE__*/ _jsx(SelectValue, {})
307
393
  }),
308
- /*#__PURE__*/ _jsx(WorkflowSourcePanel, {
309
- id: sourcePanelId,
310
- source: sourceSnapshot,
311
- open: sourcePanelOpen && sourceAvailable,
312
- onClose: closeSourcePanel,
313
- highlightedLineRange: highlightedLineRange,
314
- scrollHighlightedIntoView: true
394
+ /*#__PURE__*/ _jsxs(SelectContent, {
395
+ align: "end",
396
+ children: [
397
+ /*#__PURE__*/ _jsx(SelectItem, {
398
+ value: ALL_ANNOTATION_JOBS,
399
+ children: "All jobs"
400
+ }),
401
+ sortedJobs.map((job)=>/*#__PURE__*/ _jsx(SelectItem, {
402
+ value: job.id,
403
+ children: job.displayName
404
+ }, job.id))
405
+ ]
315
406
  })
316
407
  ]
317
408
  });
318
409
  }
410
+ function RunWorkspaceNavSkeleton() {
411
+ return /*#__PURE__*/ _jsx("aside", {
412
+ "aria-label": "Loading run navigation",
413
+ className: "hidden w-240 shrink-0 border-r border-border-neutral-base bg-background-neutral-background p-12 min-[768px]:block",
414
+ children: /*#__PURE__*/ _jsxs("div", {
415
+ className: "flex flex-col gap-16",
416
+ children: [
417
+ /*#__PURE__*/ _jsx("div", {
418
+ className: "h-32 rounded-4 bg-background-components-subtle"
419
+ }),
420
+ /*#__PURE__*/ _jsxs("div", {
421
+ className: "flex flex-col gap-6",
422
+ children: [
423
+ /*#__PURE__*/ _jsx("div", {
424
+ className: "h-16 w-64 rounded-4 bg-background-components-subtle"
425
+ }),
426
+ /*#__PURE__*/ _jsx("div", {
427
+ className: "h-32 rounded-4 bg-background-components-subtle"
428
+ }),
429
+ /*#__PURE__*/ _jsx("div", {
430
+ className: "h-32 rounded-4 bg-background-components-subtle"
431
+ })
432
+ ]
433
+ })
434
+ ]
435
+ })
436
+ });
437
+ }
438
+ function runWorkspaceSection(tab) {
439
+ return tab === 'annotations' || tab === 'source' ? tab : 'summary';
440
+ }
319
441
  function cancelErrorMessage(error) {
320
442
  if (error instanceof ApiError && error.code === 'run-already-finished') {
321
443
  return 'This workflow run has already finished.';
322
444
  }
323
445
  return 'Could not cancel workflow run.';
324
446
  }
325
- function findAttemptSelection(jobExecution, attemptId) {
326
- for (const step of jobExecution.steps){
327
- const attempt = step.attempts.find((candidate)=>candidate.id === attemptId);
328
- if (attempt) return {
329
- stepId: step.id,
330
- attemptId: attempt.id
331
- };
332
- }
333
- return undefined;
334
- }
335
447
 
336
448
  //# sourceMappingURL=workflow-run-view.js.map