@shipfox/client-workflows 16.0.0 → 17.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 (180) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-type.log +1 -0
  3. package/CHANGELOG.md +20 -0
  4. package/dist/components/identifier/identifier.js +2 -2
  5. package/dist/components/identifier/identifier.js.map +1 -1
  6. package/dist/components/job-detail/agent-config-failure-callout.d.ts.map +1 -1
  7. package/dist/components/job-detail/agent-config-failure-callout.js +1 -1
  8. package/dist/components/job-detail/agent-config-failure-callout.js.map +1 -1
  9. package/dist/components/job-detail/job-context-panel.d.ts +6 -0
  10. package/dist/components/job-detail/job-context-panel.d.ts.map +1 -0
  11. package/dist/components/job-detail/job-context-panel.js +224 -0
  12. package/dist/components/job-detail/job-context-panel.js.map +1 -0
  13. package/dist/components/job-detail/job-detail-header.d.ts +3 -1
  14. package/dist/components/job-detail/job-detail-header.d.ts.map +1 -1
  15. package/dist/components/job-detail/job-detail-header.js +54 -48
  16. package/dist/components/job-detail/job-detail-header.js.map +1 -1
  17. package/dist/components/job-detail/job-detail-view.d.ts +1 -1
  18. package/dist/components/job-detail/job-detail-view.d.ts.map +1 -1
  19. package/dist/components/job-detail/job-detail-view.js +80 -44
  20. package/dist/components/job-detail/job-detail-view.js.map +1 -1
  21. package/dist/components/job-detail/job-empty-states.d.ts.map +1 -1
  22. package/dist/components/job-detail/job-empty-states.js +19 -10
  23. package/dist/components/job-detail/job-empty-states.js.map +1 -1
  24. package/dist/components/job-detail/job-execution-switcher.js +4 -4
  25. package/dist/components/job-detail/job-execution-switcher.js.map +1 -1
  26. package/dist/components/job-detail/json-code.d.ts +14 -0
  27. package/dist/components/job-detail/json-code.d.ts.map +1 -0
  28. package/dist/components/job-detail/json-code.js +93 -0
  29. package/dist/components/job-detail/json-code.js.map +1 -0
  30. package/dist/components/job-detail/step-attempt-log-panel.d.ts.map +1 -1
  31. package/dist/components/job-detail/step-attempt-log-panel.js +9 -6
  32. package/dist/components/job-detail/step-attempt-log-panel.js.map +1 -1
  33. package/dist/components/job-detail/step-troubleshooting.d.ts +18 -0
  34. package/dist/components/job-detail/step-troubleshooting.d.ts.map +1 -0
  35. package/dist/components/job-detail/step-troubleshooting.js +493 -0
  36. package/dist/components/job-detail/step-troubleshooting.js.map +1 -0
  37. package/dist/components/job-graph/job-graph-content.d.ts.map +1 -1
  38. package/dist/components/job-graph/job-graph-content.js +5 -4
  39. package/dist/components/job-graph/job-graph-content.js.map +1 -1
  40. package/dist/components/job-graph/job-node.js +3 -3
  41. package/dist/components/job-graph/job-node.js.map +1 -1
  42. package/dist/components/step-list/index.d.ts +1 -0
  43. package/dist/components/step-list/index.d.ts.map +1 -1
  44. package/dist/components/step-list/index.js.map +1 -1
  45. package/dist/components/step-list/step-list-model.d.ts.map +1 -1
  46. package/dist/components/step-list/step-list-model.js +5 -0
  47. package/dist/components/step-list/step-list-model.js.map +1 -1
  48. package/dist/components/step-list/step-list.d.ts +5 -1
  49. package/dist/components/step-list/step-list.d.ts.map +1 -1
  50. package/dist/components/step-list/step-list.js +117 -82
  51. package/dist/components/step-list/step-list.js.map +1 -1
  52. package/dist/components/workflow-run-duration-label.js +1 -1
  53. package/dist/components/workflow-run-duration-label.js.map +1 -1
  54. package/dist/components/workflow-run-list/job-status-strip.js +6 -6
  55. package/dist/components/workflow-run-list/job-status-strip.js.map +1 -1
  56. package/dist/components/workflow-run-list/workflow-run-filter-menu.js +1 -1
  57. package/dist/components/workflow-run-list/workflow-run-filter-menu.js.map +1 -1
  58. package/dist/components/workflow-run-list/workflow-run-filters.js +4 -4
  59. package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -1
  60. package/dist/components/workflow-run-list/workflow-run-list-states.js +6 -6
  61. package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
  62. package/dist/components/workflow-run-list/workflow-run-list-view.js +1 -1
  63. package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
  64. package/dist/components/workflow-run-list/workflow-run-row.js +7 -7
  65. package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
  66. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +1 -1
  67. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js.map +1 -1
  68. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts +0 -1
  69. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
  70. package/dist/components/workflow-run-summary/workflow-run-summary.js +8 -13
  71. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
  72. package/dist/components/workflow-run-tabs/index.d.ts +1 -1
  73. package/dist/components/workflow-run-tabs/index.d.ts.map +1 -1
  74. package/dist/components/workflow-run-tabs/index.js.map +1 -1
  75. package/dist/components/workflow-run-tabs/run-annotation-count-chip.js +2 -2
  76. package/dist/components/workflow-run-tabs/run-annotation-count-chip.js.map +1 -1
  77. package/dist/components/workflow-run-tabs/run-annotation-list.d.ts +9 -2
  78. package/dist/components/workflow-run-tabs/run-annotation-list.d.ts.map +1 -1
  79. package/dist/components/workflow-run-tabs/run-annotation-list.js +28 -19
  80. package/dist/components/workflow-run-tabs/run-annotation-list.js.map +1 -1
  81. package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts.map +1 -1
  82. package/dist/components/workflow-run-tabs/run-annotation-summary-line.js +5 -5
  83. package/dist/components/workflow-run-tabs/run-annotation-summary-line.js.map +1 -1
  84. package/dist/components/workflow-run-view/run-workspace-nav.js +7 -7
  85. package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
  86. package/dist/components/workflow-run-view/workflow-run-states.js +6 -6
  87. package/dist/components/workflow-run-view/workflow-run-states.js.map +1 -1
  88. package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
  89. package/dist/components/workflow-run-view/workflow-run-view.js +72 -12
  90. package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
  91. package/dist/core/entities/job-execution.d.ts +7 -0
  92. package/dist/core/entities/job-execution.d.ts.map +1 -1
  93. package/dist/core/entities/job-execution.js.map +1 -1
  94. package/dist/core/entities/job.d.ts +9 -0
  95. package/dist/core/entities/job.d.ts.map +1 -1
  96. package/dist/core/entities/job.js.map +1 -1
  97. package/dist/core/entities/step-attempt.d.ts +29 -0
  98. package/dist/core/entities/step-attempt.d.ts.map +1 -1
  99. package/dist/core/entities/step-attempt.js.map +1 -1
  100. package/dist/core/entities/step.d.ts +3 -1
  101. package/dist/core/entities/step.d.ts.map +1 -1
  102. package/dist/core/entities/step.js.map +1 -1
  103. package/dist/core/run-annotation.d.ts +6 -0
  104. package/dist/core/run-annotation.d.ts.map +1 -1
  105. package/dist/core/run-annotation.js.map +1 -1
  106. package/dist/core/workflow-run.d.ts +1 -1
  107. package/dist/core/workflow-run.d.ts.map +1 -1
  108. package/dist/core/workflow-run.js.map +1 -1
  109. package/dist/hooks/api/annotations.d.ts +63 -0
  110. package/dist/hooks/api/annotations.d.ts.map +1 -0
  111. package/dist/hooks/api/annotations.js +145 -0
  112. package/dist/hooks/api/annotations.js.map +1 -0
  113. package/dist/hooks/api/step-attempt-detail.d.ts +14 -0
  114. package/dist/hooks/api/step-attempt-detail.d.ts.map +1 -0
  115. package/dist/hooks/api/step-attempt-detail.js +43 -0
  116. package/dist/hooks/api/step-attempt-detail.js.map +1 -0
  117. package/dist/hooks/api/workflow-run-mapper.d.ts +9 -2
  118. package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
  119. package/dist/hooks/api/workflow-run-mapper.js +47 -0
  120. package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
  121. package/dist/pages/project-workflows-page.js +19 -19
  122. package/dist/pages/project-workflows-page.js.map +1 -1
  123. package/dist/pages/workflow-run-list-page.js +1 -1
  124. package/dist/pages/workflow-run-list-page.js.map +1 -1
  125. package/dist/tsconfig.test.tsbuildinfo +1 -1
  126. package/package.json +10 -10
  127. package/src/components/identifier/identifier.tsx +2 -2
  128. package/src/components/job-detail/agent-config-failure-callout.tsx +5 -1
  129. package/src/components/job-detail/job-context-panel.test.tsx +72 -0
  130. package/src/components/job-detail/job-context-panel.tsx +237 -0
  131. package/src/components/job-detail/job-detail-header.tsx +52 -46
  132. package/src/components/job-detail/job-detail-view.stories.tsx +555 -53
  133. package/src/components/job-detail/job-detail-view.tsx +87 -45
  134. package/src/components/job-detail/job-empty-states.tsx +23 -10
  135. package/src/components/job-detail/job-execution-switcher.tsx +5 -5
  136. package/src/components/job-detail/json-code.tsx +120 -0
  137. package/src/components/job-detail/step-attempt-log-panel.test.tsx +1 -0
  138. package/src/components/job-detail/step-attempt-log-panel.tsx +17 -6
  139. package/src/components/job-detail/step-troubleshooting.stories.tsx +120 -0
  140. package/src/components/job-detail/step-troubleshooting.test.tsx +337 -0
  141. package/src/components/job-detail/step-troubleshooting.tsx +537 -0
  142. package/src/components/job-graph/job-graph-content.tsx +4 -3
  143. package/src/components/job-graph/job-node.tsx +3 -3
  144. package/src/components/step-list/index.ts +1 -0
  145. package/src/components/step-list/step-list-model.ts +5 -0
  146. package/src/components/step-list/step-list.test.tsx +75 -2
  147. package/src/components/step-list/step-list.tsx +73 -20
  148. package/src/components/workflow-run-duration-label.tsx +1 -1
  149. package/src/components/workflow-run-list/job-status-strip.tsx +6 -6
  150. package/src/components/workflow-run-list/workflow-run-filter-menu.tsx +1 -1
  151. package/src/components/workflow-run-list/workflow-run-filters.tsx +4 -4
  152. package/src/components/workflow-run-list/workflow-run-list-states.tsx +6 -6
  153. package/src/components/workflow-run-list/workflow-run-list-view.tsx +1 -1
  154. package/src/components/workflow-run-list/workflow-run-row.tsx +7 -7
  155. package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +1 -1
  156. package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +1 -1
  157. package/src/components/workflow-run-summary/workflow-run-summary.tsx +11 -11
  158. package/src/components/workflow-run-tabs/index.ts +5 -1
  159. package/src/components/workflow-run-tabs/run-annotation-count-chip.tsx +2 -2
  160. package/src/components/workflow-run-tabs/run-annotation-list.tsx +25 -10
  161. package/src/components/workflow-run-tabs/run-annotation-summary-line.tsx +5 -5
  162. package/src/components/workflow-run-view/run-workspace-nav.tsx +7 -7
  163. package/src/components/workflow-run-view/workflow-run-states.tsx +6 -6
  164. package/src/components/workflow-run-view/workflow-run-view.tsx +114 -14
  165. package/src/core/entities/job-execution.ts +7 -0
  166. package/src/core/entities/job.ts +9 -0
  167. package/src/core/entities/step-attempt.ts +33 -0
  168. package/src/core/entities/step.ts +3 -1
  169. package/src/core/run-annotation.ts +6 -0
  170. package/src/core/workflow-run.ts +4 -0
  171. package/src/hooks/api/annotations.test.ts +84 -0
  172. package/src/hooks/api/annotations.ts +240 -0
  173. package/src/hooks/api/step-attempt-detail.ts +67 -0
  174. package/src/hooks/api/workflow-run-mapper.ts +46 -0
  175. package/src/hooks/api/workflow-runs.test.tsx +61 -1
  176. package/src/pages/project-workflows-page.tsx +19 -19
  177. package/src/pages/workflow-job-detail-page.test.tsx +52 -1
  178. package/src/pages/workflow-run-list-page.tsx +1 -1
  179. package/test/fixtures/workflow-run.ts +7 -0
  180. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { TriggerSourceIcon } from '@shipfox/client-triggers';
3
+ import { MetadataSeparator } from '@shipfox/client-ui';
3
4
  import { Badge } from '@shipfox/react-ui/badge';
4
5
  import { Button } from '@shipfox/react-ui/button';
5
6
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@shipfox/react-ui/dropdown-menu';
@@ -40,14 +41,14 @@ export function WorkflowRunSummary({ workspaceSlug, projectSlug, run, cancelling
40
41
  reducedMotionIntervalMs: 10_000,
41
42
  children: /*#__PURE__*/ _jsx("section", {
42
43
  "aria-labelledby": headingId,
43
- className: "bg-background-neutral-background px-16 py-12",
44
+ className: "bg-background-neutral-background px-row py-row",
44
45
  children: /*#__PURE__*/ _jsxs("div", {
45
- className: "grid min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-x-12 gap-y-8 overflow-hidden max-[480px]:grid-cols-1",
46
+ className: "grid min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-x-cluster gap-y-inline overflow-hidden max-[480px]:grid-cols-1",
46
47
  children: [
47
48
  /*#__PURE__*/ _jsx("div", {
48
49
  className: "col-start-1 row-start-1 min-w-0 max-[480px]:col-start-auto max-[480px]:row-start-auto",
49
50
  children: /*#__PURE__*/ _jsxs("div", {
50
- className: "flex min-w-0 items-center gap-8",
51
+ className: "flex min-w-0 items-center gap-inline",
51
52
  children: [
52
53
  /*#__PURE__*/ _jsx(Badge, {
53
54
  variant: status.badge,
@@ -90,7 +91,7 @@ export function WorkflowRunSummary({ workspaceSlug, projectSlug, run, cancelling
90
91
  })
91
92
  }),
92
93
  hasAction ? /*#__PURE__*/ _jsx("div", {
93
- className: "col-start-2 row-start-1 flex min-w-max items-center gap-6 justify-self-end max-[480px]:col-start-auto max-[480px]:row-start-auto max-[480px]:justify-self-start",
94
+ className: "col-start-2 row-start-1 flex min-w-max items-center gap-inline justify-self-end max-[480px]:col-start-auto max-[480px]:row-start-auto max-[480px]:justify-self-start",
94
95
  children: /*#__PURE__*/ _jsx(WorkflowRunActionSlot, {
95
96
  action: action,
96
97
  cancelling: cancelling,
@@ -100,7 +101,7 @@ export function WorkflowRunSummary({ workspaceSlug, projectSlug, run, cancelling
100
101
  })
101
102
  }) : null,
102
103
  /*#__PURE__*/ _jsxs("div", {
103
- className: "col-span-2 row-start-2 flex min-w-0 items-center gap-12 overflow-hidden text-foreground-neutral-subtle max-[480px]:col-span-1 max-[480px]:row-start-auto",
104
+ className: "col-span-2 row-start-2 flex min-w-0 items-center gap-cluster overflow-hidden text-foreground-neutral-subtle max-[480px]:col-span-1 max-[480px]:row-start-auto",
104
105
  children: [
105
106
  run.number !== null ? /*#__PURE__*/ _jsxs(_Fragment, {
106
107
  children: [
@@ -128,7 +129,7 @@ export function WorkflowRunSummary({ workspaceSlug, projectSlug, run, cancelling
128
129
  children: /*#__PURE__*/ _jsxs("button", {
129
130
  type: "button",
130
131
  "aria-label": run.triggerLabel,
131
- className: "inline-flex max-w-full min-w-0 cursor-help items-center gap-4 rounded-6 border-0 bg-transparent p-0 text-left text-foreground-neutral-subtle outline-none focus-visible:shadow-button-neutral-focus",
132
+ className: "inline-flex max-w-full min-w-0 cursor-help items-center gap-tight rounded-6 border-0 bg-transparent p-0 text-left text-foreground-neutral-subtle outline-none focus-visible:shadow-button-neutral-focus",
132
133
  children: [
133
134
  /*#__PURE__*/ _jsx(TriggerSourceIcon, {
134
135
  provider: run.triggerProvider,
@@ -176,7 +177,7 @@ export function WorkflowRunSummary({ workspaceSlug, projectSlug, run, cancelling
176
177
  children: [
177
178
  /*#__PURE__*/ _jsx(MetadataSeparator, {}),
178
179
  /*#__PURE__*/ _jsxs("span", {
179
- className: "flex min-w-0 items-center gap-4",
180
+ className: "flex min-w-0 items-center gap-tight",
180
181
  children: [
181
182
  /*#__PURE__*/ _jsx(Text, {
182
183
  as: "span",
@@ -280,11 +281,5 @@ function hasFailedOrCancelledJobs(run) {
280
281
  function workflowRunHasJobs(run) {
281
282
  return 'jobs' in run && Array.isArray(run.jobs);
282
283
  }
283
- export function MetadataSeparator() {
284
- return /*#__PURE__*/ _jsx("span", {
285
- "aria-hidden": "true",
286
- className: "h-12 w-px shrink-0 bg-border-neutral-base"
287
- });
288
- }
289
284
 
290
285
  //# sourceMappingURL=workflow-run-summary.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/workflow-run-summary/workflow-run-summary.tsx"],"sourcesContent":["import {TriggerSourceIcon} from '@shipfox/client-triggers';\nimport {Badge} from '@shipfox/react-ui/badge';\nimport {Button} from '@shipfox/react-ui/button';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {useIsTextTruncated} from '@shipfox/react-ui/hooks';\nimport {RelativeTime} from '@shipfox/react-ui/relative-time';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Header, Text} from '@shipfox/react-ui/typography';\nimport {useId} from 'react';\nimport {WorkflowRunNumberLabel} from '#components/workflow-run-number-label.js';\nimport {\n isWorkflowRunTerminal,\n type Job,\n WORKFLOW_RUN_STATUSES,\n type WorkflowRunDetail,\n type WorkflowRunRerunMode,\n workflowRunBlockingJob,\n workflowRunDetailDisplay,\n} from '#core/workflow-run.js';\nimport {WorkflowRunDurationLabel} from '../workflow-run-duration-label.js';\nimport {getWorkflowStatusVisual} from '../workflow-status/status-visuals.js';\nimport {WorkflowRunAttemptSwitcher} from './workflow-run-attempt-switcher.js';\n\nconst STATUS_BADGE_LABEL_WIDTH_CH = Math.max(\n ...[...WORKFLOW_RUN_STATUSES, 'queued' as const].map(\n (status) => getWorkflowStatusVisual(status).label.length,\n ),\n);\nconst RERUN_BUTTON_SURFACE_CLASS_NAME =\n 'bg-background-neutral-base hover:bg-background-neutral-hover active:bg-background-neutral-pressed';\n\ntype WorkflowRunAction = 'cancel' | 'rerun-all' | 'rerun-menu' | 'none';\n\nexport interface WorkflowRunSummaryProps {\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n run: WorkflowRunDetail;\n cancelling?: boolean | undefined;\n onCancel?: (() => void) | undefined;\n rerunPending?: boolean | undefined;\n onRerun?: ((mode: WorkflowRunRerunMode) => void) | undefined;\n latestAttempt?: number | undefined;\n}\n\nexport function WorkflowRunSummary({\n workspaceSlug,\n projectSlug,\n run,\n cancelling = false,\n onCancel,\n rerunPending = false,\n onRerun,\n latestAttempt,\n}: WorkflowRunSummaryProps) {\n const headingId = useId();\n const display = workflowRunDetailDisplay(run);\n const status = getWorkflowStatusVisual(display.status);\n const action = workflowRunActionForRun(run);\n const hasAction = canRenderWorkflowRunAction(action, onCancel, onRerun);\n const attemptSwitcher =\n latestAttempt && latestAttempt > 1 && workspaceSlug && projectSlug\n ? {workspaceSlug, projectSlug, latestAttempt}\n : null;\n const displayDuration = display.duration;\n // Named only while the run is waiting and has more than one job: with a single job the rail\n // and the graph already say which one it is.\n const blockingJob =\n display.status === 'queued' && run.jobs.length > 1 ? workflowRunBlockingJob(run.jobs) : null;\n const {ref: headingTextRef, isTruncated: isHeadingTruncated} =\n useIsTextTruncated<HTMLSpanElement>(run.name);\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <section aria-labelledby={headingId} className=\"bg-background-neutral-background px-16 py-12\">\n <div className=\"grid min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-x-12 gap-y-8 overflow-hidden max-[480px]:grid-cols-1\">\n <div className=\"col-start-1 row-start-1 min-w-0 max-[480px]:col-start-auto max-[480px]:row-start-auto\">\n <div className=\"flex min-w-0 items-center gap-8\">\n <Badge variant={status.badge} size=\"xs\">\n <span className=\"text-center\" style={{width: `${STATUS_BADGE_LABEL_WIDTH_CH}ch`}}>\n {status.label}\n </span>\n </Badge>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Header as=\"h1\" id={headingId} variant=\"h3\" className=\"min-w-0 truncate\">\n <span ref={headingTextRef} className=\"block min-w-0 truncate\">\n {run.name}\n </span>\n </Header>\n </TooltipTrigger>\n {isHeadingTruncated ? (\n <TooltipContent>\n <Text as=\"span\" size=\"xs\" className=\"max-w-[360px] break-words\">\n {run.name}\n </Text>\n </TooltipContent>\n ) : null}\n </Tooltip>\n </div>\n </div>\n\n {hasAction ? (\n <div className=\"col-start-2 row-start-1 flex min-w-max items-center gap-6 justify-self-end max-[480px]:col-start-auto max-[480px]:row-start-auto max-[480px]:justify-self-start\">\n <WorkflowRunActionSlot\n action={action}\n cancelling={cancelling}\n onCancel={onCancel}\n rerunPending={rerunPending}\n onRerun={onRerun}\n />\n </div>\n ) : null}\n\n <div className=\"col-span-2 row-start-2 flex min-w-0 items-center gap-12 overflow-hidden text-foreground-neutral-subtle max-[480px]:col-span-1 max-[480px]:row-start-auto\">\n {run.number !== null ? (\n <>\n <WorkflowRunNumberLabel run={run} />\n {attemptSwitcher || run.triggerDisplayLabel ? <MetadataSeparator /> : null}\n </>\n ) : null}\n\n {attemptSwitcher ? (\n <WorkflowRunAttemptSwitcher\n workspaceSlug={attemptSwitcher.workspaceSlug}\n projectSlug={attemptSwitcher.projectSlug}\n run={run}\n latestAttempt={attemptSwitcher.latestAttempt}\n />\n ) : null}\n\n {run.triggerDisplayLabel ? (\n <>\n {attemptSwitcher ? <MetadataSeparator /> : null}\n <span className=\"min-w-0\">\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={run.triggerLabel}\n className=\"inline-flex max-w-full min-w-0 cursor-help items-center gap-4 rounded-6 border-0 bg-transparent p-0 text-left text-foreground-neutral-subtle outline-none focus-visible:shadow-button-neutral-focus\"\n >\n <TriggerSourceIcon\n provider={run.triggerProvider}\n source={run.triggerSource}\n aria-hidden=\"true\"\n className=\"size-12 shrink-0\"\n />\n <Text as=\"span\" size=\"xs\" className=\"min-w-0 truncate\">\n {run.triggerDisplayLabel}\n </Text>\n </button>\n </TooltipTrigger>\n <TooltipContent>\n <Text as=\"span\" size=\"xs\" className=\"block max-w-[360px] break-words\">\n {run.triggerLabel}\n </Text>\n </TooltipContent>\n </Tooltip>\n </span>\n </>\n ) : null}\n\n {run.number !== null || attemptSwitcher || run.triggerDisplayLabel ? (\n <MetadataSeparator />\n ) : null}\n <RelativeTime\n value={run.runAttempt.createdAt}\n className=\"shrink-0 whitespace-nowrap text-xs leading-20 text-foreground-neutral-subtle\"\n />\n\n {displayDuration ? (\n <>\n <MetadataSeparator />\n <WorkflowRunDurationLabel\n duration={displayDuration}\n className=\"text-foreground-neutral-subtle\"\n />\n </>\n ) : null}\n\n {blockingJob ? (\n <>\n <MetadataSeparator />\n <span className=\"flex min-w-0 items-center gap-4\">\n <Text as=\"span\" size=\"xs\" className=\"shrink-0\">\n waiting on\n </Text>\n <Code as=\"span\" variant=\"label\" className=\"min-w-0 truncate\">\n {blockingJob.displayName}\n </Code>\n </span>\n </>\n ) : null}\n </div>\n </div>\n </section>\n </TimeTickerProvider>\n );\n}\n\nfunction WorkflowRunActionSlot({\n action,\n cancelling,\n onCancel,\n rerunPending,\n onRerun,\n}: {\n action: WorkflowRunAction;\n cancelling: boolean;\n onCancel?: (() => void) | undefined;\n rerunPending: boolean;\n onRerun?: ((mode: WorkflowRunRerunMode) => void) | undefined;\n}) {\n if (action === 'none') return null;\n\n if (action === 'cancel') {\n if (!onCancel) return null;\n\n return (\n <Button\n type=\"button\"\n variant=\"danger\"\n size=\"xs\"\n isLoading={cancelling}\n disabled={cancelling}\n onClick={onCancel}\n >\n Cancel workflow\n </Button>\n );\n }\n\n if (action === 'rerun-all') {\n if (!onRerun) return null;\n\n return (\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"xs\"\n className={RERUN_BUTTON_SURFACE_CLASS_NAME}\n isLoading={rerunPending}\n disabled={rerunPending}\n onClick={() => onRerun('all')}\n >\n Re-run workflow\n </Button>\n );\n }\n\n if (!onRerun) return null;\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"xs\"\n className={RERUN_BUTTON_SURFACE_CLASS_NAME}\n iconRight=\"arrowDownSLine\"\n isLoading={rerunPending}\n disabled={rerunPending}\n >\n Re-run jobs\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem disabled={rerunPending} onSelect={() => onRerun('all')}>\n Re-run all jobs\n </DropdownMenuItem>\n <DropdownMenuItem disabled={rerunPending} onSelect={() => onRerun('failed')}>\n Re-run failed jobs\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\nfunction workflowRunActionForRun(run: WorkflowRunDetail): WorkflowRunAction {\n if (run.runAttempt.attempt !== run.currentAttempt) return 'none';\n if (!isWorkflowRunTerminal(run.runAttempt.status)) return 'cancel';\n if (run.runAttempt.status === 'succeeded' || !hasFailedOrCancelledJobs(run)) return 'rerun-all';\n return 'rerun-menu';\n}\n\nfunction canRenderWorkflowRunAction(\n action: WorkflowRunAction,\n onCancel: (() => void) | undefined,\n onRerun: ((mode: WorkflowRunRerunMode) => void) | undefined,\n): boolean {\n if (action === 'cancel') return onCancel !== undefined;\n if (action === 'rerun-all' || action === 'rerun-menu') return onRerun !== undefined;\n return false;\n}\n\nfunction hasFailedOrCancelledJobs(run: WorkflowRunDetail): boolean {\n if (!workflowRunHasJobs(run)) return false;\n\n return run.jobs.some((job) => job.status === 'failed' || job.status === 'cancelled');\n}\n\nfunction workflowRunHasJobs(run: WorkflowRunDetail): run is WorkflowRunDetail & {jobs: Job[]} {\n return 'jobs' in run && Array.isArray(run.jobs);\n}\n\nexport function MetadataSeparator() {\n return <span aria-hidden=\"true\" className=\"h-12 w-px shrink-0 bg-border-neutral-base\" />;\n}\n"],"names":["TriggerSourceIcon","Badge","Button","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuTrigger","useIsTextTruncated","RelativeTime","TimeTickerProvider","Tooltip","TooltipContent","TooltipTrigger","Code","Header","Text","useId","WorkflowRunNumberLabel","isWorkflowRunTerminal","WORKFLOW_RUN_STATUSES","workflowRunBlockingJob","workflowRunDetailDisplay","WorkflowRunDurationLabel","getWorkflowStatusVisual","WorkflowRunAttemptSwitcher","STATUS_BADGE_LABEL_WIDTH_CH","Math","max","map","status","label","length","RERUN_BUTTON_SURFACE_CLASS_NAME","WorkflowRunSummary","workspaceSlug","projectSlug","run","cancelling","onCancel","rerunPending","onRerun","latestAttempt","headingId","display","action","workflowRunActionForRun","hasAction","canRenderWorkflowRunAction","attemptSwitcher","displayDuration","duration","blockingJob","jobs","ref","headingTextRef","isTruncated","isHeadingTruncated","name","intervalMs","reducedMotionIntervalMs","section","aria-labelledby","className","div","variant","badge","size","span","style","width","asChild","as","id","WorkflowRunActionSlot","number","triggerDisplayLabel","MetadataSeparator","button","type","aria-label","triggerLabel","provider","triggerProvider","source","triggerSource","aria-hidden","value","runAttempt","createdAt","displayName","isLoading","disabled","onClick","iconRight","align","onSelect","attempt","currentAttempt","hasFailedOrCancelledJobs","undefined","workflowRunHasJobs","some","job","Array","isArray"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/workflow-run-summary/workflow-run-summary.tsx"],"sourcesContent":["import {TriggerSourceIcon} from '@shipfox/client-triggers';\nimport {MetadataSeparator} from '@shipfox/client-ui';\nimport {Badge} from '@shipfox/react-ui/badge';\nimport {Button} from '@shipfox/react-ui/button';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {useIsTextTruncated} from '@shipfox/react-ui/hooks';\nimport {RelativeTime} from '@shipfox/react-ui/relative-time';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Header, Text} from '@shipfox/react-ui/typography';\nimport {useId} from 'react';\nimport {WorkflowRunNumberLabel} from '#components/workflow-run-number-label.js';\nimport {\n isWorkflowRunTerminal,\n type Job,\n WORKFLOW_RUN_STATUSES,\n type WorkflowRunDetail,\n type WorkflowRunRerunMode,\n workflowRunBlockingJob,\n workflowRunDetailDisplay,\n} from '#core/workflow-run.js';\nimport {WorkflowRunDurationLabel} from '../workflow-run-duration-label.js';\nimport {getWorkflowStatusVisual} from '../workflow-status/status-visuals.js';\nimport {WorkflowRunAttemptSwitcher} from './workflow-run-attempt-switcher.js';\n\nconst STATUS_BADGE_LABEL_WIDTH_CH = Math.max(\n ...[...WORKFLOW_RUN_STATUSES, 'queued' as const].map(\n (status) => getWorkflowStatusVisual(status).label.length,\n ),\n);\nconst RERUN_BUTTON_SURFACE_CLASS_NAME =\n 'bg-background-neutral-base hover:bg-background-neutral-hover active:bg-background-neutral-pressed';\n\ntype WorkflowRunAction = 'cancel' | 'rerun-all' | 'rerun-menu' | 'none';\n\nexport interface WorkflowRunSummaryProps {\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n run: WorkflowRunDetail;\n cancelling?: boolean | undefined;\n onCancel?: (() => void) | undefined;\n rerunPending?: boolean | undefined;\n onRerun?: ((mode: WorkflowRunRerunMode) => void) | undefined;\n latestAttempt?: number | undefined;\n}\n\nexport function WorkflowRunSummary({\n workspaceSlug,\n projectSlug,\n run,\n cancelling = false,\n onCancel,\n rerunPending = false,\n onRerun,\n latestAttempt,\n}: WorkflowRunSummaryProps) {\n const headingId = useId();\n const display = workflowRunDetailDisplay(run);\n const status = getWorkflowStatusVisual(display.status);\n const action = workflowRunActionForRun(run);\n const hasAction = canRenderWorkflowRunAction(action, onCancel, onRerun);\n const attemptSwitcher =\n latestAttempt && latestAttempt > 1 && workspaceSlug && projectSlug\n ? {workspaceSlug, projectSlug, latestAttempt}\n : null;\n const displayDuration = display.duration;\n // Named only while the run is waiting and has more than one job: with a single job the rail\n // and the graph already say which one it is.\n const blockingJob =\n display.status === 'queued' && run.jobs.length > 1 ? workflowRunBlockingJob(run.jobs) : null;\n const {ref: headingTextRef, isTruncated: isHeadingTruncated} =\n useIsTextTruncated<HTMLSpanElement>(run.name);\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <section\n aria-labelledby={headingId}\n className=\"bg-background-neutral-background px-row py-row\"\n >\n <div className=\"grid min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-x-cluster gap-y-inline overflow-hidden max-[480px]:grid-cols-1\">\n <div className=\"col-start-1 row-start-1 min-w-0 max-[480px]:col-start-auto max-[480px]:row-start-auto\">\n <div className=\"flex min-w-0 items-center gap-inline\">\n <Badge variant={status.badge} size=\"xs\">\n <span className=\"text-center\" style={{width: `${STATUS_BADGE_LABEL_WIDTH_CH}ch`}}>\n {status.label}\n </span>\n </Badge>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Header as=\"h1\" id={headingId} variant=\"h3\" className=\"min-w-0 truncate\">\n <span ref={headingTextRef} className=\"block min-w-0 truncate\">\n {run.name}\n </span>\n </Header>\n </TooltipTrigger>\n {isHeadingTruncated ? (\n <TooltipContent>\n <Text as=\"span\" size=\"xs\" className=\"max-w-[360px] break-words\">\n {run.name}\n </Text>\n </TooltipContent>\n ) : null}\n </Tooltip>\n </div>\n </div>\n\n {hasAction ? (\n <div className=\"col-start-2 row-start-1 flex min-w-max items-center gap-inline justify-self-end max-[480px]:col-start-auto max-[480px]:row-start-auto max-[480px]:justify-self-start\">\n <WorkflowRunActionSlot\n action={action}\n cancelling={cancelling}\n onCancel={onCancel}\n rerunPending={rerunPending}\n onRerun={onRerun}\n />\n </div>\n ) : null}\n\n <div className=\"col-span-2 row-start-2 flex min-w-0 items-center gap-cluster overflow-hidden text-foreground-neutral-subtle max-[480px]:col-span-1 max-[480px]:row-start-auto\">\n {run.number !== null ? (\n <>\n <WorkflowRunNumberLabel run={run} />\n {attemptSwitcher || run.triggerDisplayLabel ? <MetadataSeparator /> : null}\n </>\n ) : null}\n\n {attemptSwitcher ? (\n <WorkflowRunAttemptSwitcher\n workspaceSlug={attemptSwitcher.workspaceSlug}\n projectSlug={attemptSwitcher.projectSlug}\n run={run}\n latestAttempt={attemptSwitcher.latestAttempt}\n />\n ) : null}\n\n {run.triggerDisplayLabel ? (\n <>\n {attemptSwitcher ? <MetadataSeparator /> : null}\n <span className=\"min-w-0\">\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={run.triggerLabel}\n className=\"inline-flex max-w-full min-w-0 cursor-help items-center gap-tight rounded-6 border-0 bg-transparent p-0 text-left text-foreground-neutral-subtle outline-none focus-visible:shadow-button-neutral-focus\"\n >\n <TriggerSourceIcon\n provider={run.triggerProvider}\n source={run.triggerSource}\n aria-hidden=\"true\"\n className=\"size-12 shrink-0\"\n />\n <Text as=\"span\" size=\"xs\" className=\"min-w-0 truncate\">\n {run.triggerDisplayLabel}\n </Text>\n </button>\n </TooltipTrigger>\n <TooltipContent>\n <Text as=\"span\" size=\"xs\" className=\"block max-w-[360px] break-words\">\n {run.triggerLabel}\n </Text>\n </TooltipContent>\n </Tooltip>\n </span>\n </>\n ) : null}\n\n {run.number !== null || attemptSwitcher || run.triggerDisplayLabel ? (\n <MetadataSeparator />\n ) : null}\n <RelativeTime\n value={run.runAttempt.createdAt}\n className=\"shrink-0 whitespace-nowrap text-xs leading-20 text-foreground-neutral-subtle\"\n />\n\n {displayDuration ? (\n <>\n <MetadataSeparator />\n <WorkflowRunDurationLabel\n duration={displayDuration}\n className=\"text-foreground-neutral-subtle\"\n />\n </>\n ) : null}\n\n {blockingJob ? (\n <>\n <MetadataSeparator />\n <span className=\"flex min-w-0 items-center gap-tight\">\n <Text as=\"span\" size=\"xs\" className=\"shrink-0\">\n waiting on\n </Text>\n <Code as=\"span\" variant=\"label\" className=\"min-w-0 truncate\">\n {blockingJob.displayName}\n </Code>\n </span>\n </>\n ) : null}\n </div>\n </div>\n </section>\n </TimeTickerProvider>\n );\n}\n\nfunction WorkflowRunActionSlot({\n action,\n cancelling,\n onCancel,\n rerunPending,\n onRerun,\n}: {\n action: WorkflowRunAction;\n cancelling: boolean;\n onCancel?: (() => void) | undefined;\n rerunPending: boolean;\n onRerun?: ((mode: WorkflowRunRerunMode) => void) | undefined;\n}) {\n if (action === 'none') return null;\n\n if (action === 'cancel') {\n if (!onCancel) return null;\n\n return (\n <Button\n type=\"button\"\n variant=\"danger\"\n size=\"xs\"\n isLoading={cancelling}\n disabled={cancelling}\n onClick={onCancel}\n >\n Cancel workflow\n </Button>\n );\n }\n\n if (action === 'rerun-all') {\n if (!onRerun) return null;\n\n return (\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"xs\"\n className={RERUN_BUTTON_SURFACE_CLASS_NAME}\n isLoading={rerunPending}\n disabled={rerunPending}\n onClick={() => onRerun('all')}\n >\n Re-run workflow\n </Button>\n );\n }\n\n if (!onRerun) return null;\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"xs\"\n className={RERUN_BUTTON_SURFACE_CLASS_NAME}\n iconRight=\"arrowDownSLine\"\n isLoading={rerunPending}\n disabled={rerunPending}\n >\n Re-run jobs\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem disabled={rerunPending} onSelect={() => onRerun('all')}>\n Re-run all jobs\n </DropdownMenuItem>\n <DropdownMenuItem disabled={rerunPending} onSelect={() => onRerun('failed')}>\n Re-run failed jobs\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\nfunction workflowRunActionForRun(run: WorkflowRunDetail): WorkflowRunAction {\n if (run.runAttempt.attempt !== run.currentAttempt) return 'none';\n if (!isWorkflowRunTerminal(run.runAttempt.status)) return 'cancel';\n if (run.runAttempt.status === 'succeeded' || !hasFailedOrCancelledJobs(run)) return 'rerun-all';\n return 'rerun-menu';\n}\n\nfunction canRenderWorkflowRunAction(\n action: WorkflowRunAction,\n onCancel: (() => void) | undefined,\n onRerun: ((mode: WorkflowRunRerunMode) => void) | undefined,\n): boolean {\n if (action === 'cancel') return onCancel !== undefined;\n if (action === 'rerun-all' || action === 'rerun-menu') return onRerun !== undefined;\n return false;\n}\n\nfunction hasFailedOrCancelledJobs(run: WorkflowRunDetail): boolean {\n if (!workflowRunHasJobs(run)) return false;\n\n return run.jobs.some((job) => job.status === 'failed' || job.status === 'cancelled');\n}\n\nfunction workflowRunHasJobs(run: WorkflowRunDetail): run is WorkflowRunDetail & {jobs: Job[]} {\n return 'jobs' in run && Array.isArray(run.jobs);\n}\n"],"names":["TriggerSourceIcon","MetadataSeparator","Badge","Button","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuTrigger","useIsTextTruncated","RelativeTime","TimeTickerProvider","Tooltip","TooltipContent","TooltipTrigger","Code","Header","Text","useId","WorkflowRunNumberLabel","isWorkflowRunTerminal","WORKFLOW_RUN_STATUSES","workflowRunBlockingJob","workflowRunDetailDisplay","WorkflowRunDurationLabel","getWorkflowStatusVisual","WorkflowRunAttemptSwitcher","STATUS_BADGE_LABEL_WIDTH_CH","Math","max","map","status","label","length","RERUN_BUTTON_SURFACE_CLASS_NAME","WorkflowRunSummary","workspaceSlug","projectSlug","run","cancelling","onCancel","rerunPending","onRerun","latestAttempt","headingId","display","action","workflowRunActionForRun","hasAction","canRenderWorkflowRunAction","attemptSwitcher","displayDuration","duration","blockingJob","jobs","ref","headingTextRef","isTruncated","isHeadingTruncated","name","intervalMs","reducedMotionIntervalMs","section","aria-labelledby","className","div","variant","badge","size","span","style","width","asChild","as","id","WorkflowRunActionSlot","number","triggerDisplayLabel","button","type","aria-label","triggerLabel","provider","triggerProvider","source","triggerSource","aria-hidden","value","runAttempt","createdAt","displayName","isLoading","disabled","onClick","iconRight","align","onSelect","attempt","currentAttempt","hasFailedOrCancelledJobs","undefined","workflowRunHasJobs","some","job","Array","isArray"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  export { RunAnnotationCountChip } from './run-annotation-count-chip.js';
2
2
  export { annotationElementId, RunAnnotationItem } from './run-annotation-item.js';
3
- export { RunAnnotationList, type RunAnnotationListQuery } from './run-annotation-list.js';
3
+ export { type DerivedRunAnnotation, RunAnnotationList, type RunAnnotationListQuery, } from './run-annotation-list.js';
4
4
  export { RunAnnotationSummaryLine } from './run-annotation-summary-line.js';
5
5
  export { RunAnnotationsEmpty } from './run-annotations-empty.js';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-tabs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,sBAAsB,EAAC,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAC,mBAAmB,EAAE,iBAAiB,EAAC,MAAM,0BAA0B,CAAC;AAChF,OAAO,EAAC,iBAAiB,EAAE,KAAK,sBAAsB,EAAC,MAAM,0BAA0B,CAAC;AACxF,OAAO,EAAC,wBAAwB,EAAC,MAAM,kCAAkC,CAAC;AAC1E,OAAO,EAAC,mBAAmB,EAAC,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-tabs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,sBAAsB,EAAC,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAC,mBAAmB,EAAE,iBAAiB,EAAC,MAAM,0BAA0B,CAAC;AAChF,OAAO,EACL,KAAK,oBAAoB,EACzB,iBAAiB,EACjB,KAAK,sBAAsB,GAC5B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAC,wBAAwB,EAAC,MAAM,kCAAkC,CAAC;AAC1E,OAAO,EAAC,mBAAmB,EAAC,MAAM,4BAA4B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/workflow-run-tabs/index.ts"],"sourcesContent":["export {RunAnnotationCountChip} from './run-annotation-count-chip.js';\nexport {annotationElementId, RunAnnotationItem} from './run-annotation-item.js';\nexport {RunAnnotationList, type RunAnnotationListQuery} from './run-annotation-list.js';\nexport {RunAnnotationSummaryLine} from './run-annotation-summary-line.js';\nexport {RunAnnotationsEmpty} from './run-annotations-empty.js';\n"],"names":["RunAnnotationCountChip","annotationElementId","RunAnnotationItem","RunAnnotationList","RunAnnotationSummaryLine","RunAnnotationsEmpty"],"mappings":"AAAA,SAAQA,sBAAsB,QAAO,iCAAiC;AACtE,SAAQC,mBAAmB,EAAEC,iBAAiB,QAAO,2BAA2B;AAChF,SAAQC,iBAAiB,QAAoC,2BAA2B;AACxF,SAAQC,wBAAwB,QAAO,mCAAmC;AAC1E,SAAQC,mBAAmB,QAAO,6BAA6B"}
1
+ {"version":3,"sources":["../../../src/components/workflow-run-tabs/index.ts"],"sourcesContent":["export {RunAnnotationCountChip} from './run-annotation-count-chip.js';\nexport {annotationElementId, RunAnnotationItem} from './run-annotation-item.js';\nexport {\n type DerivedRunAnnotation,\n RunAnnotationList,\n type RunAnnotationListQuery,\n} from './run-annotation-list.js';\nexport {RunAnnotationSummaryLine} from './run-annotation-summary-line.js';\nexport {RunAnnotationsEmpty} from './run-annotations-empty.js';\n"],"names":["RunAnnotationCountChip","annotationElementId","RunAnnotationItem","RunAnnotationList","RunAnnotationSummaryLine","RunAnnotationsEmpty"],"mappings":"AAAA,SAAQA,sBAAsB,QAAO,iCAAiC;AACtE,SAAQC,mBAAmB,EAAEC,iBAAiB,QAAO,2BAA2B;AAChF,SAEEC,iBAAiB,QAEZ,2BAA2B;AAClC,SAAQC,wBAAwB,QAAO,mCAAmC;AAC1E,SAAQC,mBAAmB,QAAO,6BAA6B"}
@@ -30,9 +30,9 @@ import { SEVERITY_CHIP_TONE, SEVERITY_ICON } from './severity-visuals.js';
30
30
  },
31
31
  search: workflowRunSearchParams(search, search),
32
32
  "aria-label": accessibleLabel,
33
- className: cn('inline-flex h-20 shrink-0 items-center gap-4 rounded-4 border px-6 font-code text-xs leading-16 tabular-nums outline-none transition-colors hover:bg-background-neutral-hover focus-visible:shadow-border-interactive-with-active', // The chip stays 20px optically so it sits level with the duration metadata beside it;
33
+ className: cn('inline-flex h-20 shrink-0 items-center gap-tight rounded-4 border px-tight font-code text-xs leading-16 tabular-nums outline-none transition-colors hover:bg-background-neutral-hover focus-visible:shadow-border-interactive-with-active', // The chip stays 20px optically so it sits level with the duration metadata beside it;
34
34
  // the hit area grows to the 44px minimum only where the pointer is coarse.
35
- '[@media(pointer:coarse)]:h-44 [@media(pointer:coarse)]:px-10', severity ? SEVERITY_CHIP_TONE[severity] : 'border-border-neutral-base'),
35
+ '[@media(pointer:coarse)]:h-44 [@media(pointer:coarse)]:px-[10px]', severity ? SEVERITY_CHIP_TONE[severity] : 'border-border-neutral-base'),
36
36
  children: [
37
37
  /*#__PURE__*/ _jsx(Icon, {
38
38
  name: severity ? SEVERITY_ICON[severity] : 'fileTextLine',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/workflow-run-tabs/run-annotation-count-chip.tsx"],"sourcesContent":["import {Icon} from '@shipfox/react-ui/icon';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport {highestRunAnnotationSeverity, type RunAnnotationSummary} from '#core/run-annotation.js';\nimport {type WorkflowRunsSearch, workflowRunSearchParams} from '#routes/inputs.js';\nimport {SEVERITY_CHIP_TONE, SEVERITY_ICON} from './severity-visuals.js';\n\nexport interface RunAnnotationCountChipProps {\n summary: RunAnnotationSummary | undefined;\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n runAttempt?: number | undefined;\n /** Scopes the destination list to one job. */\n jobId?: string | undefined;\n}\n\n/**\n * A bounded reference into the run's Annotations section: a count, its highest severity, and one\n * link. Never a body, so an annotation is rendered exactly once per page.\n */\nexport function RunAnnotationCountChip({\n summary,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n runAttempt,\n jobId,\n}: RunAnnotationCountChipProps) {\n if (!summary || summary.total === 0) return null;\n\n const severity = highestRunAnnotationSeverity(summary);\n const count = `${summary.total}${summary.truncated ? '+' : ''}`;\n const label = `${count} ${summary.total === 1 && !summary.truncated ? 'annotation' : 'annotations'}`;\n const accessibleLabel = severity\n ? `View ${label}, highest severity ${severity}`\n : `View ${label}`;\n const search: WorkflowRunsSearch = {tab: 'annotations', ...(runAttempt ? {runAttempt} : {})};\n if (jobId) search.jobId = jobId;\n\n return (\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={workflowRunSearchParams(search, search) as never}\n aria-label={accessibleLabel}\n className={cn(\n 'inline-flex h-20 shrink-0 items-center gap-4 rounded-4 border px-6 font-code text-xs leading-16 tabular-nums outline-none transition-colors hover:bg-background-neutral-hover focus-visible:shadow-border-interactive-with-active',\n // The chip stays 20px optically so it sits level with the duration metadata beside it;\n // the hit area grows to the 44px minimum only where the pointer is coarse.\n '[@media(pointer:coarse)]:h-44 [@media(pointer:coarse)]:px-10',\n severity ? SEVERITY_CHIP_TONE[severity] : 'border-border-neutral-base',\n )}\n >\n <Icon\n name={severity ? SEVERITY_ICON[severity] : 'fileTextLine'}\n size={12}\n aria-hidden=\"true\"\n />\n <span className=\"text-foreground-neutral-base\">{count}</span>\n </Link>\n );\n}\n"],"names":["Icon","cn","Link","highestRunAnnotationSeverity","workflowRunSearchParams","SEVERITY_CHIP_TONE","SEVERITY_ICON","RunAnnotationCountChip","summary","workspaceSlug","projectSlug","workflowRunId","runAttempt","jobId","total","severity","count","truncated","label","accessibleLabel","search","tab","to","params","aria-label","className","name","size","aria-hidden","span"],"mappings":";AAAA,SAAQA,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,EAAE,QAAO,0BAA0B;AAC3C,SAAQC,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,4BAA4B,QAAkC,0BAA0B;AAChG,SAAiCC,uBAAuB,QAAO,oBAAoB;AACnF,SAAQC,kBAAkB,EAAEC,aAAa,QAAO,wBAAwB;AAYxE;;;CAGC,GACD,OAAO,SAASC,uBAAuB,EACrCC,OAAO,EACPC,aAAa,EACbC,WAAW,EACXC,aAAa,EACbC,UAAU,EACVC,KAAK,EACuB;IAC5B,IAAI,CAACL,WAAWA,QAAQM,KAAK,KAAK,GAAG,OAAO;IAE5C,MAAMC,WAAWZ,6BAA6BK;IAC9C,MAAMQ,QAAQ,GAAGR,QAAQM,KAAK,GAAGN,QAAQS,SAAS,GAAG,MAAM,IAAI;IAC/D,MAAMC,QAAQ,GAAGF,MAAM,CAAC,EAAER,QAAQM,KAAK,KAAK,KAAK,CAACN,QAAQS,SAAS,GAAG,eAAe,eAAe;IACpG,MAAME,kBAAkBJ,WACpB,CAAC,KAAK,EAAEG,MAAM,mBAAmB,EAAEH,UAAU,GAC7C,CAAC,KAAK,EAAEG,OAAO;IACnB,MAAME,SAA6B;QAACC,KAAK;QAAe,GAAIT,aAAa;YAACA;QAAU,IAAI,CAAC,CAAC;IAAC;IAC3F,IAAIC,OAAOO,OAAOP,KAAK,GAAGA;IAE1B,qBACE,MAACX;QACCoB,IAAG;QACHC,QAAQ;YAACd;YAAeC;YAAaC;QAAa;QAClDS,QAAQhB,wBAAwBgB,QAAQA;QACxCI,cAAYL;QACZM,WAAWxB,GACT,qOACA,uFAAuF;QACvF,2EAA2E;QAC3E,gEACAc,WAAWV,kBAAkB,CAACU,SAAS,GAAG;;0BAG5C,KAACf;gBACC0B,MAAMX,WAAWT,aAAa,CAACS,SAAS,GAAG;gBAC3CY,MAAM;gBACNC,eAAY;;0BAEd,KAACC;gBAAKJ,WAAU;0BAAgCT;;;;AAGtD"}
1
+ {"version":3,"sources":["../../../src/components/workflow-run-tabs/run-annotation-count-chip.tsx"],"sourcesContent":["import {Icon} from '@shipfox/react-ui/icon';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport {highestRunAnnotationSeverity, type RunAnnotationSummary} from '#core/run-annotation.js';\nimport {type WorkflowRunsSearch, workflowRunSearchParams} from '#routes/inputs.js';\nimport {SEVERITY_CHIP_TONE, SEVERITY_ICON} from './severity-visuals.js';\n\nexport interface RunAnnotationCountChipProps {\n summary: RunAnnotationSummary | undefined;\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n runAttempt?: number | undefined;\n /** Scopes the destination list to one job. */\n jobId?: string | undefined;\n}\n\n/**\n * A bounded reference into the run's Annotations section: a count, its highest severity, and one\n * link. Never a body, so an annotation is rendered exactly once per page.\n */\nexport function RunAnnotationCountChip({\n summary,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n runAttempt,\n jobId,\n}: RunAnnotationCountChipProps) {\n if (!summary || summary.total === 0) return null;\n\n const severity = highestRunAnnotationSeverity(summary);\n const count = `${summary.total}${summary.truncated ? '+' : ''}`;\n const label = `${count} ${summary.total === 1 && !summary.truncated ? 'annotation' : 'annotations'}`;\n const accessibleLabel = severity\n ? `View ${label}, highest severity ${severity}`\n : `View ${label}`;\n const search: WorkflowRunsSearch = {tab: 'annotations', ...(runAttempt ? {runAttempt} : {})};\n if (jobId) search.jobId = jobId;\n\n return (\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={workflowRunSearchParams(search, search) as never}\n aria-label={accessibleLabel}\n className={cn(\n 'inline-flex h-20 shrink-0 items-center gap-tight rounded-4 border px-tight font-code text-xs leading-16 tabular-nums outline-none transition-colors hover:bg-background-neutral-hover focus-visible:shadow-border-interactive-with-active',\n // The chip stays 20px optically so it sits level with the duration metadata beside it;\n // the hit area grows to the 44px minimum only where the pointer is coarse.\n '[@media(pointer:coarse)]:h-44 [@media(pointer:coarse)]:px-[10px]',\n severity ? SEVERITY_CHIP_TONE[severity] : 'border-border-neutral-base',\n )}\n >\n <Icon\n name={severity ? SEVERITY_ICON[severity] : 'fileTextLine'}\n size={12}\n aria-hidden=\"true\"\n />\n <span className=\"text-foreground-neutral-base\">{count}</span>\n </Link>\n );\n}\n"],"names":["Icon","cn","Link","highestRunAnnotationSeverity","workflowRunSearchParams","SEVERITY_CHIP_TONE","SEVERITY_ICON","RunAnnotationCountChip","summary","workspaceSlug","projectSlug","workflowRunId","runAttempt","jobId","total","severity","count","truncated","label","accessibleLabel","search","tab","to","params","aria-label","className","name","size","aria-hidden","span"],"mappings":";AAAA,SAAQA,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,EAAE,QAAO,0BAA0B;AAC3C,SAAQC,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,4BAA4B,QAAkC,0BAA0B;AAChG,SAAiCC,uBAAuB,QAAO,oBAAoB;AACnF,SAAQC,kBAAkB,EAAEC,aAAa,QAAO,wBAAwB;AAYxE;;;CAGC,GACD,OAAO,SAASC,uBAAuB,EACrCC,OAAO,EACPC,aAAa,EACbC,WAAW,EACXC,aAAa,EACbC,UAAU,EACVC,KAAK,EACuB;IAC5B,IAAI,CAACL,WAAWA,QAAQM,KAAK,KAAK,GAAG,OAAO;IAE5C,MAAMC,WAAWZ,6BAA6BK;IAC9C,MAAMQ,QAAQ,GAAGR,QAAQM,KAAK,GAAGN,QAAQS,SAAS,GAAG,MAAM,IAAI;IAC/D,MAAMC,QAAQ,GAAGF,MAAM,CAAC,EAAER,QAAQM,KAAK,KAAK,KAAK,CAACN,QAAQS,SAAS,GAAG,eAAe,eAAe;IACpG,MAAME,kBAAkBJ,WACpB,CAAC,KAAK,EAAEG,MAAM,mBAAmB,EAAEH,UAAU,GAC7C,CAAC,KAAK,EAAEG,OAAO;IACnB,MAAME,SAA6B;QAACC,KAAK;QAAe,GAAIT,aAAa;YAACA;QAAU,IAAI,CAAC,CAAC;IAAC;IAC3F,IAAIC,OAAOO,OAAOP,KAAK,GAAGA;IAE1B,qBACE,MAACX;QACCoB,IAAG;QACHC,QAAQ;YAACd;YAAeC;YAAaC;QAAa;QAClDS,QAAQhB,wBAAwBgB,QAAQA;QACxCI,cAAYL;QACZM,WAAWxB,GACT,6OACA,uFAAuF;QACvF,2EAA2E;QAC3E,oEACAc,WAAWV,kBAAkB,CAACU,SAAS,GAAG;;0BAG5C,KAACf;gBACC0B,MAAMX,WAAWT,aAAa,CAACS,SAAS,GAAG;gBAC3CY,MAAM;gBACNC,eAAY;;0BAEd,KAACC;gBAAKJ,WAAU;0BAAgCT;;;;AAGtD"}
@@ -1,5 +1,5 @@
1
1
  import { type QueryLoadErrorQuery } from '@shipfox/client-ui';
2
- import type { RunAnnotationEntry } from '#core/run-annotation.js';
2
+ import type { RunAnnotationEntry, RunAnnotationStyle } from '#core/run-annotation.js';
3
3
  export interface RunAnnotationListQuery extends QueryLoadErrorQuery {
4
4
  isPending: boolean;
5
5
  hasNextPage: boolean;
@@ -9,6 +9,8 @@ export interface RunAnnotationListQuery extends QueryLoadErrorQuery {
9
9
  export interface RunAnnotationListProps {
10
10
  query: RunAnnotationListQuery;
11
11
  entries: RunAnnotationEntry[] | undefined;
12
+ /** Synthetic terminal-job diagnostics for jobs that never created an execution record. */
13
+ derivedAnnotations?: readonly DerivedRunAnnotation[] | undefined;
12
14
  workflowRunId: string;
13
15
  workspaceSlug?: string | undefined;
14
16
  projectSlug?: string | undefined;
@@ -22,11 +24,16 @@ export interface RunAnnotationListProps {
22
24
  /** Annotation id from `?annotation=`, which the matching card scrolls to and focuses. */
23
25
  selectedAnnotationId?: string | undefined;
24
26
  }
27
+ export interface DerivedRunAnnotation {
28
+ id: string;
29
+ style: RunAnnotationStyle;
30
+ body: string;
31
+ }
25
32
  /**
26
33
  * The annotations list.
27
34
  *
28
35
  * Loading, empty, error, and stale are four distinct renderings on purpose: coalescing a failed
29
36
  * fetch into an empty result tells a reader their run is clean when the truth is unknown.
30
37
  */
31
- export declare function RunAnnotationList({ query, entries, workflowRunId, workspaceSlug, projectSlug, runAttempt, filteredJobName, filteredSeverity, filtered, onClearFilters, selectedAnnotationId, }: RunAnnotationListProps): import("react").JSX.Element;
38
+ export declare function RunAnnotationList({ query, entries, derivedAnnotations, workflowRunId, workspaceSlug, projectSlug, runAttempt, filteredJobName, filteredSeverity, filtered, onClearFilters, selectedAnnotationId, }: RunAnnotationListProps): import("react").JSX.Element;
32
39
  //# sourceMappingURL=run-annotation-list.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"run-annotation-list.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-tabs/run-annotation-list.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,mBAAmB,EAAC,MAAM,oBAAoB,CAAC;AAO5E,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,yBAAyB,CAAC;AAIhE,MAAM,WAAW,sBAAuB,SAAQ,mBAAmB;IACjE,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,aAAa,EAAE,MAAM,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,sBAAsB,CAAC;IAC9B,OAAO,EAAE,kBAAkB,EAAE,GAAG,SAAS,CAAC;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,0FAA0F;IAC1F,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IAC1C,yFAAyF;IACzF,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3C;AAWD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,OAAO,EACP,aAAa,EACb,aAAa,EACb,WAAW,EACX,UAAU,EACV,eAAe,EACf,gBAAgB,EAChB,QAAQ,EACR,cAAc,EACd,oBAAoB,GACrB,EAAE,sBAAsB,+BA8ExB"}
1
+ {"version":3,"file":"run-annotation-list.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-tabs/run-annotation-list.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAiC,KAAK,mBAAmB,EAAC,MAAM,oBAAoB,CAAC;AAO5F,OAAO,KAAK,EAAC,kBAAkB,EAAE,kBAAkB,EAAC,MAAM,yBAAyB,CAAC;AAIpF,MAAM,WAAW,sBAAuB,SAAQ,mBAAmB;IACjE,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,aAAa,EAAE,MAAM,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,sBAAsB,CAAC;IAC9B,OAAO,EAAE,kBAAkB,EAAE,GAAG,SAAS,CAAC;IAC1C,0FAA0F;IAC1F,kBAAkB,CAAC,EAAE,SAAS,oBAAoB,EAAE,GAAG,SAAS,CAAC;IACjE,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,0FAA0F;IAC1F,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IAC1C,yFAAyF;IACzF,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3C;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,kBAAkB,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd;AAWD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,OAAO,EACP,kBAAuB,EACvB,aAAa,EACb,aAAa,EACb,WAAW,EACX,UAAU,EACV,eAAe,EACf,gBAAgB,EAChB,QAAQ,EACR,cAAc,EACd,oBAAoB,GACrB,EAAE,sBAAsB,+BAoFxB"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { QueryLoadError } from '@shipfox/client-ui';
2
+ import { AnnotationCard, QueryLoadError } from '@shipfox/client-ui';
3
3
  import { Button } from '@shipfox/react-ui/button';
4
4
  import { Callout } from '@shipfox/react-ui/callout';
5
5
  import { EmptyState } from '@shipfox/react-ui/empty-state';
@@ -20,7 +20,7 @@ import { RunAnnotationsEmpty } from './run-annotations-empty.js';
20
20
  *
21
21
  * Loading, empty, error, and stale are four distinct renderings on purpose: coalescing a failed
22
22
  * fetch into an empty result tells a reader their run is clean when the truth is unknown.
23
- */ export function RunAnnotationList({ query, entries, workflowRunId, workspaceSlug, projectSlug, runAttempt, filteredJobName, filteredSeverity, filtered, onClearFilters, selectedAnnotationId }) {
23
+ */ export function RunAnnotationList({ query, entries, derivedAnnotations = [], workflowRunId, workspaceSlug, projectSlug, runAttempt, filteredJobName, filteredSeverity, filtered, onClearFilters, selectedAnnotationId }) {
24
24
  const [visibleCount, setVisibleCount] = useState(RENDER_WINDOW);
25
25
  // A deep-linked annotation past the window would otherwise be unreachable: it is in the data,
26
26
  // absent from the DOM, and nothing on screen says so.
@@ -37,27 +37,36 @@ import { RunAnnotationsEmpty } from './run-annotations-empty.js';
37
37
  if (entries === undefined) return /*#__PURE__*/ _jsx(RunAnnotationListSkeleton, {});
38
38
  const visible = entries.slice(0, renderCount);
39
39
  const hiddenCount = entries.length - visible.length;
40
+ const hasContent = entries.length > 0 || derivedAnnotations.length > 0;
40
41
  return /*#__PURE__*/ _jsxs("div", {
41
- className: "flex min-w-0 flex-col gap-12",
42
+ className: "flex min-w-0 flex-col gap-cluster",
42
43
  children: [
43
44
  query.isError ? /*#__PURE__*/ _jsx(RunAnnotationStaleError, {
44
45
  query: query
45
46
  }) : null,
46
- entries.length === 0 ? filtered ? /*#__PURE__*/ _jsx(RunAnnotationsFilteredEmpty, {
47
+ !hasContent ? filtered ? /*#__PURE__*/ _jsx(RunAnnotationsFilteredEmpty, {
47
48
  jobName: filteredJobName,
48
49
  severity: filteredSeverity,
49
50
  incomplete: query.hasNextPage,
50
51
  onClearFilters: onClearFilters
51
- }) : /*#__PURE__*/ _jsx(RunAnnotationsEmpty, {}) : /*#__PURE__*/ _jsx("ol", {
52
- className: "flex min-w-0 flex-col gap-12",
53
- children: visible.map((entry)=>/*#__PURE__*/ _jsx(RunAnnotationItem, {
54
- entry: entry,
55
- workspaceSlug: workspaceSlug,
56
- projectSlug: projectSlug,
57
- workflowRunId: workflowRunId,
58
- runAttempt: runAttempt,
59
- selected: entry.annotation.id === selectedAnnotationId
60
- }, entry.annotation.id))
52
+ }) : /*#__PURE__*/ _jsx(RunAnnotationsEmpty, {}) : /*#__PURE__*/ _jsxs("ol", {
53
+ className: "flex min-w-0 flex-col gap-cluster",
54
+ children: [
55
+ visible.map((entry)=>/*#__PURE__*/ _jsx(RunAnnotationItem, {
56
+ entry: entry,
57
+ workspaceSlug: workspaceSlug,
58
+ projectSlug: projectSlug,
59
+ workflowRunId: workflowRunId,
60
+ runAttempt: runAttempt,
61
+ selected: entry.annotation.id === selectedAnnotationId
62
+ }, entry.annotation.id)),
63
+ derivedAnnotations.map((annotation)=>/*#__PURE__*/ _jsx("li", {
64
+ children: /*#__PURE__*/ _jsx(AnnotationCard, {
65
+ style: annotation.style,
66
+ body: annotation.body
67
+ })
68
+ }, annotation.id))
69
+ ]
61
70
  }),
62
71
  hiddenCount > 0 ? /*#__PURE__*/ _jsxs(Button, {
63
72
  type: "button",
@@ -112,7 +121,7 @@ import { RunAnnotationsEmpty } from './run-annotations-empty.js';
112
121
  }
113
122
  function RunAnnotationsFilteredEmpty({ jobName, severity, incomplete, onClearFilters }) {
114
123
  return /*#__PURE__*/ _jsxs("div", {
115
- className: "flex flex-col items-center gap-12",
124
+ className: "flex flex-col items-center gap-cluster",
116
125
  children: [
117
126
  /*#__PURE__*/ _jsx(EmptyState, {
118
127
  icon: "fileDamageLine",
@@ -144,7 +153,7 @@ function RunAnnotationsFilteredEmpty({ jobName, severity, incomplete, onClearFil
144
153
  "aria-live": "polite",
145
154
  type: "error",
146
155
  children: /*#__PURE__*/ _jsxs("div", {
147
- className: "flex w-full items-center justify-between gap-8",
156
+ className: "flex w-full items-center justify-between gap-inline",
148
157
  children: [
149
158
  /*#__PURE__*/ _jsx(Text, {
150
159
  size: "xs",
@@ -173,15 +182,15 @@ const ANNOTATION_SKELETON_ROWS = [
173
182
  function RunAnnotationListSkeleton() {
174
183
  return /*#__PURE__*/ _jsx("section", {
175
184
  "aria-label": "Loading annotations",
176
- className: "flex flex-col gap-12",
185
+ className: "flex flex-col gap-cluster",
177
186
  children: ANNOTATION_SKELETON_ROWS.map((row)=>/*#__PURE__*/ _jsxs("div", {
178
- className: "flex gap-12 rounded-8 border border-border-neutral-base bg-background-components-base px-12 py-8",
187
+ className: "flex gap-cluster rounded-8 border border-border-neutral-base bg-background-components-base px-[12px] py-[8px]",
179
188
  children: [
180
189
  /*#__PURE__*/ _jsx(Skeleton, {
181
190
  className: "h-40 w-4 rounded-full"
182
191
  }),
183
192
  /*#__PURE__*/ _jsxs("div", {
184
- className: "flex min-w-0 flex-1 flex-col gap-6",
193
+ className: "flex min-w-0 flex-1 flex-col gap-inline",
185
194
  children: [
186
195
  /*#__PURE__*/ _jsx(Skeleton, {
187
196
  className: "h-20 w-160 rounded-4"
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/workflow-run-tabs/run-annotation-list.tsx"],"sourcesContent":["import {QueryLoadError, type QueryLoadErrorQuery} from '@shipfox/client-ui';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useState} from 'react';\nimport type {RunAnnotationEntry} from '#core/run-annotation.js';\nimport {RunAnnotationItem} from './run-annotation-item.js';\nimport {RunAnnotationsEmpty} from './run-annotations-empty.js';\n\nexport interface RunAnnotationListQuery extends QueryLoadErrorQuery {\n isPending: boolean;\n hasNextPage: boolean;\n isFetchingNextPage: boolean;\n fetchNextPage: () => unknown;\n}\n\nexport interface RunAnnotationListProps {\n query: RunAnnotationListQuery;\n entries: RunAnnotationEntry[] | undefined;\n workflowRunId: string;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n runAttempt?: number | undefined;\n /** Name of the job the list is filtered to, used only for empty-state copy. */\n filteredJobName?: string | undefined;\n /** Active severity filter, named in the empty state so the reader knows what to clear. */\n filteredSeverity?: string | undefined;\n filtered: boolean;\n onClearFilters?: (() => void) | undefined;\n /** Annotation id from `?annotation=`, which the matching card scrolls to and focuses. */\n selectedAnnotationId?: string | undefined;\n}\n\n/**\n * How many annotations render at once.\n *\n * A page holds up to 500 rows and each one mounts a Markdown pipeline, so the fetch budget is\n * not a render budget. The window grows on demand and the server page only loads once the\n * reader has actually seen everything already fetched.\n */\nconst RENDER_WINDOW = 25;\n\n/**\n * The annotations list.\n *\n * Loading, empty, error, and stale are four distinct renderings on purpose: coalescing a failed\n * fetch into an empty result tells a reader their run is clean when the truth is unknown.\n */\nexport function RunAnnotationList({\n query,\n entries,\n workflowRunId,\n workspaceSlug,\n projectSlug,\n runAttempt,\n filteredJobName,\n filteredSeverity,\n filtered,\n onClearFilters,\n selectedAnnotationId,\n}: RunAnnotationListProps) {\n const [visibleCount, setVisibleCount] = useState(RENDER_WINDOW);\n\n // A deep-linked annotation past the window would otherwise be unreachable: it is in the data,\n // absent from the DOM, and nothing on screen says so.\n const selectedIndex = selectedAnnotationId\n ? (entries?.findIndex((entry) => entry.annotation.id === selectedAnnotationId) ?? -1)\n : -1;\n const renderCount = Math.max(visibleCount, selectedIndex + 1);\n\n if (query.isPending) return <RunAnnotationListSkeleton />;\n\n if (query.isError && entries === undefined) {\n return <QueryLoadError query={query} subject=\"annotations\" icon=\"fileDamageLine\" />;\n }\n\n if (entries === undefined) return <RunAnnotationListSkeleton />;\n\n const visible = entries.slice(0, renderCount);\n const hiddenCount = entries.length - visible.length;\n\n return (\n <div className=\"flex min-w-0 flex-col gap-12\">\n {query.isError ? <RunAnnotationStaleError query={query} /> : null}\n\n {entries.length === 0 ? (\n filtered ? (\n <RunAnnotationsFilteredEmpty\n jobName={filteredJobName}\n severity={filteredSeverity}\n incomplete={query.hasNextPage}\n onClearFilters={onClearFilters}\n />\n ) : (\n <RunAnnotationsEmpty />\n )\n ) : (\n <ol className=\"flex min-w-0 flex-col gap-12\">\n {visible.map((entry) => (\n <RunAnnotationItem\n key={entry.annotation.id}\n entry={entry}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n runAttempt={runAttempt}\n selected={entry.annotation.id === selectedAnnotationId}\n />\n ))}\n </ol>\n )}\n\n {hiddenCount > 0 ? (\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"secondary\"\n className=\"self-start [@media(pointer:coarse)]:min-h-44\"\n onClick={() => setVisibleCount((current) => current + RENDER_WINDOW)}\n >\n Show {Math.min(hiddenCount, RENDER_WINDOW)} more of {entries.length}\n </Button>\n ) : query.hasNextPage ? (\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"secondary\"\n className=\"self-start [@media(pointer:coarse)]:min-h-44\"\n isLoading={query.isFetchingNextPage}\n onClick={() => {\n void query.fetchNextPage();\n }}\n >\n Load more annotations\n </Button>\n ) : null}\n </div>\n );\n}\n\n/**\n * Names the filters that are actually active.\n *\n * Blaming severity for a job-only filter sends the reader to change the wrong control, which\n * is worse than saying nothing.\n */\nfunction filteredEmptyDescription({\n jobName,\n severity,\n incomplete,\n}: {\n jobName: string | undefined;\n severity: string | undefined;\n incomplete: boolean;\n}): string {\n if (incomplete) {\n if (jobName && severity) {\n return `None of the loaded annotations are from ${jobName} at ${severity} severity. Load more annotations to continue searching.`;\n }\n if (jobName) {\n return `None of the loaded annotations are from ${jobName}. Load more annotations to continue searching.`;\n }\n if (severity) {\n return `None of the loaded annotations are at ${severity} severity. Load more annotations to continue searching.`;\n }\n return 'None of the loaded annotations match the current filters. Load more annotations to continue searching.';\n }\n if (jobName && severity) {\n return `This run has annotations, but none from ${jobName} at ${severity} severity.`;\n }\n if (jobName) return `This run has annotations, but none from ${jobName}.`;\n if (severity) return `This run has annotations, but none at ${severity} severity.`;\n return 'This run has annotations, but none match the current filters.';\n}\n\nfunction RunAnnotationsFilteredEmpty({\n jobName,\n severity,\n incomplete,\n onClearFilters,\n}: {\n jobName: string | undefined;\n severity: string | undefined;\n incomplete: boolean;\n onClearFilters: (() => void) | undefined;\n}) {\n return (\n <div className=\"flex flex-col items-center gap-12\">\n <EmptyState\n icon=\"fileDamageLine\"\n title={incomplete ? 'No matches in loaded annotations' : 'No matching annotations'}\n description={filteredEmptyDescription({jobName, severity, incomplete})}\n />\n {onClearFilters ? (\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"secondary\"\n className=\"[@media(pointer:coarse)]:min-h-44\"\n onClick={onClearFilters}\n >\n Clear filters\n </Button>\n ) : null}\n </div>\n );\n}\n\n/**\n * A failed poll keeps the annotations already on screen and marks them stale rather than\n * clearing. Polite rather than assertive: the poll runs every 4s and a flapping API would\n * otherwise interrupt a screen reader on every cycle.\n */\nfunction RunAnnotationStaleError({query}: {query: QueryLoadErrorQuery}) {\n return (\n <Callout role=\"status\" aria-live=\"polite\" type=\"error\">\n <div className=\"flex w-full items-center justify-between gap-8\">\n <Text size=\"xs\">Could not refresh annotations.</Text>\n <Button\n type=\"button\"\n size=\"2xs\"\n variant=\"secondary\"\n className=\"[@media(pointer:coarse)]:min-h-44\"\n isLoading={query.isFetching}\n onClick={() => {\n void query.refetch();\n }}\n >\n Retry\n </Button>\n </div>\n </Callout>\n );\n}\n\nconst ANNOTATION_SKELETON_ROWS = ['first', 'second', 'third'];\n\nfunction RunAnnotationListSkeleton() {\n return (\n <section aria-label=\"Loading annotations\" className=\"flex flex-col gap-12\">\n {ANNOTATION_SKELETON_ROWS.map((row) => (\n <div\n key={row}\n className=\"flex gap-12 rounded-8 border border-border-neutral-base bg-background-components-base px-12 py-8\"\n >\n <Skeleton className=\"h-40 w-4 rounded-full\" />\n <div className=\"flex min-w-0 flex-1 flex-col gap-6\">\n <Skeleton className=\"h-20 w-160 rounded-4\" />\n <Skeleton className=\"h-16 w-240 rounded-4\" />\n <Skeleton className=\"h-40 w-full rounded-4\" />\n </div>\n </div>\n ))}\n </section>\n );\n}\n"],"names":["QueryLoadError","Button","Callout","EmptyState","Skeleton","Text","useState","RunAnnotationItem","RunAnnotationsEmpty","RENDER_WINDOW","RunAnnotationList","query","entries","workflowRunId","workspaceSlug","projectSlug","runAttempt","filteredJobName","filteredSeverity","filtered","onClearFilters","selectedAnnotationId","visibleCount","setVisibleCount","selectedIndex","findIndex","entry","annotation","id","renderCount","Math","max","isPending","RunAnnotationListSkeleton","isError","undefined","subject","icon","visible","slice","hiddenCount","length","div","className","RunAnnotationStaleError","RunAnnotationsFilteredEmpty","jobName","severity","incomplete","hasNextPage","ol","map","selected","type","size","variant","onClick","current","min","isLoading","isFetchingNextPage","fetchNextPage","filteredEmptyDescription","title","description","role","aria-live","isFetching","refetch","ANNOTATION_SKELETON_ROWS","section","aria-label","row"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/workflow-run-tabs/run-annotation-list.tsx"],"sourcesContent":["import {AnnotationCard, QueryLoadError, type QueryLoadErrorQuery} from '@shipfox/client-ui';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useState} from 'react';\nimport type {RunAnnotationEntry, RunAnnotationStyle} from '#core/run-annotation.js';\nimport {RunAnnotationItem} from './run-annotation-item.js';\nimport {RunAnnotationsEmpty} from './run-annotations-empty.js';\n\nexport interface RunAnnotationListQuery extends QueryLoadErrorQuery {\n isPending: boolean;\n hasNextPage: boolean;\n isFetchingNextPage: boolean;\n fetchNextPage: () => unknown;\n}\n\nexport interface RunAnnotationListProps {\n query: RunAnnotationListQuery;\n entries: RunAnnotationEntry[] | undefined;\n /** Synthetic terminal-job diagnostics for jobs that never created an execution record. */\n derivedAnnotations?: readonly DerivedRunAnnotation[] | undefined;\n workflowRunId: string;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n runAttempt?: number | undefined;\n /** Name of the job the list is filtered to, used only for empty-state copy. */\n filteredJobName?: string | undefined;\n /** Active severity filter, named in the empty state so the reader knows what to clear. */\n filteredSeverity?: string | undefined;\n filtered: boolean;\n onClearFilters?: (() => void) | undefined;\n /** Annotation id from `?annotation=`, which the matching card scrolls to and focuses. */\n selectedAnnotationId?: string | undefined;\n}\n\nexport interface DerivedRunAnnotation {\n id: string;\n style: RunAnnotationStyle;\n body: string;\n}\n\n/**\n * How many annotations render at once.\n *\n * A page holds up to 500 rows and each one mounts a Markdown pipeline, so the fetch budget is\n * not a render budget. The window grows on demand and the server page only loads once the\n * reader has actually seen everything already fetched.\n */\nconst RENDER_WINDOW = 25;\n\n/**\n * The annotations list.\n *\n * Loading, empty, error, and stale are four distinct renderings on purpose: coalescing a failed\n * fetch into an empty result tells a reader their run is clean when the truth is unknown.\n */\nexport function RunAnnotationList({\n query,\n entries,\n derivedAnnotations = [],\n workflowRunId,\n workspaceSlug,\n projectSlug,\n runAttempt,\n filteredJobName,\n filteredSeverity,\n filtered,\n onClearFilters,\n selectedAnnotationId,\n}: RunAnnotationListProps) {\n const [visibleCount, setVisibleCount] = useState(RENDER_WINDOW);\n\n // A deep-linked annotation past the window would otherwise be unreachable: it is in the data,\n // absent from the DOM, and nothing on screen says so.\n const selectedIndex = selectedAnnotationId\n ? (entries?.findIndex((entry) => entry.annotation.id === selectedAnnotationId) ?? -1)\n : -1;\n const renderCount = Math.max(visibleCount, selectedIndex + 1);\n\n if (query.isPending) return <RunAnnotationListSkeleton />;\n\n if (query.isError && entries === undefined) {\n return <QueryLoadError query={query} subject=\"annotations\" icon=\"fileDamageLine\" />;\n }\n\n if (entries === undefined) return <RunAnnotationListSkeleton />;\n\n const visible = entries.slice(0, renderCount);\n const hiddenCount = entries.length - visible.length;\n const hasContent = entries.length > 0 || derivedAnnotations.length > 0;\n\n return (\n <div className=\"flex min-w-0 flex-col gap-cluster\">\n {query.isError ? <RunAnnotationStaleError query={query} /> : null}\n\n {!hasContent ? (\n filtered ? (\n <RunAnnotationsFilteredEmpty\n jobName={filteredJobName}\n severity={filteredSeverity}\n incomplete={query.hasNextPage}\n onClearFilters={onClearFilters}\n />\n ) : (\n <RunAnnotationsEmpty />\n )\n ) : (\n <ol className=\"flex min-w-0 flex-col gap-cluster\">\n {visible.map((entry) => (\n <RunAnnotationItem\n key={entry.annotation.id}\n entry={entry}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n runAttempt={runAttempt}\n selected={entry.annotation.id === selectedAnnotationId}\n />\n ))}\n {derivedAnnotations.map((annotation) => (\n <li key={annotation.id}>\n <AnnotationCard style={annotation.style} body={annotation.body} />\n </li>\n ))}\n </ol>\n )}\n\n {hiddenCount > 0 ? (\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"secondary\"\n className=\"self-start [@media(pointer:coarse)]:min-h-44\"\n onClick={() => setVisibleCount((current) => current + RENDER_WINDOW)}\n >\n Show {Math.min(hiddenCount, RENDER_WINDOW)} more of {entries.length}\n </Button>\n ) : query.hasNextPage ? (\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"secondary\"\n className=\"self-start [@media(pointer:coarse)]:min-h-44\"\n isLoading={query.isFetchingNextPage}\n onClick={() => {\n void query.fetchNextPage();\n }}\n >\n Load more annotations\n </Button>\n ) : null}\n </div>\n );\n}\n\n/**\n * Names the filters that are actually active.\n *\n * Blaming severity for a job-only filter sends the reader to change the wrong control, which\n * is worse than saying nothing.\n */\nfunction filteredEmptyDescription({\n jobName,\n severity,\n incomplete,\n}: {\n jobName: string | undefined;\n severity: string | undefined;\n incomplete: boolean;\n}): string {\n if (incomplete) {\n if (jobName && severity) {\n return `None of the loaded annotations are from ${jobName} at ${severity} severity. Load more annotations to continue searching.`;\n }\n if (jobName) {\n return `None of the loaded annotations are from ${jobName}. Load more annotations to continue searching.`;\n }\n if (severity) {\n return `None of the loaded annotations are at ${severity} severity. Load more annotations to continue searching.`;\n }\n return 'None of the loaded annotations match the current filters. Load more annotations to continue searching.';\n }\n if (jobName && severity) {\n return `This run has annotations, but none from ${jobName} at ${severity} severity.`;\n }\n if (jobName) return `This run has annotations, but none from ${jobName}.`;\n if (severity) return `This run has annotations, but none at ${severity} severity.`;\n return 'This run has annotations, but none match the current filters.';\n}\n\nfunction RunAnnotationsFilteredEmpty({\n jobName,\n severity,\n incomplete,\n onClearFilters,\n}: {\n jobName: string | undefined;\n severity: string | undefined;\n incomplete: boolean;\n onClearFilters: (() => void) | undefined;\n}) {\n return (\n <div className=\"flex flex-col items-center gap-cluster\">\n <EmptyState\n icon=\"fileDamageLine\"\n title={incomplete ? 'No matches in loaded annotations' : 'No matching annotations'}\n description={filteredEmptyDescription({jobName, severity, incomplete})}\n />\n {onClearFilters ? (\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"secondary\"\n className=\"[@media(pointer:coarse)]:min-h-44\"\n onClick={onClearFilters}\n >\n Clear filters\n </Button>\n ) : null}\n </div>\n );\n}\n\n/**\n * A failed poll keeps the annotations already on screen and marks them stale rather than\n * clearing. Polite rather than assertive: the poll runs every 4s and a flapping API would\n * otherwise interrupt a screen reader on every cycle.\n */\nfunction RunAnnotationStaleError({query}: {query: QueryLoadErrorQuery}) {\n return (\n <Callout role=\"status\" aria-live=\"polite\" type=\"error\">\n <div className=\"flex w-full items-center justify-between gap-inline\">\n <Text size=\"xs\">Could not refresh annotations.</Text>\n <Button\n type=\"button\"\n size=\"2xs\"\n variant=\"secondary\"\n className=\"[@media(pointer:coarse)]:min-h-44\"\n isLoading={query.isFetching}\n onClick={() => {\n void query.refetch();\n }}\n >\n Retry\n </Button>\n </div>\n </Callout>\n );\n}\n\nconst ANNOTATION_SKELETON_ROWS = ['first', 'second', 'third'];\n\nfunction RunAnnotationListSkeleton() {\n return (\n <section aria-label=\"Loading annotations\" className=\"flex flex-col gap-cluster\">\n {ANNOTATION_SKELETON_ROWS.map((row) => (\n <div\n key={row}\n className=\"flex gap-cluster rounded-8 border border-border-neutral-base bg-background-components-base px-[12px] py-[8px]\"\n >\n <Skeleton className=\"h-40 w-4 rounded-full\" />\n <div className=\"flex min-w-0 flex-1 flex-col gap-inline\">\n <Skeleton className=\"h-20 w-160 rounded-4\" />\n <Skeleton className=\"h-16 w-240 rounded-4\" />\n <Skeleton className=\"h-40 w-full rounded-4\" />\n </div>\n </div>\n ))}\n </section>\n );\n}\n"],"names":["AnnotationCard","QueryLoadError","Button","Callout","EmptyState","Skeleton","Text","useState","RunAnnotationItem","RunAnnotationsEmpty","RENDER_WINDOW","RunAnnotationList","query","entries","derivedAnnotations","workflowRunId","workspaceSlug","projectSlug","runAttempt","filteredJobName","filteredSeverity","filtered","onClearFilters","selectedAnnotationId","visibleCount","setVisibleCount","selectedIndex","findIndex","entry","annotation","id","renderCount","Math","max","isPending","RunAnnotationListSkeleton","isError","undefined","subject","icon","visible","slice","hiddenCount","length","hasContent","div","className","RunAnnotationStaleError","RunAnnotationsFilteredEmpty","jobName","severity","incomplete","hasNextPage","ol","map","selected","li","style","body","type","size","variant","onClick","current","min","isLoading","isFetchingNextPage","fetchNextPage","filteredEmptyDescription","title","description","role","aria-live","isFetching","refetch","ANNOTATION_SKELETON_ROWS","section","aria-label","row"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"run-annotation-summary-line.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-tabs/run-annotation-summary-line.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAEL,KAAK,kBAAkB,EAExB,MAAM,mBAAmB,CAAC;AAiB3B,MAAM,WAAW,6BAA6B;IAC5C,OAAO,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;IAC3C,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,MAAM,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;CACzC;AAED,wBAAgB,wBAAwB,CAAC,EACvC,OAAO,EACP,aAAa,EACb,WAAW,EACX,aAAa,EACb,MAAW,GACZ,EAAE,6BAA6B,sCAsD/B"}
1
+ {"version":3,"file":"run-annotation-summary-line.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-tabs/run-annotation-summary-line.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAEL,KAAK,kBAAkB,EAExB,MAAM,mBAAmB,CAAC;AAgB3B,MAAM,WAAW,6BAA6B;IAC5C,OAAO,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;IAC3C,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,MAAM,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;CACzC;AAED,wBAAgB,wBAAwB,CAAC,EACvC,OAAO,EACP,aAAa,EACb,WAAW,EACX,aAAa,EACb,MAAW,GACZ,EAAE,6BAA6B,sCAsD/B"}
@@ -1,10 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { MetadataSeparator } from '@shipfox/client-ui';
2
3
  import { Icon } from '@shipfox/react-ui/icon';
3
4
  import { Text } from '@shipfox/react-ui/typography';
4
5
  import { cn } from '@shipfox/react-ui/utils';
5
6
  import { Link } from '@tanstack/react-router';
6
7
  import { workflowRunSearchParams } from '#routes/inputs.js';
7
- import { MetadataSeparator } from '../workflow-run-summary/workflow-run-summary.js';
8
8
  import { SEVERITY_ICON, SEVERITY_ICON_TONE } from './severity-visuals.js';
9
9
  /**
10
10
  * The severities this line names.
@@ -24,7 +24,7 @@ export function RunAnnotationSummaryLine({ summary, workspaceSlug, projectSlug,
24
24
  const visibleSeverities = ATTENTION_SEVERITIES.filter((severity)=>summary[severity] > 0);
25
25
  const totalLabel = countLabel(summary.total, 'annotation', summary.truncated);
26
26
  return /*#__PURE__*/ _jsxs("div", {
27
- className: "flex flex-wrap items-center gap-x-8 gap-y-4 text-foreground-neutral-subtle",
27
+ className: "flex flex-wrap items-center gap-x-inline gap-y-tight text-foreground-neutral-subtle",
28
28
  children: [
29
29
  search.severity && workspaceSlug && projectSlug && workflowRunId ? // The severity filter has no control of its own, so while one is active the total is
30
30
  // the way back out. Unfiltered it stays plain text: a link to where you already are
@@ -55,7 +55,7 @@ export function RunAnnotationSummaryLine({ summary, workspaceSlug, projectSlug,
55
55
  children: totalLabel
56
56
  }),
57
57
  visibleSeverities.map((severity)=>/*#__PURE__*/ _jsxs("span", {
58
- className: "inline-flex shrink-0 items-center gap-8",
58
+ className: "inline-flex shrink-0 items-center gap-inline",
59
59
  children: [
60
60
  /*#__PURE__*/ _jsx(MetadataSeparator, {}),
61
61
  /*#__PURE__*/ _jsx(SeverityLink, {
@@ -92,7 +92,7 @@ function SeverityLink({ count, truncated, severity, workspaceSlug, projectSlug,
92
92
  });
93
93
  if (!workspaceSlug || !projectSlug || !workflowRunId) {
94
94
  return /*#__PURE__*/ _jsx("span", {
95
- className: "inline-flex items-center gap-4",
95
+ className: "inline-flex items-center gap-tight",
96
96
  children: content
97
97
  });
98
98
  }
@@ -109,7 +109,7 @@ function SeverityLink({ count, truncated, severity, workspaceSlug, projectSlug,
109
109
  tab: 'annotations',
110
110
  severity
111
111
  }, search),
112
- className: "inline-flex items-center gap-4 rounded-4 outline-none hover:underline focus-visible:shadow-border-interactive-with-active",
112
+ className: "inline-flex items-center gap-tight rounded-4 outline-none hover:underline focus-visible:shadow-border-interactive-with-active",
113
113
  children: content
114
114
  });
115
115
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/workflow-run-tabs/run-annotation-summary-line.tsx"],"sourcesContent":["import {Icon} from '@shipfox/react-ui/icon';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport type {RunAnnotationSummary} from '#core/run-annotation.js';\nimport {\n type WorkflowRunAnnotationSeverity,\n type WorkflowRunsSearch,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {MetadataSeparator} from '../workflow-run-summary/workflow-run-summary.js';\nimport {SEVERITY_ICON, SEVERITY_ICON_TONE} from './severity-visuals.js';\n\n/**\n * The severities this line names.\n *\n * The line exists so a failing run cannot look clean while the explanation sits behind an\n * inactive section, which makes its subject \"what needs attention\", not \"what happened\".\n * `info` and `success` are already inside the total, and repeating them gives \"nothing is\n * wrong\" the same weight and the same accent as \"something failed\".\n */\nconst ATTENTION_SEVERITIES = [\n 'error',\n 'warning',\n] as const satisfies readonly WorkflowRunAnnotationSeverity[];\n\nexport interface RunAnnotationSummaryLineProps {\n summary?: RunAnnotationSummary | undefined;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n workflowRunId?: string | undefined;\n search?: WorkflowRunsSearch | undefined;\n}\n\nexport function RunAnnotationSummaryLine({\n summary,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n search = {},\n}: RunAnnotationSummaryLineProps) {\n // A run with no annotations says so in its empty state; a \"0 annotations\" line beside it is\n // the same fact twice.\n if (!summary || summary.total === 0) return null;\n\n const visibleSeverities = ATTENTION_SEVERITIES.filter((severity) => summary[severity] > 0);\n\n const totalLabel = countLabel(summary.total, 'annotation', summary.truncated);\n\n return (\n <div className=\"flex flex-wrap items-center gap-x-8 gap-y-4 text-foreground-neutral-subtle\">\n {search.severity && workspaceSlug && projectSlug && workflowRunId ? (\n // The severity filter has no control of its own, so while one is active the total is\n // the way back out. Unfiltered it stays plain text: a link to where you already are\n // is noise.\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={\n workflowRunSearchParams(\n {...withoutSeverity(search), tab: 'annotations'},\n search,\n ) as never\n }\n aria-label={`Show all ${totalLabel}`}\n className=\"shrink-0 rounded-4 outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n {/* Underlined rather than accented: the escape hatch has to read as a link without\n becoming the loudest thing on a line whose job is to be scannable. */}\n <Text as=\"span\" size=\"xs\" className=\"text-foreground-neutral-subtle underline\">\n {totalLabel}\n </Text>\n </Link>\n ) : (\n <Text as=\"span\" size=\"xs\" className=\"shrink-0 text-foreground-neutral-subtle\">\n {totalLabel}\n </Text>\n )}\n {visibleSeverities.map((severity) => (\n <span key={severity} className=\"inline-flex shrink-0 items-center gap-8\">\n <MetadataSeparator />\n <SeverityLink\n count={summary[severity]}\n truncated={summary.truncated}\n severity={severity}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n search={search}\n />\n </span>\n ))}\n </div>\n );\n}\n\nfunction SeverityLink({\n count,\n truncated,\n severity,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n search,\n}: {\n count: number;\n truncated: boolean;\n severity: WorkflowRunAnnotationSeverity;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n workflowRunId?: string | undefined;\n search: WorkflowRunsSearch;\n}) {\n const label = countLabel(count, severity, truncated);\n const content = (\n <>\n <Icon\n name={SEVERITY_ICON[severity]}\n size={12}\n aria-hidden=\"true\"\n className={cn('shrink-0', SEVERITY_ICON_TONE[severity])}\n />\n <Text as=\"span\" size=\"xs\" className=\"text-foreground-neutral-subtle\">\n {label}\n </Text>\n </>\n );\n\n if (!workspaceSlug || !projectSlug || !workflowRunId) {\n return <span className=\"inline-flex items-center gap-4\">{content}</span>;\n }\n\n const searchWithoutAnnotation = withoutAnnotation(search);\n\n return (\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={\n workflowRunSearchParams(\n {...searchWithoutAnnotation, tab: 'annotations', severity},\n search,\n ) as never\n }\n className=\"inline-flex items-center gap-4 rounded-4 outline-none hover:underline focus-visible:shadow-border-interactive-with-active\"\n >\n {content}\n </Link>\n );\n}\n\n/** A selected annotation is stale the moment the list it was chosen from is refiltered. */\nfunction withoutAnnotation(search: WorkflowRunsSearch): WorkflowRunsSearch {\n const next = {...search};\n delete next.annotation;\n return next;\n}\n\nfunction withoutSeverity(search: WorkflowRunsSearch): WorkflowRunsSearch {\n const next = withoutAnnotation(search);\n delete next.severity;\n return next;\n}\n\n/**\n * A truncated read renders `500+ annotations`: the counts are a lower bound once the page budget\n * is hit, and presenting them as exact would understate a run the reader is trying to trust.\n */\nfunction countLabel(count: number, word: string, truncated: boolean): string {\n const plural = count === 1 && !truncated ? word : `${word}s`;\n return `${count}${truncated ? '+' : ''} ${plural}`;\n}\n"],"names":["Icon","Text","cn","Link","workflowRunSearchParams","MetadataSeparator","SEVERITY_ICON","SEVERITY_ICON_TONE","ATTENTION_SEVERITIES","RunAnnotationSummaryLine","summary","workspaceSlug","projectSlug","workflowRunId","search","total","visibleSeverities","filter","severity","totalLabel","countLabel","truncated","div","className","to","params","withoutSeverity","tab","aria-label","as","size","map","span","SeverityLink","count","label","content","name","aria-hidden","searchWithoutAnnotation","withoutAnnotation","next","annotation","word","plural"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/workflow-run-tabs/run-annotation-summary-line.tsx"],"sourcesContent":["import {MetadataSeparator} from '@shipfox/client-ui';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport type {RunAnnotationSummary} from '#core/run-annotation.js';\nimport {\n type WorkflowRunAnnotationSeverity,\n type WorkflowRunsSearch,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {SEVERITY_ICON, SEVERITY_ICON_TONE} from './severity-visuals.js';\n\n/**\n * The severities this line names.\n *\n * The line exists so a failing run cannot look clean while the explanation sits behind an\n * inactive section, which makes its subject \"what needs attention\", not \"what happened\".\n * `info` and `success` are already inside the total, and repeating them gives \"nothing is\n * wrong\" the same weight and the same accent as \"something failed\".\n */\nconst ATTENTION_SEVERITIES = [\n 'error',\n 'warning',\n] as const satisfies readonly WorkflowRunAnnotationSeverity[];\n\nexport interface RunAnnotationSummaryLineProps {\n summary?: RunAnnotationSummary | undefined;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n workflowRunId?: string | undefined;\n search?: WorkflowRunsSearch | undefined;\n}\n\nexport function RunAnnotationSummaryLine({\n summary,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n search = {},\n}: RunAnnotationSummaryLineProps) {\n // A run with no annotations says so in its empty state; a \"0 annotations\" line beside it is\n // the same fact twice.\n if (!summary || summary.total === 0) return null;\n\n const visibleSeverities = ATTENTION_SEVERITIES.filter((severity) => summary[severity] > 0);\n\n const totalLabel = countLabel(summary.total, 'annotation', summary.truncated);\n\n return (\n <div className=\"flex flex-wrap items-center gap-x-inline gap-y-tight text-foreground-neutral-subtle\">\n {search.severity && workspaceSlug && projectSlug && workflowRunId ? (\n // The severity filter has no control of its own, so while one is active the total is\n // the way back out. Unfiltered it stays plain text: a link to where you already are\n // is noise.\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={\n workflowRunSearchParams(\n {...withoutSeverity(search), tab: 'annotations'},\n search,\n ) as never\n }\n aria-label={`Show all ${totalLabel}`}\n className=\"shrink-0 rounded-4 outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n {/* Underlined rather than accented: the escape hatch has to read as a link without\n becoming the loudest thing on a line whose job is to be scannable. */}\n <Text as=\"span\" size=\"xs\" className=\"text-foreground-neutral-subtle underline\">\n {totalLabel}\n </Text>\n </Link>\n ) : (\n <Text as=\"span\" size=\"xs\" className=\"shrink-0 text-foreground-neutral-subtle\">\n {totalLabel}\n </Text>\n )}\n {visibleSeverities.map((severity) => (\n <span key={severity} className=\"inline-flex shrink-0 items-center gap-inline\">\n <MetadataSeparator />\n <SeverityLink\n count={summary[severity]}\n truncated={summary.truncated}\n severity={severity}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n search={search}\n />\n </span>\n ))}\n </div>\n );\n}\n\nfunction SeverityLink({\n count,\n truncated,\n severity,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n search,\n}: {\n count: number;\n truncated: boolean;\n severity: WorkflowRunAnnotationSeverity;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n workflowRunId?: string | undefined;\n search: WorkflowRunsSearch;\n}) {\n const label = countLabel(count, severity, truncated);\n const content = (\n <>\n <Icon\n name={SEVERITY_ICON[severity]}\n size={12}\n aria-hidden=\"true\"\n className={cn('shrink-0', SEVERITY_ICON_TONE[severity])}\n />\n <Text as=\"span\" size=\"xs\" className=\"text-foreground-neutral-subtle\">\n {label}\n </Text>\n </>\n );\n\n if (!workspaceSlug || !projectSlug || !workflowRunId) {\n return <span className=\"inline-flex items-center gap-tight\">{content}</span>;\n }\n\n const searchWithoutAnnotation = withoutAnnotation(search);\n\n return (\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={\n workflowRunSearchParams(\n {...searchWithoutAnnotation, tab: 'annotations', severity},\n search,\n ) as never\n }\n className=\"inline-flex items-center gap-tight rounded-4 outline-none hover:underline focus-visible:shadow-border-interactive-with-active\"\n >\n {content}\n </Link>\n );\n}\n\n/** A selected annotation is stale the moment the list it was chosen from is refiltered. */\nfunction withoutAnnotation(search: WorkflowRunsSearch): WorkflowRunsSearch {\n const next = {...search};\n delete next.annotation;\n return next;\n}\n\nfunction withoutSeverity(search: WorkflowRunsSearch): WorkflowRunsSearch {\n const next = withoutAnnotation(search);\n delete next.severity;\n return next;\n}\n\n/**\n * A truncated read renders `500+ annotations`: the counts are a lower bound once the page budget\n * is hit, and presenting them as exact would understate a run the reader is trying to trust.\n */\nfunction countLabel(count: number, word: string, truncated: boolean): string {\n const plural = count === 1 && !truncated ? word : `${word}s`;\n return `${count}${truncated ? '+' : ''} ${plural}`;\n}\n"],"names":["MetadataSeparator","Icon","Text","cn","Link","workflowRunSearchParams","SEVERITY_ICON","SEVERITY_ICON_TONE","ATTENTION_SEVERITIES","RunAnnotationSummaryLine","summary","workspaceSlug","projectSlug","workflowRunId","search","total","visibleSeverities","filter","severity","totalLabel","countLabel","truncated","div","className","to","params","withoutSeverity","tab","aria-label","as","size","map","span","SeverityLink","count","label","content","name","aria-hidden","searchWithoutAnnotation","withoutAnnotation","next","annotation","word","plural"],"mappings":"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"}