@shipfox/client-workflows 20.0.0 → 21.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 (105) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +29 -0
  3. package/dist/components/job-detail/job-detail-view.d.ts.map +1 -1
  4. package/dist/components/job-detail/job-detail-view.js +231 -42
  5. package/dist/components/job-detail/job-detail-view.js.map +1 -1
  6. package/dist/components/job-detail/step-attempt-log-panel.d.ts +7 -1
  7. package/dist/components/job-detail/step-attempt-log-panel.d.ts.map +1 -1
  8. package/dist/components/job-detail/step-attempt-log-panel.js +37 -10
  9. package/dist/components/job-detail/step-attempt-log-panel.js.map +1 -1
  10. package/dist/components/step-list/step-list-model.d.ts +1 -0
  11. package/dist/components/step-list/step-list-model.d.ts.map +1 -1
  12. package/dist/components/step-list/step-list-model.js +1 -1
  13. package/dist/components/step-list/step-list-model.js.map +1 -1
  14. package/dist/components/step-list/step-list.d.ts +2 -1
  15. package/dist/components/step-list/step-list.d.ts.map +1 -1
  16. package/dist/components/step-list/step-list.js +24 -7
  17. package/dist/components/step-list/step-list.js.map +1 -1
  18. package/dist/components/workflow-run-list/job-status-strip.d.ts +9 -1
  19. package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -1
  20. package/dist/components/workflow-run-list/job-status-strip.js +22 -11
  21. package/dist/components/workflow-run-list/job-status-strip.js.map +1 -1
  22. package/dist/components/workflow-run-list/workflow-run-filters.js +1 -1
  23. package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -1
  24. package/dist/components/workflow-run-list/workflow-run-list-states.js +2 -2
  25. package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
  26. package/dist/components/workflow-run-list/workflow-run-list-view.d.ts.map +1 -1
  27. package/dist/components/workflow-run-list/workflow-run-list-view.js +34 -22
  28. package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
  29. package/dist/components/workflow-run-list/workflow-run-row.d.ts +3 -3
  30. package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
  31. package/dist/components/workflow-run-list/workflow-run-row.js +13 -10
  32. package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
  33. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
  34. package/dist/components/workflow-run-summary/workflow-run-summary.js +1 -1
  35. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
  36. package/dist/components/workflow-run-view/run-workspace-nav.js +1 -1
  37. package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
  38. package/dist/components/workflow-run-view/workflow-run-states.d.ts.map +1 -1
  39. package/dist/components/workflow-run-view/workflow-run-states.js +1 -1
  40. package/dist/components/workflow-run-view/workflow-run-states.js.map +1 -1
  41. package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
  42. package/dist/components/workflow-run-view/workflow-run-view.js +43 -36
  43. package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
  44. package/dist/components/workflow-source-panel/workflow-source-content.js +1 -1
  45. package/dist/components/workflow-source-panel/workflow-source-content.js.map +1 -1
  46. package/dist/pages/project-workflows-page.d.ts.map +1 -1
  47. package/dist/pages/project-workflows-page.js +3 -2
  48. package/dist/pages/project-workflows-page.js.map +1 -1
  49. package/dist/pages/workflow-job-detail-page.d.ts.map +1 -1
  50. package/dist/pages/workflow-job-detail-page.js +3 -2
  51. package/dist/pages/workflow-job-detail-page.js.map +1 -1
  52. package/dist/pages/workflow-run-detail-page.js +2 -1
  53. package/dist/pages/workflow-run-detail-page.js.map +1 -1
  54. package/dist/pages/workflow-run-list-page.d.ts.map +1 -1
  55. package/dist/pages/workflow-run-list-page.js +9 -11
  56. package/dist/pages/workflow-run-list-page.js.map +1 -1
  57. package/dist/routes/job-detail.d.ts +1 -1
  58. package/dist/routes/job-detail.js +1 -1
  59. package/dist/routes/job-detail.js.map +1 -1
  60. package/dist/routes/run-detail.d.ts +1 -1
  61. package/dist/routes/run-detail.js +1 -1
  62. package/dist/routes/run-detail.js.map +1 -1
  63. package/dist/routes/runs.d.ts +1 -1
  64. package/dist/routes/runs.js +1 -1
  65. package/dist/routes/runs.js.map +1 -1
  66. package/dist/routes/workflows.d.ts +3 -0
  67. package/dist/routes/workflows.js +3 -0
  68. package/dist/routes/workflows.js.map +1 -1
  69. package/dist/tsconfig.test.tsbuildinfo +1 -1
  70. package/package.json +7 -7
  71. package/src/components/job-detail/job-detail-view.tsx +263 -47
  72. package/src/components/job-detail/step-attempt-log-panel.test.tsx +30 -12
  73. package/src/components/job-detail/step-attempt-log-panel.tsx +68 -10
  74. package/src/components/job-graph/job-graph.stories.tsx +1 -1
  75. package/src/components/job-graph/job-node.stories.tsx +1 -1
  76. package/src/components/step-list/step-list-model.ts +1 -1
  77. package/src/components/step-list/step-list.test.tsx +29 -1
  78. package/src/components/step-list/step-list.tsx +28 -9
  79. package/src/components/workflow-run-list/job-status-strip.tsx +25 -12
  80. package/src/components/workflow-run-list/workflow-run-filters.tsx +1 -1
  81. package/src/components/workflow-run-list/workflow-run-list-states.tsx +2 -2
  82. package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +53 -4
  83. package/src/components/workflow-run-list/workflow-run-list-view.tsx +28 -22
  84. package/src/components/workflow-run-list/workflow-run-list.stories.tsx +9 -20
  85. package/src/components/workflow-run-list/workflow-run-row.tsx +17 -13
  86. package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +2 -2
  87. package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +2 -1
  88. package/src/components/workflow-run-summary/workflow-run-summary.tsx +1 -4
  89. package/src/components/workflow-run-view/run-workspace-nav.tsx +1 -1
  90. package/src/components/workflow-run-view/workflow-run-states.tsx +1 -4
  91. package/src/components/workflow-run-view/workflow-run-view.stories.tsx +140 -0
  92. package/src/components/workflow-run-view/workflow-run-view.test.tsx +38 -7
  93. package/src/components/workflow-run-view/workflow-run-view.tsx +45 -37
  94. package/src/components/workflow-source-panel/workflow-source-content.tsx +2 -2
  95. package/src/pages/project-workflows-page.tsx +3 -2
  96. package/src/pages/workflow-job-detail-page.test.tsx +32 -0
  97. package/src/pages/workflow-job-detail-page.tsx +5 -1
  98. package/src/pages/workflow-run-detail-page.tsx +1 -1
  99. package/src/pages/workflow-run-list-page.tsx +9 -11
  100. package/src/pages/workflow-run-pages.test.tsx +26 -0
  101. package/src/routes/job-detail.tsx +1 -1
  102. package/src/routes/run-detail.tsx +1 -1
  103. package/src/routes/runs.tsx +1 -1
  104. package/src/routes/workflows.tsx +1 -0
  105. package/tsconfig.build.tsbuildinfo +1 -1
@@ -8,8 +8,14 @@ export interface StepAttemptLogPanelProps {
8
8
  /** The page scroll column used by the uncapped job-detail log surface. */
9
9
  pageScrollRef: RefObject<HTMLElement | null>;
10
10
  surfaceClassName?: string | undefined;
11
+ search?: string | undefined;
12
+ wrap?: boolean | undefined;
13
+ showLineNumbers?: boolean | undefined;
14
+ attemptId?: string | undefined;
15
+ refreshToken?: number | undefined;
16
+ onFetchingChange?: ((attemptId: string, isFetching: boolean) => void) | undefined;
11
17
  initialErrorRetryCount?: number | undefined;
12
18
  initialErrorRetryDelayMs?: number | undefined;
13
19
  }
14
- export declare function StepAttemptLogPanel({ stepId, attempt, attemptStatus, attemptStartedAt, pageScrollRef, surfaceClassName, initialErrorRetryCount, initialErrorRetryDelayMs, }: StepAttemptLogPanelProps): import("react").JSX.Element;
20
+ export declare function StepAttemptLogPanel({ stepId, attempt, attemptStatus, attemptStartedAt, pageScrollRef, surfaceClassName, search, wrap, showLineNumbers, attemptId, refreshToken, onFetchingChange, initialErrorRetryCount, initialErrorRetryDelayMs, }: StepAttemptLogPanelProps): import("react").JSX.Element;
15
21
  //# sourceMappingURL=step-attempt-log-panel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"step-attempt-log-panel.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/step-attempt-log-panel.tsx"],"names":[],"mappings":"AASA,OAAO,EAAC,KAAK,SAAS,EAAoB,MAAM,OAAO,CAAC;AAQxD,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,0EAA0E;IAC1E,aAAa,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC7C,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,wBAAwB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/C;AAED,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,OAAO,EACP,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,gBAA2C,EAC3C,sBAAsD,EACtD,wBAA2D,GAC5D,EAAE,wBAAwB,+BA8F1B"}
1
+ {"version":3,"file":"step-attempt-log-panel.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/step-attempt-log-panel.tsx"],"names":[],"mappings":"AASA,OAAO,EAAC,KAAK,SAAS,EAAoB,MAAM,OAAO,CAAC;AAQxD,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,0EAA0E;IAC1E,aAAa,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC7C,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3B,eAAe,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,gBAAgB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAClF,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,wBAAwB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/C;AAED,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,OAAO,EACP,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,gBAA2C,EAC3C,MAAW,EACX,IAAY,EACZ,eAAsB,EACtB,SAAS,EACT,YAAgB,EAChB,gBAAgB,EAChB,sBAAsD,EACtD,wBAA2D,GAC5D,EAAE,wBAAwB,+BA2H1B"}
@@ -8,8 +8,8 @@ import { JobExecutionTimeText } from './job-execution-time-text.js';
8
8
  const TAIL_FOLLOW_THRESHOLD_PX = 24;
9
9
  const INITIAL_LOG_ERROR_RETRY_COUNT = 5;
10
10
  const INITIAL_LOG_ERROR_RETRY_DELAY_MS = 1_500;
11
- const defaultLogSurfaceClasses = 'rounded-8';
12
- export function StepAttemptLogPanel({ stepId, attempt, attemptStatus, attemptStartedAt, pageScrollRef, surfaceClassName = defaultLogSurfaceClasses, initialErrorRetryCount = INITIAL_LOG_ERROR_RETRY_COUNT, initialErrorRetryDelayMs = INITIAL_LOG_ERROR_RETRY_DELAY_MS }) {
11
+ const defaultLogSurfaceClasses = 'rounded-none border-0 bg-background-contrast-base shadow-none';
12
+ export function StepAttemptLogPanel({ stepId, attempt, attemptStatus, attemptStartedAt, pageScrollRef, surfaceClassName = defaultLogSurfaceClasses, search = '', wrap = false, showLineNumbers = true, attemptId, refreshToken = 0, onFetchingChange, initialErrorRetryCount = INITIAL_LOG_ERROR_RETRY_COUNT, initialErrorRetryDelayMs = INITIAL_LOG_ERROR_RETRY_DELAY_MS }) {
13
13
  const shouldFollowTailRef = useRef(true);
14
14
  const missingStreamRetryCount = attemptStatus === 'running' ? undefined : initialErrorRetryCount;
15
15
  const retryMissingStream = attemptStatus === 'running' || isTerminalAttemptStatus(attemptStatus);
@@ -26,6 +26,23 @@ export function StepAttemptLogPanel({ stepId, attempt, attemptStatus, attemptSta
26
26
  const missingActiveStream = retryMissingStream && query.data === undefined && isMissingStepLogStreamError(query.error);
27
27
  const initialError = query.isError && query.data === undefined && !missingActiveStream;
28
28
  const staleError = query.isError && query.data !== undefined;
29
+ const lastRefreshTokenRef = useRef(refreshToken);
30
+ useEffect(()=>{
31
+ if (refreshToken === lastRefreshTokenRef.current) return;
32
+ lastRefreshTokenRef.current = refreshToken;
33
+ void query.refetchLogs();
34
+ }, [
35
+ query.refetchLogs,
36
+ refreshToken
37
+ ]);
38
+ useEffect(()=>{
39
+ if (!attemptId) return;
40
+ onFetchingChange?.(attemptId, query.isFetching);
41
+ }, [
42
+ attemptId,
43
+ onFetchingChange,
44
+ query.isFetching
45
+ ]);
29
46
  useEffect(()=>{
30
47
  const scrollElement = pageScrollRef.current;
31
48
  if (!scrollElement) return undefined;
@@ -60,7 +77,9 @@ export function StepAttemptLogPanel({ stepId, attempt, attemptStatus, attemptSta
60
77
  if (query.isPending) {
61
78
  return /*#__PURE__*/ _jsx(StepLogsLoadingSurface, {
62
79
  label: "Loading logs",
63
- className: surfaceClassName
80
+ className: surfaceClassName,
81
+ wrap: wrap,
82
+ showLineNumbers: showLineNumbers
64
83
  });
65
84
  }
66
85
  if (missingActiveStream) {
@@ -72,13 +91,15 @@ export function StepAttemptLogPanel({ stepId, attempt, attemptStatus, attemptSta
72
91
  }
73
92
  return /*#__PURE__*/ _jsx(StepLogsLoadingSurface, {
74
93
  label: "Waiting for logs",
75
- className: surfaceClassName
94
+ className: surfaceClassName,
95
+ wrap: wrap,
96
+ showLineNumbers: showLineNumbers
76
97
  });
77
98
  }
78
99
  if (initialError) {
79
100
  return /*#__PURE__*/ _jsx(StepLogsError, {
80
101
  retrying: query.isFetching,
81
- onRetry: ()=>void query.refetch()
102
+ onRetry: ()=>void query.refetchLogs()
82
103
  });
83
104
  }
84
105
  return /*#__PURE__*/ _jsxs("div", {
@@ -101,7 +122,7 @@ export function StepAttemptLogPanel({ stepId, attempt, attemptStatus, attemptSta
101
122
  size: "2xs",
102
123
  variant: "secondary",
103
124
  isLoading: query.isFetching,
104
- onClick: ()=>void query.refetch(),
125
+ onClick: ()=>void query.refetchLogs(),
105
126
  children: "Retry"
106
127
  })
107
128
  ]
@@ -109,20 +130,25 @@ export function StepAttemptLogPanel({ stepId, attempt, attemptStatus, attemptSta
109
130
  }) : null,
110
131
  /*#__PURE__*/ _jsx(LogView, {
111
132
  records: records,
133
+ search: search,
134
+ wrap: wrap,
135
+ showLineNumbers: showLineNumbers,
112
136
  emptyState: query.data?.complete ? 'complete' : 'pending',
113
137
  anchorToFailure: anchorToFailure,
114
- ariaLive: attemptStatus === 'running' ? 'polite' : 'off',
138
+ ariaLive: search.trim() ? 'off' : attemptStatus === 'running' ? 'polite' : 'off',
115
139
  className: surfaceClassName
116
140
  })
117
141
  ]
118
142
  });
119
143
  }
120
- function StepLogsLoadingSurface({ label, className }) {
144
+ function StepLogsLoadingSurface({ label, className, wrap, showLineNumbers }) {
121
145
  return /*#__PURE__*/ _jsx("div", {
122
146
  role: "status",
123
147
  "aria-label": label,
124
148
  children: /*#__PURE__*/ _jsx(LogViewSkeleton, {
125
- className: className
149
+ className: className,
150
+ wrap: wrap,
151
+ showLineNumbers: showLineNumbers
126
152
  })
127
153
  });
128
154
  }
@@ -133,7 +159,7 @@ function StepLogsWaitingSurface({ startedAt, className }) {
133
159
  className: className,
134
160
  children: /*#__PURE__*/ _jsxs(Text, {
135
161
  size: "xs",
136
- className: "px-tight py-row text-foreground-neutral-muted",
162
+ className: "px-tight py-row text-foreground-contrast-secondary",
137
163
  children: [
138
164
  "Waiting for output ·",
139
165
  ' ',
@@ -189,6 +215,7 @@ function StepLogsError({ retrying, onRetry }) {
189
215
  size: "2xs",
190
216
  variant: "secondary",
191
217
  isLoading: retrying,
218
+ disabled: retrying,
192
219
  onClick: onRetry,
193
220
  children: "Retry"
194
221
  })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/job-detail/step-attempt-log-panel.tsx"],"sourcesContent":["import {\n isMissingStepLogStreamError,\n LogView,\n LogViewSkeleton,\n useStepAttemptLogsQuery,\n} from '@shipfox/client-logs';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {type RefObject, useEffect, useRef} from 'react';\nimport {JobExecutionTimeText} from './job-execution-time-text.js';\n\nconst TAIL_FOLLOW_THRESHOLD_PX = 24;\nconst INITIAL_LOG_ERROR_RETRY_COUNT = 5;\nconst INITIAL_LOG_ERROR_RETRY_DELAY_MS = 1_500;\nconst defaultLogSurfaceClasses = 'rounded-8';\n\nexport interface StepAttemptLogPanelProps {\n stepId: string;\n attempt: number;\n attemptStatus: string;\n /** The start time used by the page's quiet waiting-for-output state. */\n attemptStartedAt?: string | undefined;\n /** The page scroll column used by the uncapped job-detail log surface. */\n pageScrollRef: RefObject<HTMLElement | null>;\n surfaceClassName?: string | undefined;\n initialErrorRetryCount?: number | undefined;\n initialErrorRetryDelayMs?: number | undefined;\n}\n\nexport function StepAttemptLogPanel({\n stepId,\n attempt,\n attemptStatus,\n attemptStartedAt,\n pageScrollRef,\n surfaceClassName = defaultLogSurfaceClasses,\n initialErrorRetryCount = INITIAL_LOG_ERROR_RETRY_COUNT,\n initialErrorRetryDelayMs = INITIAL_LOG_ERROR_RETRY_DELAY_MS,\n}: StepAttemptLogPanelProps) {\n const shouldFollowTailRef = useRef(true);\n const missingStreamRetryCount = attemptStatus === 'running' ? undefined : initialErrorRetryCount;\n const retryMissingStream = attemptStatus === 'running' || isTerminalAttemptStatus(attemptStatus);\n const query = useStepAttemptLogsQuery(stepId, attempt, {\n retryMissingStream,\n missingStreamRetryCount,\n missingStreamRetryDelayMs: initialErrorRetryDelayMs,\n initialErrorRetryCount,\n initialErrorRetryDelayMs,\n });\n const records = query.data?.records ?? [];\n const recordCount = records.length;\n const anchorToFailure = attemptStatus === 'failed';\n const missingActiveStream =\n retryMissingStream && query.data === undefined && isMissingStepLogStreamError(query.error);\n const initialError = query.isError && query.data === undefined && !missingActiveStream;\n const staleError = query.isError && query.data !== undefined;\n\n useEffect(() => {\n const scrollElement = pageScrollRef.current;\n if (!scrollElement) return undefined;\n\n const onScroll = () => {\n shouldFollowTailRef.current = isNearPageBottom(scrollElement);\n };\n scrollElement.addEventListener('scroll', onScroll, {passive: true});\n onScroll();\n return () => scrollElement.removeEventListener('scroll', onScroll);\n }, [pageScrollRef]);\n\n useEffect(() => {\n if (recordCount === 0) return undefined;\n if (anchorToFailure) return undefined;\n if (!shouldFollowTailRef.current) return undefined;\n\n const frame = scheduleAnimationFrame(() => {\n const scrollElement = pageScrollRef.current;\n if (!scrollElement) return;\n scrollElement.scrollTop = scrollElement.scrollHeight;\n });\n\n return () => {\n cancelScheduledFrame(frame);\n };\n }, [anchorToFailure, pageScrollRef, recordCount]);\n\n if (query.isPending) {\n return <StepLogsLoadingSurface label=\"Loading logs\" className={surfaceClassName} />;\n }\n\n if (missingActiveStream) {\n if (attemptStatus === 'running' && attemptStartedAt) {\n return <StepLogsWaitingSurface startedAt={attemptStartedAt} className={surfaceClassName} />;\n }\n return <StepLogsLoadingSurface label=\"Waiting for logs\" className={surfaceClassName} />;\n }\n\n if (initialError) {\n return <StepLogsError retrying={query.isFetching} onRetry={() => void query.refetch()} />;\n }\n\n return (\n <div className=\"flex min-w-0 flex-col gap-inline\">\n {staleError ? (\n <Callout\n role=\"alert\"\n type=\"warning\"\n variant=\"secondary\"\n className=\"rounded-none border-b border-border-neutral-base px-0 py-row shadow-none\"\n >\n <div className=\"flex min-w-0 flex-1 items-center justify-between gap-inline\">\n <Text size=\"xs\">Could not refresh logs.</Text>\n <Button\n type=\"button\"\n size=\"2xs\"\n variant=\"secondary\"\n isLoading={query.isFetching}\n onClick={() => void query.refetch()}\n >\n Retry\n </Button>\n </div>\n </Callout>\n ) : null}\n <LogView\n records={records}\n emptyState={query.data?.complete ? 'complete' : 'pending'}\n anchorToFailure={anchorToFailure}\n ariaLive={attemptStatus === 'running' ? 'polite' : 'off'}\n className={surfaceClassName}\n />\n </div>\n );\n}\n\nfunction StepLogsLoadingSurface({label, className}: {label: string; className: string}) {\n return (\n <div role=\"status\" aria-label={label}>\n <LogViewSkeleton className={className} />\n </div>\n );\n}\n\nfunction StepLogsWaitingSurface({startedAt, className}: {startedAt: string; className: string}) {\n return (\n <div role=\"status\" aria-label=\"Waiting for logs\" className={className}>\n <Text size=\"xs\" className=\"px-tight py-row text-foreground-neutral-muted\">\n Waiting for output ·{' '}\n <span className=\"font-code tabular-nums\" aria-hidden=\"true\">\n <JobExecutionTimeText time={{state: 'live', fromIso: startedAt}} />\n </span>\n </Text>\n </div>\n );\n}\n\nfunction scheduleAnimationFrame(callback: FrameRequestCallback): number {\n if (typeof globalThis.requestAnimationFrame === 'function') {\n return globalThis.requestAnimationFrame(callback);\n }\n return window.setTimeout(() => callback(Date.now()), 0);\n}\n\nfunction cancelScheduledFrame(frame: number) {\n if (typeof globalThis.cancelAnimationFrame === 'function') {\n globalThis.cancelAnimationFrame(frame);\n return;\n }\n window.clearTimeout(frame);\n}\n\nfunction isNearPageBottom(element: HTMLElement): boolean {\n const distanceFromBottom = element.scrollHeight - element.scrollTop - element.clientHeight;\n return distanceFromBottom <= TAIL_FOLLOW_THRESHOLD_PX;\n}\n\nfunction isTerminalAttemptStatus(status: string): boolean {\n return status === 'succeeded' || status === 'failed' || status === 'cancelled';\n}\n\nfunction StepLogsError({retrying, onRetry}: {retrying: boolean; onRetry: () => void}) {\n return (\n <Callout\n role=\"alert\"\n type=\"error\"\n variant=\"secondary\"\n className=\"rounded-none border-b border-border-neutral-base px-0 py-row shadow-none\"\n >\n <div className=\"flex min-w-0 flex-1 items-center justify-between gap-inline\">\n <Text size=\"xs\">Could not load logs.</Text>\n <Button type=\"button\" size=\"2xs\" variant=\"secondary\" isLoading={retrying} onClick={onRetry}>\n Retry\n </Button>\n </div>\n </Callout>\n );\n}\n"],"names":["isMissingStepLogStreamError","LogView","LogViewSkeleton","useStepAttemptLogsQuery","Button","Callout","Text","useEffect","useRef","JobExecutionTimeText","TAIL_FOLLOW_THRESHOLD_PX","INITIAL_LOG_ERROR_RETRY_COUNT","INITIAL_LOG_ERROR_RETRY_DELAY_MS","defaultLogSurfaceClasses","StepAttemptLogPanel","stepId","attempt","attemptStatus","attemptStartedAt","pageScrollRef","surfaceClassName","initialErrorRetryCount","initialErrorRetryDelayMs","shouldFollowTailRef","missingStreamRetryCount","undefined","retryMissingStream","isTerminalAttemptStatus","query","missingStreamRetryDelayMs","records","data","recordCount","length","anchorToFailure","missingActiveStream","error","initialError","isError","staleError","scrollElement","current","onScroll","isNearPageBottom","addEventListener","passive","removeEventListener","frame","scheduleAnimationFrame","scrollTop","scrollHeight","cancelScheduledFrame","isPending","StepLogsLoadingSurface","label","className","StepLogsWaitingSurface","startedAt","StepLogsError","retrying","isFetching","onRetry","refetch","div","role","type","variant","size","isLoading","onClick","emptyState","complete","ariaLive","aria-label","span","aria-hidden","time","state","fromIso","callback","globalThis","requestAnimationFrame","window","setTimeout","Date","now","cancelAnimationFrame","clearTimeout","element","distanceFromBottom","clientHeight","status"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/job-detail/step-attempt-log-panel.tsx"],"sourcesContent":["import {\n isMissingStepLogStreamError,\n LogView,\n LogViewSkeleton,\n useStepAttemptLogsQuery,\n} from '@shipfox/client-logs';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {type RefObject, useEffect, useRef} from 'react';\nimport {JobExecutionTimeText} from './job-execution-time-text.js';\n\nconst TAIL_FOLLOW_THRESHOLD_PX = 24;\nconst INITIAL_LOG_ERROR_RETRY_COUNT = 5;\nconst INITIAL_LOG_ERROR_RETRY_DELAY_MS = 1_500;\nconst defaultLogSurfaceClasses = 'rounded-none border-0 bg-background-contrast-base shadow-none';\n\nexport interface StepAttemptLogPanelProps {\n stepId: string;\n attempt: number;\n attemptStatus: string;\n /** The start time used by the page's quiet waiting-for-output state. */\n attemptStartedAt?: string | undefined;\n /** The page scroll column used by the uncapped job-detail log surface. */\n pageScrollRef: RefObject<HTMLElement | null>;\n surfaceClassName?: string | undefined;\n search?: string | undefined;\n wrap?: boolean | undefined;\n showLineNumbers?: boolean | undefined;\n attemptId?: string | undefined;\n refreshToken?: number | undefined;\n onFetchingChange?: ((attemptId: string, isFetching: boolean) => void) | undefined;\n initialErrorRetryCount?: number | undefined;\n initialErrorRetryDelayMs?: number | undefined;\n}\n\nexport function StepAttemptLogPanel({\n stepId,\n attempt,\n attemptStatus,\n attemptStartedAt,\n pageScrollRef,\n surfaceClassName = defaultLogSurfaceClasses,\n search = '',\n wrap = false,\n showLineNumbers = true,\n attemptId,\n refreshToken = 0,\n onFetchingChange,\n initialErrorRetryCount = INITIAL_LOG_ERROR_RETRY_COUNT,\n initialErrorRetryDelayMs = INITIAL_LOG_ERROR_RETRY_DELAY_MS,\n}: StepAttemptLogPanelProps) {\n const shouldFollowTailRef = useRef(true);\n const missingStreamRetryCount = attemptStatus === 'running' ? undefined : initialErrorRetryCount;\n const retryMissingStream = attemptStatus === 'running' || isTerminalAttemptStatus(attemptStatus);\n const query = useStepAttemptLogsQuery(stepId, attempt, {\n retryMissingStream,\n missingStreamRetryCount,\n missingStreamRetryDelayMs: initialErrorRetryDelayMs,\n initialErrorRetryCount,\n initialErrorRetryDelayMs,\n });\n const records = query.data?.records ?? [];\n const recordCount = records.length;\n const anchorToFailure = attemptStatus === 'failed';\n const missingActiveStream =\n retryMissingStream && query.data === undefined && isMissingStepLogStreamError(query.error);\n const initialError = query.isError && query.data === undefined && !missingActiveStream;\n const staleError = query.isError && query.data !== undefined;\n const lastRefreshTokenRef = useRef(refreshToken);\n\n useEffect(() => {\n if (refreshToken === lastRefreshTokenRef.current) return;\n lastRefreshTokenRef.current = refreshToken;\n void query.refetchLogs();\n }, [query.refetchLogs, refreshToken]);\n\n useEffect(() => {\n if (!attemptId) return;\n onFetchingChange?.(attemptId, query.isFetching);\n }, [attemptId, onFetchingChange, query.isFetching]);\n\n useEffect(() => {\n const scrollElement = pageScrollRef.current;\n if (!scrollElement) return undefined;\n\n const onScroll = () => {\n shouldFollowTailRef.current = isNearPageBottom(scrollElement);\n };\n scrollElement.addEventListener('scroll', onScroll, {passive: true});\n onScroll();\n return () => scrollElement.removeEventListener('scroll', onScroll);\n }, [pageScrollRef]);\n\n useEffect(() => {\n if (recordCount === 0) return undefined;\n if (anchorToFailure) return undefined;\n if (!shouldFollowTailRef.current) return undefined;\n\n const frame = scheduleAnimationFrame(() => {\n const scrollElement = pageScrollRef.current;\n if (!scrollElement) return;\n scrollElement.scrollTop = scrollElement.scrollHeight;\n });\n\n return () => {\n cancelScheduledFrame(frame);\n };\n }, [anchorToFailure, pageScrollRef, recordCount]);\n\n if (query.isPending) {\n return (\n <StepLogsLoadingSurface\n label=\"Loading logs\"\n className={surfaceClassName}\n wrap={wrap}\n showLineNumbers={showLineNumbers}\n />\n );\n }\n\n if (missingActiveStream) {\n if (attemptStatus === 'running' && attemptStartedAt) {\n return <StepLogsWaitingSurface startedAt={attemptStartedAt} className={surfaceClassName} />;\n }\n return (\n <StepLogsLoadingSurface\n label=\"Waiting for logs\"\n className={surfaceClassName}\n wrap={wrap}\n showLineNumbers={showLineNumbers}\n />\n );\n }\n\n if (initialError) {\n return <StepLogsError retrying={query.isFetching} onRetry={() => void query.refetchLogs()} />;\n }\n\n return (\n <div className=\"flex min-w-0 flex-col gap-inline\">\n {staleError ? (\n <Callout\n role=\"alert\"\n type=\"warning\"\n variant=\"secondary\"\n className=\"rounded-none border-b border-border-neutral-base px-0 py-row shadow-none\"\n >\n <div className=\"flex min-w-0 flex-1 items-center justify-between gap-inline\">\n <Text size=\"xs\">Could not refresh logs.</Text>\n <Button\n type=\"button\"\n size=\"2xs\"\n variant=\"secondary\"\n isLoading={query.isFetching}\n onClick={() => void query.refetchLogs()}\n >\n Retry\n </Button>\n </div>\n </Callout>\n ) : null}\n <LogView\n records={records}\n search={search}\n wrap={wrap}\n showLineNumbers={showLineNumbers}\n emptyState={query.data?.complete ? 'complete' : 'pending'}\n anchorToFailure={anchorToFailure}\n ariaLive={search.trim() ? 'off' : attemptStatus === 'running' ? 'polite' : 'off'}\n className={surfaceClassName}\n />\n </div>\n );\n}\n\nfunction StepLogsLoadingSurface({\n label,\n className,\n wrap,\n showLineNumbers,\n}: {\n label: string;\n className: string;\n wrap: boolean;\n showLineNumbers: boolean;\n}) {\n return (\n <div role=\"status\" aria-label={label}>\n <LogViewSkeleton className={className} wrap={wrap} showLineNumbers={showLineNumbers} />\n </div>\n );\n}\n\nfunction StepLogsWaitingSurface({startedAt, className}: {startedAt: string; className: string}) {\n return (\n <div role=\"status\" aria-label=\"Waiting for logs\" className={className}>\n <Text size=\"xs\" className=\"px-tight py-row text-foreground-contrast-secondary\">\n Waiting for output ·{' '}\n <span className=\"font-code tabular-nums\" aria-hidden=\"true\">\n <JobExecutionTimeText time={{state: 'live', fromIso: startedAt}} />\n </span>\n </Text>\n </div>\n );\n}\n\nfunction scheduleAnimationFrame(callback: FrameRequestCallback): number {\n if (typeof globalThis.requestAnimationFrame === 'function') {\n return globalThis.requestAnimationFrame(callback);\n }\n return window.setTimeout(() => callback(Date.now()), 0);\n}\n\nfunction cancelScheduledFrame(frame: number) {\n if (typeof globalThis.cancelAnimationFrame === 'function') {\n globalThis.cancelAnimationFrame(frame);\n return;\n }\n window.clearTimeout(frame);\n}\n\nfunction isNearPageBottom(element: HTMLElement): boolean {\n const distanceFromBottom = element.scrollHeight - element.scrollTop - element.clientHeight;\n return distanceFromBottom <= TAIL_FOLLOW_THRESHOLD_PX;\n}\n\nfunction isTerminalAttemptStatus(status: string): boolean {\n return status === 'succeeded' || status === 'failed' || status === 'cancelled';\n}\n\nfunction StepLogsError({retrying, onRetry}: {retrying: boolean; onRetry: () => void}) {\n return (\n <Callout\n role=\"alert\"\n type=\"error\"\n variant=\"secondary\"\n className=\"rounded-none border-b border-border-neutral-base px-0 py-row shadow-none\"\n >\n <div className=\"flex min-w-0 flex-1 items-center justify-between gap-inline\">\n <Text size=\"xs\">Could not load logs.</Text>\n <Button\n type=\"button\"\n size=\"2xs\"\n variant=\"secondary\"\n isLoading={retrying}\n disabled={retrying}\n onClick={onRetry}\n >\n Retry\n </Button>\n </div>\n </Callout>\n );\n}\n"],"names":["isMissingStepLogStreamError","LogView","LogViewSkeleton","useStepAttemptLogsQuery","Button","Callout","Text","useEffect","useRef","JobExecutionTimeText","TAIL_FOLLOW_THRESHOLD_PX","INITIAL_LOG_ERROR_RETRY_COUNT","INITIAL_LOG_ERROR_RETRY_DELAY_MS","defaultLogSurfaceClasses","StepAttemptLogPanel","stepId","attempt","attemptStatus","attemptStartedAt","pageScrollRef","surfaceClassName","search","wrap","showLineNumbers","attemptId","refreshToken","onFetchingChange","initialErrorRetryCount","initialErrorRetryDelayMs","shouldFollowTailRef","missingStreamRetryCount","undefined","retryMissingStream","isTerminalAttemptStatus","query","missingStreamRetryDelayMs","records","data","recordCount","length","anchorToFailure","missingActiveStream","error","initialError","isError","staleError","lastRefreshTokenRef","current","refetchLogs","isFetching","scrollElement","onScroll","isNearPageBottom","addEventListener","passive","removeEventListener","frame","scheduleAnimationFrame","scrollTop","scrollHeight","cancelScheduledFrame","isPending","StepLogsLoadingSurface","label","className","StepLogsWaitingSurface","startedAt","StepLogsError","retrying","onRetry","div","role","type","variant","size","isLoading","onClick","emptyState","complete","ariaLive","trim","aria-label","span","aria-hidden","time","state","fromIso","callback","globalThis","requestAnimationFrame","window","setTimeout","Date","now","cancelAnimationFrame","clearTimeout","element","distanceFromBottom","clientHeight","status","disabled"],"mappings":"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"}
@@ -33,4 +33,5 @@ export declare function defaultStepListJobExecution(job: Job): JobExecution;
33
33
  export declare function emptyJobExecutionForJob(job: Job): JobExecution;
34
34
  export declare function getStepStatusVisual(status: string): StepStatusVisual;
35
35
  export declare function humanizeStatus(status: string): string;
36
+ export declare function stepLabel(step: Step, index: number): string;
36
37
  //# sourceMappingURL=step-list-model.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"step-list-model.d.ts","sourceRoot":"","sources":["../../../src/components/step-list/step-list-model.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAGL,KAAK,GAAG,EACR,YAAY,EACZ,KAAK,IAAI,EACT,KAAK,WAAW,EAChB,KAAK,0BAA0B,EAChC,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACnD,eAAe,EAAE,0BAA0B,GAAG,IAAI,CAAC;IACnD,YAAY,EAAE,gBAAgB,CAAC;IAC/B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,SAAU,SAAQ,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAmB,SAAQ,gBAAgB;IAC1D,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,OAAO,EAAE,kBAAkB,EAAE,CAAC;CAC/B;AAED,wBAAgB,kBAAkB,CAAC,EACjC,GAAG,EACH,YAAY,GACb,EAAE;IACD,GAAG,EAAE,GAAG,CAAC;IACT,YAAY,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;CACzC,GAAG,aAAa,CAehB;AAED,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,GAAG,GAAG,YAAY,CAElE;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,GAAG,GAAG,YAAY,CAqB9D;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAepE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKrD"}
1
+ {"version":3,"file":"step-list-model.d.ts","sourceRoot":"","sources":["../../../src/components/step-list/step-list-model.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAGL,KAAK,GAAG,EACR,YAAY,EACZ,KAAK,IAAI,EACT,KAAK,WAAW,EAChB,KAAK,0BAA0B,EAChC,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACnD,eAAe,EAAE,0BAA0B,GAAG,IAAI,CAAC;IACnD,YAAY,EAAE,gBAAgB,CAAC;IAC/B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,SAAU,SAAQ,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAmB,SAAQ,gBAAgB;IAC1D,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,OAAO,EAAE,kBAAkB,EAAE,CAAC;CAC/B;AAED,wBAAgB,kBAAkB,CAAC,EACjC,GAAG,EACH,YAAY,GACb,EAAE;IACD,GAAG,EAAE,GAAG,CAAC;IACT,YAAY,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;CACzC,GAAG,aAAa,CAehB;AAED,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,GAAG,GAAG,YAAY,CAElE;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,GAAG,GAAG,YAAY,CAqB9D;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAepE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKrD;AAkBD,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ3D"}
@@ -79,7 +79,7 @@ function toStepModel(step, index) {
79
79
  attempts
80
80
  };
81
81
  }
82
- function stepLabel(step, index) {
82
+ export function stepLabel(step, index) {
83
83
  const name = step.name.trim();
84
84
  if (name) return name;
85
85
  const key = step.key?.trim();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/step-list/step-list-model.ts"],"sourcesContent":["import {\n getWorkflowStatusVisual,\n type WorkflowStatusVisual,\n} from '#components/workflow-status/status-visuals.js';\nimport {\n defaultJobExecution,\n isWorkflowStatus as isKnownWorkflowStatus,\n type Job,\n JobExecution,\n type Step,\n type StepAttempt,\n type StepAttemptDisplayDuration,\n} from '#core/workflow-run.js';\n\nexport interface StepStatusVisual extends Omit<WorkflowStatusVisual, 'kind'> {\n kind: string;\n ripple: boolean;\n}\n\nexport interface StepAttemptModel extends StepAttempt {\n displayDuration: StepAttemptDisplayDuration | null;\n statusVisual: StepStatusVisual;\n carriedOver: boolean;\n}\n\nexport interface StepModel extends Omit<Step, 'attempts'> {\n index: number;\n label: string;\n attempts: StepAttemptModel[];\n}\n\nexport interface StepListEntryModel extends StepAttemptModel {\n step: StepModel;\n}\n\nexport interface StepListModel {\n jobId: string;\n jobName: string;\n jobExecutionId: string;\n stepCount: number;\n activeEntryId: string | undefined;\n entries: StepListEntryModel[];\n}\n\nexport function buildStepListModel({\n job,\n jobExecution,\n}: {\n job: Job;\n jobExecution?: JobExecution | undefined;\n}): StepListModel {\n const selectedJobExecution = jobExecution ?? defaultStepListJobExecution(job);\n const steps = [...selectedJobExecution.steps].sort(compareSteps).map(toStepModel);\n const entries = steps\n .flatMap((step) => toStepEntries(step, job.carriedOver))\n .sort(compareEntries);\n\n return {\n jobId: job.id,\n jobName: job.name ?? job.key,\n jobExecutionId: selectedJobExecution.id,\n stepCount: steps.length,\n activeEntryId: landingEntryId(entries),\n entries,\n };\n}\n\nexport function defaultStepListJobExecution(job: Job): JobExecution {\n return defaultJobExecution(job) ?? emptyJobExecutionForJob(job);\n}\n\nexport function emptyJobExecutionForJob(job: Job): JobExecution {\n return new JobExecution({\n id: `missing:${job.id}`,\n jobId: job.id,\n sequence: 1,\n name: job.name ?? job.key,\n status: job.status === 'skipped' ? 'cancelled' : job.status,\n statusReason: job.statusReason,\n statusReasonMessage: null,\n runner: job.runner,\n outputs: job.outputs,\n triggerEvents: [],\n queuedAt: null,\n startedAt: null,\n finishedAt: null,\n timedOutAt: null,\n evaluationTrace: job.evaluationTrace,\n createdAt: job.createdAt,\n updatedAt: job.updatedAt,\n steps: [],\n });\n}\n\nexport function getStepStatusVisual(status: string): StepStatusVisual {\n const normalized = normalizeStatus(status);\n\n if (isKnownWorkflowStatus(normalized)) {\n const visual = getWorkflowStatusVisual(normalized);\n return {...visual, ripple: normalized === 'running'};\n }\n\n return {\n kind: status,\n label: humanizeStatus(status),\n dot: 'neutral',\n badge: 'neutral',\n ripple: false,\n };\n}\n\nexport function humanizeStatus(status: string): string {\n const words = status.trim().replace(/[_-]+/g, ' ').replace(/\\s+/g, ' ').toLowerCase();\n if (!words) return 'Unknown';\n const firstLetter = words.at(0);\n return firstLetter === undefined ? 'Unknown' : firstLetter.toUpperCase() + words.slice(1);\n}\n\nfunction toStepModel(step: Step, index: number): StepModel {\n const attempts = [...step.attempts].sort(compareAttempts).map((attempt) => ({\n ...attempt,\n displayDuration: attempt.displayDuration,\n statusVisual: getStepStatusVisual(attempt.status),\n carriedOver: false,\n }));\n\n return {\n ...step,\n index: index + 1,\n label: stepLabel(step, index),\n attempts,\n };\n}\n\nfunction stepLabel(step: Step, index: number): string {\n const name = step.name.trim();\n if (name) return name;\n\n const key = step.key?.trim();\n if (key) return key;\n\n return `Step ${index + 1}`;\n}\n\nfunction toStepEntries(step: StepModel, carriedOverJob: boolean): StepListEntryModel[] {\n if (step.attempts.length > 0) {\n return step.attempts.map((attempt) => ({\n ...attempt,\n step,\n }));\n }\n\n if (!carriedOverJob) return [];\n\n return [\n {\n id: `carried-over:${step.id}`,\n stepId: step.id,\n jobExecutionId: step.jobExecutionId,\n attempt: step.currentAttempt,\n executionOrder: step.position,\n status: step.status,\n exitCode: null,\n output: null,\n outputs: null,\n response: null,\n error: null,\n gateResult: null,\n restartFeedback: null,\n startedAt: step.createdAt,\n finishedAt: null,\n displayDuration: null,\n statusVisual: getStepStatusVisual(step.status),\n carriedOver: true,\n step,\n },\n ];\n}\n\nfunction compareSteps(left: Step, right: Step): number {\n return (\n left.position - right.position ||\n (left.name ?? '').localeCompare(right.name ?? '') ||\n left.id.localeCompare(right.id)\n );\n}\n\nfunction compareAttempts(left: Step['attempts'][number], right: Step['attempts'][number]) {\n return left.attempt - right.attempt || left.id.localeCompare(right.id);\n}\n\nfunction compareEntries(left: StepListEntryModel, right: StepListEntryModel): number {\n return (\n left.executionOrder - right.executionOrder ||\n left.step.index - right.step.index ||\n left.attempt - right.attempt ||\n left.id.localeCompare(right.id)\n );\n}\n\nfunction landingEntryId(entries: readonly StepListEntryModel[]): string | undefined {\n for (let index = entries.length - 1; index >= 0; index -= 1) {\n const entry = entries[index];\n if (entry?.statusVisual.kind === 'running') return entry.id;\n }\n return undefined;\n}\n\nfunction normalizeStatus(status: string): string {\n return status.trim().toLowerCase().replace(/-/g, '_');\n}\n"],"names":["getWorkflowStatusVisual","defaultJobExecution","isWorkflowStatus","isKnownWorkflowStatus","JobExecution","buildStepListModel","job","jobExecution","selectedJobExecution","defaultStepListJobExecution","steps","sort","compareSteps","map","toStepModel","entries","flatMap","step","toStepEntries","carriedOver","compareEntries","jobId","id","jobName","name","key","jobExecutionId","stepCount","length","activeEntryId","landingEntryId","emptyJobExecutionForJob","sequence","status","statusReason","statusReasonMessage","runner","outputs","triggerEvents","queuedAt","startedAt","finishedAt","timedOutAt","evaluationTrace","createdAt","updatedAt","getStepStatusVisual","normalized","normalizeStatus","visual","ripple","kind","label","humanizeStatus","dot","badge","words","trim","replace","toLowerCase","firstLetter","at","undefined","toUpperCase","slice","index","attempts","compareAttempts","attempt","displayDuration","statusVisual","stepLabel","carriedOverJob","stepId","currentAttempt","executionOrder","position","exitCode","output","response","error","gateResult","restartFeedback","left","right","localeCompare","entry"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/step-list/step-list-model.ts"],"sourcesContent":["import {\n getWorkflowStatusVisual,\n type WorkflowStatusVisual,\n} from '#components/workflow-status/status-visuals.js';\nimport {\n defaultJobExecution,\n isWorkflowStatus as isKnownWorkflowStatus,\n type Job,\n JobExecution,\n type Step,\n type StepAttempt,\n type StepAttemptDisplayDuration,\n} from '#core/workflow-run.js';\n\nexport interface StepStatusVisual extends Omit<WorkflowStatusVisual, 'kind'> {\n kind: string;\n ripple: boolean;\n}\n\nexport interface StepAttemptModel extends StepAttempt {\n displayDuration: StepAttemptDisplayDuration | null;\n statusVisual: StepStatusVisual;\n carriedOver: boolean;\n}\n\nexport interface StepModel extends Omit<Step, 'attempts'> {\n index: number;\n label: string;\n attempts: StepAttemptModel[];\n}\n\nexport interface StepListEntryModel extends StepAttemptModel {\n step: StepModel;\n}\n\nexport interface StepListModel {\n jobId: string;\n jobName: string;\n jobExecutionId: string;\n stepCount: number;\n activeEntryId: string | undefined;\n entries: StepListEntryModel[];\n}\n\nexport function buildStepListModel({\n job,\n jobExecution,\n}: {\n job: Job;\n jobExecution?: JobExecution | undefined;\n}): StepListModel {\n const selectedJobExecution = jobExecution ?? defaultStepListJobExecution(job);\n const steps = [...selectedJobExecution.steps].sort(compareSteps).map(toStepModel);\n const entries = steps\n .flatMap((step) => toStepEntries(step, job.carriedOver))\n .sort(compareEntries);\n\n return {\n jobId: job.id,\n jobName: job.name ?? job.key,\n jobExecutionId: selectedJobExecution.id,\n stepCount: steps.length,\n activeEntryId: landingEntryId(entries),\n entries,\n };\n}\n\nexport function defaultStepListJobExecution(job: Job): JobExecution {\n return defaultJobExecution(job) ?? emptyJobExecutionForJob(job);\n}\n\nexport function emptyJobExecutionForJob(job: Job): JobExecution {\n return new JobExecution({\n id: `missing:${job.id}`,\n jobId: job.id,\n sequence: 1,\n name: job.name ?? job.key,\n status: job.status === 'skipped' ? 'cancelled' : job.status,\n statusReason: job.statusReason,\n statusReasonMessage: null,\n runner: job.runner,\n outputs: job.outputs,\n triggerEvents: [],\n queuedAt: null,\n startedAt: null,\n finishedAt: null,\n timedOutAt: null,\n evaluationTrace: job.evaluationTrace,\n createdAt: job.createdAt,\n updatedAt: job.updatedAt,\n steps: [],\n });\n}\n\nexport function getStepStatusVisual(status: string): StepStatusVisual {\n const normalized = normalizeStatus(status);\n\n if (isKnownWorkflowStatus(normalized)) {\n const visual = getWorkflowStatusVisual(normalized);\n return {...visual, ripple: normalized === 'running'};\n }\n\n return {\n kind: status,\n label: humanizeStatus(status),\n dot: 'neutral',\n badge: 'neutral',\n ripple: false,\n };\n}\n\nexport function humanizeStatus(status: string): string {\n const words = status.trim().replace(/[_-]+/g, ' ').replace(/\\s+/g, ' ').toLowerCase();\n if (!words) return 'Unknown';\n const firstLetter = words.at(0);\n return firstLetter === undefined ? 'Unknown' : firstLetter.toUpperCase() + words.slice(1);\n}\n\nfunction toStepModel(step: Step, index: number): StepModel {\n const attempts = [...step.attempts].sort(compareAttempts).map((attempt) => ({\n ...attempt,\n displayDuration: attempt.displayDuration,\n statusVisual: getStepStatusVisual(attempt.status),\n carriedOver: false,\n }));\n\n return {\n ...step,\n index: index + 1,\n label: stepLabel(step, index),\n attempts,\n };\n}\n\nexport function stepLabel(step: Step, index: number): string {\n const name = step.name.trim();\n if (name) return name;\n\n const key = step.key?.trim();\n if (key) return key;\n\n return `Step ${index + 1}`;\n}\n\nfunction toStepEntries(step: StepModel, carriedOverJob: boolean): StepListEntryModel[] {\n if (step.attempts.length > 0) {\n return step.attempts.map((attempt) => ({\n ...attempt,\n step,\n }));\n }\n\n if (!carriedOverJob) return [];\n\n return [\n {\n id: `carried-over:${step.id}`,\n stepId: step.id,\n jobExecutionId: step.jobExecutionId,\n attempt: step.currentAttempt,\n executionOrder: step.position,\n status: step.status,\n exitCode: null,\n output: null,\n outputs: null,\n response: null,\n error: null,\n gateResult: null,\n restartFeedback: null,\n startedAt: step.createdAt,\n finishedAt: null,\n displayDuration: null,\n statusVisual: getStepStatusVisual(step.status),\n carriedOver: true,\n step,\n },\n ];\n}\n\nfunction compareSteps(left: Step, right: Step): number {\n return (\n left.position - right.position ||\n (left.name ?? '').localeCompare(right.name ?? '') ||\n left.id.localeCompare(right.id)\n );\n}\n\nfunction compareAttempts(left: Step['attempts'][number], right: Step['attempts'][number]) {\n return left.attempt - right.attempt || left.id.localeCompare(right.id);\n}\n\nfunction compareEntries(left: StepListEntryModel, right: StepListEntryModel): number {\n return (\n left.executionOrder - right.executionOrder ||\n left.step.index - right.step.index ||\n left.attempt - right.attempt ||\n left.id.localeCompare(right.id)\n );\n}\n\nfunction landingEntryId(entries: readonly StepListEntryModel[]): string | undefined {\n for (let index = entries.length - 1; index >= 0; index -= 1) {\n const entry = entries[index];\n if (entry?.statusVisual.kind === 'running') return entry.id;\n }\n return undefined;\n}\n\nfunction normalizeStatus(status: string): string {\n return status.trim().toLowerCase().replace(/-/g, '_');\n}\n"],"names":["getWorkflowStatusVisual","defaultJobExecution","isWorkflowStatus","isKnownWorkflowStatus","JobExecution","buildStepListModel","job","jobExecution","selectedJobExecution","defaultStepListJobExecution","steps","sort","compareSteps","map","toStepModel","entries","flatMap","step","toStepEntries","carriedOver","compareEntries","jobId","id","jobName","name","key","jobExecutionId","stepCount","length","activeEntryId","landingEntryId","emptyJobExecutionForJob","sequence","status","statusReason","statusReasonMessage","runner","outputs","triggerEvents","queuedAt","startedAt","finishedAt","timedOutAt","evaluationTrace","createdAt","updatedAt","getStepStatusVisual","normalized","normalizeStatus","visual","ripple","kind","label","humanizeStatus","dot","badge","words","trim","replace","toLowerCase","firstLetter","at","undefined","toUpperCase","slice","index","attempts","compareAttempts","attempt","displayDuration","statusVisual","stepLabel","carriedOverJob","stepId","currentAttempt","executionOrder","position","exitCode","output","response","error","gateResult","restartFeedback","left","right","localeCompare","entry"],"mappings":"AAAA,SACEA,uBAAuB,QAElB,gDAAgD;AACvD,SACEC,mBAAmB,EACnBC,oBAAoBC,qBAAqB,EAEzCC,YAAY,QAIP,wBAAwB;AAgC/B,OAAO,SAASC,mBAAmB,EACjCC,GAAG,EACHC,YAAY,EAIb;IACC,MAAMC,uBAAuBD,gBAAgBE,4BAA4BH;IACzE,MAAMI,QAAQ;WAAIF,qBAAqBE,KAAK;KAAC,CAACC,IAAI,CAACC,cAAcC,GAAG,CAACC;IACrE,MAAMC,UAAUL,MACbM,OAAO,CAAC,CAACC,OAASC,cAAcD,MAAMX,IAAIa,WAAW,GACrDR,IAAI,CAACS;IAER,OAAO;QACLC,OAAOf,IAAIgB,EAAE;QACbC,SAASjB,IAAIkB,IAAI,IAAIlB,IAAImB,GAAG;QAC5BC,gBAAgBlB,qBAAqBc,EAAE;QACvCK,WAAWjB,MAAMkB,MAAM;QACvBC,eAAeC,eAAef;QAC9BA;IACF;AACF;AAEA,OAAO,SAASN,4BAA4BH,GAAQ;IAClD,OAAOL,oBAAoBK,QAAQyB,wBAAwBzB;AAC7D;AAEA,OAAO,SAASyB,wBAAwBzB,GAAQ;IAC9C,OAAO,IAAIF,aAAa;QACtBkB,IAAI,CAAC,QAAQ,EAAEhB,IAAIgB,EAAE,EAAE;QACvBD,OAAOf,IAAIgB,EAAE;QACbU,UAAU;QACVR,MAAMlB,IAAIkB,IAAI,IAAIlB,IAAImB,GAAG;QACzBQ,QAAQ3B,IAAI2B,MAAM,KAAK,YAAY,cAAc3B,IAAI2B,MAAM;QAC3DC,cAAc5B,IAAI4B,YAAY;QAC9BC,qBAAqB;QACrBC,QAAQ9B,IAAI8B,MAAM;QAClBC,SAAS/B,IAAI+B,OAAO;QACpBC,eAAe,EAAE;QACjBC,UAAU;QACVC,WAAW;QACXC,YAAY;QACZC,YAAY;QACZC,iBAAiBrC,IAAIqC,eAAe;QACpCC,WAAWtC,IAAIsC,SAAS;QACxBC,WAAWvC,IAAIuC,SAAS;QACxBnC,OAAO,EAAE;IACX;AACF;AAEA,OAAO,SAASoC,oBAAoBb,MAAc;IAChD,MAAMc,aAAaC,gBAAgBf;IAEnC,IAAI9B,sBAAsB4C,aAAa;QACrC,MAAME,SAASjD,wBAAwB+C;QACvC,OAAO;YAAC,GAAGE,MAAM;YAAEC,QAAQH,eAAe;QAAS;IACrD;IAEA,OAAO;QACLI,MAAMlB;QACNmB,OAAOC,eAAepB;QACtBqB,KAAK;QACLC,OAAO;QACPL,QAAQ;IACV;AACF;AAEA,OAAO,SAASG,eAAepB,MAAc;IAC3C,MAAMuB,QAAQvB,OAAOwB,IAAI,GAAGC,OAAO,CAAC,UAAU,KAAKA,OAAO,CAAC,QAAQ,KAAKC,WAAW;IACnF,IAAI,CAACH,OAAO,OAAO;IACnB,MAAMI,cAAcJ,MAAMK,EAAE,CAAC;IAC7B,OAAOD,gBAAgBE,YAAY,YAAYF,YAAYG,WAAW,KAAKP,MAAMQ,KAAK,CAAC;AACzF;AAEA,SAASlD,YAAYG,IAAU,EAAEgD,KAAa;IAC5C,MAAMC,WAAW;WAAIjD,KAAKiD,QAAQ;KAAC,CAACvD,IAAI,CAACwD,iBAAiBtD,GAAG,CAAC,CAACuD,UAAa,CAAA;YAC1E,GAAGA,OAAO;YACVC,iBAAiBD,QAAQC,eAAe;YACxCC,cAAcxB,oBAAoBsB,QAAQnC,MAAM;YAChDd,aAAa;QACf,CAAA;IAEA,OAAO;QACL,GAAGF,IAAI;QACPgD,OAAOA,QAAQ;QACfb,OAAOmB,UAAUtD,MAAMgD;QACvBC;IACF;AACF;AAEA,OAAO,SAASK,UAAUtD,IAAU,EAAEgD,KAAa;IACjD,MAAMzC,OAAOP,KAAKO,IAAI,CAACiC,IAAI;IAC3B,IAAIjC,MAAM,OAAOA;IAEjB,MAAMC,MAAMR,KAAKQ,GAAG,EAAEgC;IACtB,IAAIhC,KAAK,OAAOA;IAEhB,OAAO,CAAC,KAAK,EAAEwC,QAAQ,GAAG;AAC5B;AAEA,SAAS/C,cAAcD,IAAe,EAAEuD,cAAuB;IAC7D,IAAIvD,KAAKiD,QAAQ,CAACtC,MAAM,GAAG,GAAG;QAC5B,OAAOX,KAAKiD,QAAQ,CAACrD,GAAG,CAAC,CAACuD,UAAa,CAAA;gBACrC,GAAGA,OAAO;gBACVnD;YACF,CAAA;IACF;IAEA,IAAI,CAACuD,gBAAgB,OAAO,EAAE;IAE9B,OAAO;QACL;YACElD,IAAI,CAAC,aAAa,EAAEL,KAAKK,EAAE,EAAE;YAC7BmD,QAAQxD,KAAKK,EAAE;YACfI,gBAAgBT,KAAKS,cAAc;YACnC0C,SAASnD,KAAKyD,cAAc;YAC5BC,gBAAgB1D,KAAK2D,QAAQ;YAC7B3C,QAAQhB,KAAKgB,MAAM;YACnB4C,UAAU;YACVC,QAAQ;YACRzC,SAAS;YACT0C,UAAU;YACVC,OAAO;YACPC,YAAY;YACZC,iBAAiB;YACjB1C,WAAWvB,KAAK2B,SAAS;YACzBH,YAAY;YACZ4B,iBAAiB;YACjBC,cAAcxB,oBAAoB7B,KAAKgB,MAAM;YAC7Cd,aAAa;YACbF;QACF;KACD;AACH;AAEA,SAASL,aAAauE,IAAU,EAAEC,KAAW;IAC3C,OACED,KAAKP,QAAQ,GAAGQ,MAAMR,QAAQ,IAC9B,AAACO,CAAAA,KAAK3D,IAAI,IAAI,EAAC,EAAG6D,aAAa,CAACD,MAAM5D,IAAI,IAAI,OAC9C2D,KAAK7D,EAAE,CAAC+D,aAAa,CAACD,MAAM9D,EAAE;AAElC;AAEA,SAAS6C,gBAAgBgB,IAA8B,EAAEC,KAA+B;IACtF,OAAOD,KAAKf,OAAO,GAAGgB,MAAMhB,OAAO,IAAIe,KAAK7D,EAAE,CAAC+D,aAAa,CAACD,MAAM9D,EAAE;AACvE;AAEA,SAASF,eAAe+D,IAAwB,EAAEC,KAAyB;IACzE,OACED,KAAKR,cAAc,GAAGS,MAAMT,cAAc,IAC1CQ,KAAKlE,IAAI,CAACgD,KAAK,GAAGmB,MAAMnE,IAAI,CAACgD,KAAK,IAClCkB,KAAKf,OAAO,GAAGgB,MAAMhB,OAAO,IAC5Be,KAAK7D,EAAE,CAAC+D,aAAa,CAACD,MAAM9D,EAAE;AAElC;AAEA,SAASQ,eAAef,OAAsC;IAC5D,IAAK,IAAIkD,QAAQlD,QAAQa,MAAM,GAAG,GAAGqC,SAAS,GAAGA,SAAS,EAAG;QAC3D,MAAMqB,QAAQvE,OAAO,CAACkD,MAAM;QAC5B,IAAIqB,OAAOhB,aAAanB,SAAS,WAAW,OAAOmC,MAAMhE,EAAE;IAC7D;IACA,OAAOwC;AACT;AAEA,SAASd,gBAAgBf,MAAc;IACrC,OAAOA,OAAOwB,IAAI,GAAGE,WAAW,GAAGD,OAAO,CAAC,MAAM;AACnD"}
@@ -24,6 +24,7 @@ export interface StepListProps {
24
24
  selectedAttemptId?: string | null | undefined;
25
25
  defaultSelectedAttemptId?: string | undefined;
26
26
  onSelectedAttemptChange?: ((attemptId: string | undefined) => void) | undefined;
27
+ onExpandedAttemptIdsChange?: ((attemptIds: readonly string[]) => void) | undefined;
27
28
  inspectorOpenAttemptId?: string | null | undefined;
28
29
  onInspectorOpenChange?: ((attemptId: string | null) => void) | undefined;
29
30
  autoSelectActiveAttempt?: boolean | undefined;
@@ -33,5 +34,5 @@ export interface StepListProps {
33
34
  showHeader?: boolean | undefined;
34
35
  className?: string | undefined;
35
36
  }
36
- export declare function StepList({ job, jobExecution, selectedAttemptId, defaultSelectedAttemptId, onSelectedAttemptChange, inspectorOpenAttemptId, onInspectorOpenChange, autoSelectActiveAttempt, emptyState, renderExpandedStep, renderInspector, showHeader, className, }: StepListProps): import("react").JSX.Element;
37
+ export declare function StepList({ job, jobExecution, selectedAttemptId, defaultSelectedAttemptId, onSelectedAttemptChange, onExpandedAttemptIdsChange, inspectorOpenAttemptId, onInspectorOpenChange, autoSelectActiveAttempt, emptyState, renderExpandedStep, renderInspector, showHeader, className, }: StepListProps): import("react").JSX.Element;
37
38
  //# sourceMappingURL=step-list.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"step-list.d.ts","sourceRoot":"","sources":["../../../src/components/step-list/step-list.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAGrC,OAAO,EAEL,KAAK,GAAG,EACR,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,IAAI,EACT,KAAK,kBAAkB,EACxB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAKL,KAAK,kBAAkB,EAExB,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,GAAG,CAAC;IACT,YAAY,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IACxC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC9C,wBAAwB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9C,uBAAuB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAChF,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACnD,qBAAqB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACzE,uBAAuB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9C,UAAU,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAC5C,kBAAkB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,mBAAmB,KAAK,SAAS,CAAC,GAAG,SAAS,CAAC;IAC/E,eAAe,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,kBAAkB,KAAK,SAAS,CAAC,GAAG,SAAS,CAAC;IACzE,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,wBAAgB,QAAQ,CAAC,EACvB,GAAG,EACH,YAAY,EACZ,iBAAiB,EACjB,wBAAwB,EACxB,uBAAuB,EACvB,sBAA6B,EAC7B,qBAAqB,EACrB,uBAA+B,EAC/B,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,UAAiB,EACjB,SAAS,GACV,EAAE,aAAa,+BAwBf"}
1
+ {"version":3,"file":"step-list.d.ts","sourceRoot":"","sources":["../../../src/components/step-list/step-list.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAGrC,OAAO,EAEL,KAAK,GAAG,EACR,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,IAAI,EACT,KAAK,kBAAkB,EACxB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAKL,KAAK,kBAAkB,EAExB,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,GAAG,CAAC;IACT,YAAY,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IACxC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC9C,wBAAwB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9C,uBAAuB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAChF,0BAA0B,CAAC,EAAE,CAAC,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACnF,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACnD,qBAAqB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACzE,uBAAuB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9C,UAAU,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAC5C,kBAAkB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,mBAAmB,KAAK,SAAS,CAAC,GAAG,SAAS,CAAC;IAC/E,eAAe,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,kBAAkB,KAAK,SAAS,CAAC,GAAG,SAAS,CAAC;IACzE,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,wBAAgB,QAAQ,CAAC,EACvB,GAAG,EACH,YAAY,EACZ,iBAAiB,EACjB,wBAAwB,EACxB,uBAAuB,EACvB,0BAA0B,EAC1B,sBAA6B,EAC7B,qBAAqB,EACrB,uBAA+B,EAC/B,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,UAAiB,EACjB,SAAS,GACV,EAAE,aAAa,+BAyBf"}
@@ -13,7 +13,7 @@ import { WorkflowStatusIcon } from '#components/workflow-status/workflow-status-
13
13
  import { isWorkflowStatus } from '#core/workflow-run.js';
14
14
  import { formatJobExecutionTimeLabel } from '../job-graph/job-duration-format.js';
15
15
  import { buildStepListModel, defaultStepListJobExecution, humanizeStatus } from './step-list-model.js';
16
- export function StepList({ job, jobExecution, selectedAttemptId, defaultSelectedAttemptId, onSelectedAttemptChange, inspectorOpenAttemptId = null, onInspectorOpenChange, autoSelectActiveAttempt = false, emptyState, renderExpandedStep, renderInspector, showHeader = true, className }) {
16
+ export function StepList({ job, jobExecution, selectedAttemptId, defaultSelectedAttemptId, onSelectedAttemptChange, onExpandedAttemptIdsChange, inspectorOpenAttemptId = null, onInspectorOpenChange, autoSelectActiveAttempt = false, emptyState, renderExpandedStep, renderInspector, showHeader = true, className }) {
17
17
  const selectedJobExecution = jobExecution ?? defaultStepListJobExecution(job);
18
18
  const model = useMemo(()=>buildStepListModel({
19
19
  job,
@@ -27,6 +27,7 @@ export function StepList({ job, jobExecution, selectedAttemptId, defaultSelected
27
27
  selectedAttemptId: selectedAttemptId,
28
28
  defaultSelectedAttemptId: defaultSelectedAttemptId,
29
29
  onSelectedAttemptChange: onSelectedAttemptChange,
30
+ onExpandedAttemptIdsChange: onExpandedAttemptIdsChange,
30
31
  inspectorOpenAttemptId: inspectorOpenAttemptId,
31
32
  onInspectorOpenChange: onInspectorOpenChange,
32
33
  autoSelectActiveAttempt: autoSelectActiveAttempt,
@@ -37,7 +38,7 @@ export function StepList({ job, jobExecution, selectedAttemptId, defaultSelected
37
38
  className: className
38
39
  }, model.jobExecutionId);
39
40
  }
40
- function StepListContent({ model, selectedAttemptId, defaultSelectedAttemptId, onSelectedAttemptChange, inspectorOpenAttemptId = null, onInspectorOpenChange, autoSelectActiveAttempt, emptyState, renderExpandedStep, renderInspector, showHeader, className }) {
41
+ function StepListContent({ model, selectedAttemptId, defaultSelectedAttemptId, onSelectedAttemptChange, onExpandedAttemptIdsChange, inspectorOpenAttemptId = null, onInspectorOpenChange, autoSelectActiveAttempt, emptyState, renderExpandedStep, renderInspector, showHeader, className }) {
41
42
  const titleId = useId();
42
43
  const [localSelectedAttemptIds, setLocalSelectedAttemptIds] = useState(()=>selectedAttemptId ? [
43
44
  selectedAttemptId
@@ -48,13 +49,29 @@ function StepListContent({ model, selectedAttemptId, defaultSelectedAttemptId, o
48
49
  const lastNotifiedSelectedAttemptId = useRef(null);
49
50
  const autoSelectedAttemptIdRef = useRef(undefined);
50
51
  const previousSelectedAttemptIdRef = useRef(selectedAttemptId);
52
+ const lastReportedExpandedAttemptIdsKeyRef = useRef(null);
51
53
  const shouldUseControlledCollapsedState = selectedAttemptId === null && lastNotifiedSelectedAttemptId.current === null;
52
54
  const autoSelectedAttemptId = selectedAttemptId === undefined && autoSelectActiveAttempt && !userSelectedAttempt ? autoSelectedAttemptIdRef.current ?? model.activeEntryId : undefined;
53
- const autoSelectedAttemptIds = autoSelectedAttemptId ? [
55
+ const autoSelectedAttemptIds = useMemo(()=>autoSelectedAttemptId ? [
56
+ autoSelectedAttemptId
57
+ ] : [], [
54
58
  autoSelectedAttemptId
55
- ] : [];
56
- const selectedAttemptIds = shouldUseControlledCollapsedState ? [] : localSelectedAttemptIds.length > 0 ? localSelectedAttemptIds : autoSelectedAttemptIds;
59
+ ]);
60
+ const selectedAttemptIds = useMemo(()=>shouldUseControlledCollapsedState ? [] : localSelectedAttemptIds.length > 0 ? localSelectedAttemptIds : autoSelectedAttemptIds, [
61
+ autoSelectedAttemptIds,
62
+ localSelectedAttemptIds,
63
+ shouldUseControlledCollapsedState
64
+ ]);
57
65
  const hasExpandedContent = renderExpandedStep !== undefined;
66
+ useEffect(()=>{
67
+ const key = selectedAttemptIds.join('|');
68
+ if (lastReportedExpandedAttemptIdsKeyRef.current === key) return;
69
+ lastReportedExpandedAttemptIdsKeyRef.current = key;
70
+ onExpandedAttemptIdsChange?.(selectedAttemptIds);
71
+ }, [
72
+ onExpandedAttemptIdsChange,
73
+ selectedAttemptIds
74
+ ]);
58
75
  useEffect(()=>{
59
76
  if (selectedAttemptId !== undefined) {
60
77
  previousSelectedAttemptIdRef.current = selectedAttemptId;
@@ -123,7 +140,7 @@ function StepListContent({ model, selectedAttemptId, defaultSelectedAttemptId, o
123
140
  children: [
124
141
  /*#__PURE__*/ _jsxs("section", {
125
142
  "aria-labelledby": showHeader ? titleId : undefined,
126
- className: cn('flex min-h-0 flex-col rounded-8 border border-border-neutral-base bg-background-components-base', className),
143
+ className: cn('flex min-h-0 flex-col rounded-8 border border-border-neutral-base bg-background-neutral-base', className),
127
144
  children: [
128
145
  showHeader ? /*#__PURE__*/ _jsx("div", {
129
146
  className: "flex min-h-40 items-center border-b border-border-neutral-base px-row py-row",
@@ -302,7 +319,7 @@ function StepRow({ entry, selected, hasExpandedContent, isLast, onSelect, onInsp
302
319
  const row = /*#__PURE__*/ _jsxs(_Fragment, {
303
320
  children: [
304
321
  /*#__PURE__*/ _jsxs("div", {
305
- className: cn('group flex min-w-0 items-center gap-inline pr-[8px] transition-colors hover:bg-background-components-hover active:bg-background-components-pressed', selected && 'bg-background-components-hover', !hasExpandedContent && [
322
+ className: cn('group flex min-w-0 items-center gap-inline bg-background-neutral-base pr-[8px] transition-colors hover:bg-background-neutral-hover active:bg-background-neutral-pressed', selected && 'bg-background-neutral-hover', !hasExpandedContent && [
306
323
  'border-b border-border-neutral-base',
307
324
  isLast && 'border-b-0'
308
325
  ]),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/step-list/step-list.tsx"],"sourcesContent":["import {\n Accordion,\n AccordionContent,\n AccordionItem,\n AccordionTrigger,\n} from '@shipfox/react-ui/accordion';\nimport {Badge, type BadgeVariant} from '@shipfox/react-ui/badge';\nimport {Dot} from '@shipfox/react-ui/dot';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {TimeTickerProvider, useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn, humanDuration} from '@shipfox/react-ui/utils';\nimport type {ReactNode} from 'react';\nimport {useEffect, useId, useMemo, useRef, useState} from 'react';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {\n isWorkflowStatus,\n type Job,\n type JobDisplayStatus,\n type JobExecution,\n type Step,\n type StepSourceLocation,\n} from '#core/workflow-run.js';\nimport {formatJobExecutionTimeLabel} from '../job-graph/job-duration-format.js';\nimport {\n buildStepListModel,\n defaultStepListJobExecution,\n humanizeStatus,\n type StepAttemptModel,\n type StepListEntryModel,\n type StepListModel,\n} from './step-list-model.js';\n\nexport interface StepExpandedContext {\n step: Step;\n stepId: string;\n stepLabel: string;\n sourceLocation: StepSourceLocation | null;\n attempt: number;\n attemptId: string;\n attemptStartedAt: string;\n attemptError: Record<string, unknown> | null;\n attemptStatus: string;\n carriedOver: boolean;\n}\n\nexport interface StepListEmptyState {\n title: string;\n description: string;\n status?: JobDisplayStatus | undefined;\n}\n\nexport interface StepListProps {\n job: Job;\n jobExecution?: JobExecution | undefined;\n selectedAttemptId?: string | null | undefined;\n defaultSelectedAttemptId?: string | undefined;\n onSelectedAttemptChange?: ((attemptId: string | undefined) => void) | undefined;\n inspectorOpenAttemptId?: string | null | undefined;\n onInspectorOpenChange?: ((attemptId: string | null) => void) | undefined;\n autoSelectActiveAttempt?: boolean | undefined;\n emptyState?: StepListEmptyState | undefined;\n renderExpandedStep?: ((context: StepExpandedContext) => ReactNode) | undefined;\n renderInspector?: ((entry: StepListEntryModel) => ReactNode) | undefined;\n showHeader?: boolean | undefined;\n className?: string | undefined;\n}\n\nexport function StepList({\n job,\n jobExecution,\n selectedAttemptId,\n defaultSelectedAttemptId,\n onSelectedAttemptChange,\n inspectorOpenAttemptId = null,\n onInspectorOpenChange,\n autoSelectActiveAttempt = false,\n emptyState,\n renderExpandedStep,\n renderInspector,\n showHeader = true,\n className,\n}: StepListProps) {\n const selectedJobExecution = jobExecution ?? defaultStepListJobExecution(job);\n const model = useMemo(\n () => buildStepListModel({job, jobExecution: selectedJobExecution}),\n [job, selectedJobExecution],\n );\n\n return (\n <StepListContent\n key={model.jobExecutionId}\n model={model}\n selectedAttemptId={selectedAttemptId}\n defaultSelectedAttemptId={defaultSelectedAttemptId}\n onSelectedAttemptChange={onSelectedAttemptChange}\n inspectorOpenAttemptId={inspectorOpenAttemptId}\n onInspectorOpenChange={onInspectorOpenChange}\n autoSelectActiveAttempt={autoSelectActiveAttempt}\n emptyState={emptyState}\n renderExpandedStep={renderExpandedStep}\n renderInspector={renderInspector}\n showHeader={showHeader}\n className={className}\n />\n );\n}\n\nfunction StepListContent({\n model,\n selectedAttemptId,\n defaultSelectedAttemptId,\n onSelectedAttemptChange,\n inspectorOpenAttemptId = null,\n onInspectorOpenChange,\n autoSelectActiveAttempt,\n emptyState,\n renderExpandedStep,\n renderInspector,\n showHeader,\n className,\n}: Omit<StepListProps, 'job' | 'jobExecution'> & {model: StepListModel}) {\n const titleId = useId();\n const [localSelectedAttemptIds, setLocalSelectedAttemptIds] = useState<string[]>(() =>\n selectedAttemptId\n ? [selectedAttemptId]\n : defaultSelectedAttemptId\n ? [defaultSelectedAttemptId]\n : [],\n );\n const [userSelectedAttempt, setUserSelectedAttempt] = useState(false);\n const lastNotifiedSelectedAttemptId = useRef<string | null>(null);\n const autoSelectedAttemptIdRef = useRef<string | undefined>(undefined);\n const previousSelectedAttemptIdRef = useRef<string | null | undefined>(selectedAttemptId);\n const shouldUseControlledCollapsedState =\n selectedAttemptId === null && lastNotifiedSelectedAttemptId.current === null;\n const autoSelectedAttemptId =\n selectedAttemptId === undefined && autoSelectActiveAttempt && !userSelectedAttempt\n ? (autoSelectedAttemptIdRef.current ?? model.activeEntryId)\n : undefined;\n const autoSelectedAttemptIds = autoSelectedAttemptId ? [autoSelectedAttemptId] : [];\n const selectedAttemptIds = shouldUseControlledCollapsedState\n ? []\n : localSelectedAttemptIds.length > 0\n ? localSelectedAttemptIds\n : autoSelectedAttemptIds;\n const hasExpandedContent = renderExpandedStep !== undefined;\n\n useEffect(() => {\n if (selectedAttemptId !== undefined) {\n previousSelectedAttemptIdRef.current = selectedAttemptId;\n return;\n }\n\n // A changing landing candidate is normal while a live job polls. Only reset the local\n // accordion when the parent has just cleared a previously controlled URL selection.\n const wasControlled = previousSelectedAttemptIdRef.current !== undefined;\n previousSelectedAttemptIdRef.current = undefined;\n if (!wasControlled) return;\n\n setLocalSelectedAttemptIds(defaultSelectedAttemptId ? [defaultSelectedAttemptId] : []);\n setUserSelectedAttempt(false);\n autoSelectedAttemptIdRef.current = undefined;\n }, [defaultSelectedAttemptId, selectedAttemptId]);\n\n useEffect(() => {\n if (\n selectedAttemptId !== undefined ||\n !autoSelectActiveAttempt ||\n userSelectedAttempt ||\n localSelectedAttemptIds.length > 0 ||\n autoSelectedAttemptIdRef.current !== undefined\n ) {\n return;\n }\n if (model.activeEntryId) autoSelectedAttemptIdRef.current = model.activeEntryId;\n }, [\n autoSelectActiveAttempt,\n localSelectedAttemptIds.length,\n model.activeEntryId,\n selectedAttemptId,\n userSelectedAttempt,\n ]);\n\n useEffect(() => {\n if (selectedAttemptId === undefined) return;\n\n const nextSelectedAttemptId = selectedAttemptId ?? null;\n if (lastNotifiedSelectedAttemptId.current === nextSelectedAttemptId) {\n lastNotifiedSelectedAttemptId.current = null;\n return;\n }\n\n setLocalSelectedAttemptIds(selectedAttemptId ? [selectedAttemptId] : []);\n setUserSelectedAttempt(true);\n }, [selectedAttemptId]);\n\n useEffect(() => {\n if (\n inspectorOpenAttemptId !== null &&\n !model.entries.some((entry) => entry.id === inspectorOpenAttemptId && !entry.carriedOver)\n ) {\n onInspectorOpenChange?.(null);\n }\n }, [inspectorOpenAttemptId, model.entries, onInspectorOpenChange]);\n\n function selectAttempt(nextAttemptIds: string[]) {\n const nextAttemptId = nextSelectedAttemptId(selectedAttemptIds, nextAttemptIds);\n setUserSelectedAttempt(true);\n setLocalSelectedAttemptIds(nextAttemptIds);\n lastNotifiedSelectedAttemptId.current = nextAttemptId ?? null;\n onSelectedAttemptChange?.(nextAttemptId);\n }\n\n const inspectorEntry = model.entries.find(\n (entry) => entry.id === inspectorOpenAttemptId && !entry.carriedOver,\n );\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <section\n aria-labelledby={showHeader ? titleId : undefined}\n className={cn(\n 'flex min-h-0 flex-col rounded-8 border border-border-neutral-base bg-background-components-base',\n className,\n )}\n >\n {showHeader ? (\n <div className=\"flex min-h-40 items-center border-b border-border-neutral-base px-row py-row\">\n <Text as=\"h2\" id={titleId} size=\"sm\" bold className=\"text-foreground-neutral-base\">\n {model.jobName}\n </Text>\n </div>\n ) : null}\n\n {model.entries.length === 0 ? (\n <StepListEmptyStateView emptyState={emptyState} />\n ) : (\n <Accordion\n type=\"multiple\"\n value={selectedAttemptIds}\n onValueChange={selectAttempt}\n asChild\n >\n <ol>\n {model.entries.map((entry, index) => {\n const selected = selectedAttemptIds.includes(entry.id);\n return (\n <StepRow\n key={entry.id}\n entry={entry}\n selected={selected}\n hasExpandedContent={hasExpandedContent}\n isLast={index === model.entries.length - 1}\n onSelect={() => {\n selectAttempt(\n hasExpandedContent\n ? toggleAttemptId(selectedAttemptIds, entry.id)\n : selected\n ? []\n : [entry.id],\n );\n }}\n onInspect={\n onInspectorOpenChange && !entry.carriedOver\n ? () => onInspectorOpenChange(entry.id)\n : undefined\n }\n expandedContent={\n selected\n ? renderExpandedStep?.({\n step: entry.step,\n stepId: entry.step.id,\n stepLabel: entry.step.label,\n sourceLocation: entry.step.sourceLocation,\n attempt: entry.attempt,\n attemptId: entry.id,\n attemptStartedAt: entry.startedAt,\n attemptError: entry.error,\n attemptStatus: entry.statusVisual.kind,\n carriedOver: entry.carriedOver,\n })\n : null\n }\n />\n );\n })}\n </ol>\n </Accordion>\n )}\n </section>\n {inspectorEntry ? renderInspector?.(inspectorEntry) : null}\n </TimeTickerProvider>\n );\n}\n\nfunction StepListEmptyStateView({\n emptyState = {\n title: 'No steps recorded',\n description: 'This job has not recorded any steps.',\n },\n}: {\n emptyState?: StepListEmptyState | undefined;\n}) {\n if (!emptyState.status) {\n return (\n <EmptyState\n className=\"min-h-120 p-panel\"\n icon=\"componentLine\"\n title={emptyState.title}\n description={emptyState.description}\n variant=\"compact\"\n />\n );\n }\n\n return (\n <div className=\"flex min-h-120 flex-col items-center justify-center gap-inline p-panel\">\n <StepListEmptyStateIcon status={emptyState.status} />\n <div className=\"text-center\">\n <Text size=\"sm\" className=\"text-foreground-neutral-subtle\">\n {emptyState.title}\n </Text>\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n {emptyState.description}\n </Text>\n </div>\n </div>\n );\n}\n\nfunction StepListEmptyStateIcon({status}: {status: JobDisplayStatus}) {\n if (status !== 'running') {\n return (\n <div className=\"flex size-32 items-center justify-center rounded-6 border border-border-neutral-strong bg-background-neutral-base p-tight\">\n <WorkflowStatusIcon status={status} size={20} tooltip={false} />\n </div>\n );\n }\n\n return (\n <div className=\"flex size-32 items-center justify-center rounded-6 border border-border-neutral-strong bg-background-neutral-base p-tight text-foreground-neutral-muted\">\n <Icon name=\"timerLine\" size={18} aria-hidden=\"true\" />\n </div>\n );\n}\n\nfunction StepRow({\n entry,\n selected,\n hasExpandedContent,\n isLast,\n onSelect,\n onInspect,\n expandedContent,\n}: {\n entry: StepListEntryModel;\n selected: boolean;\n hasExpandedContent: boolean;\n isLast: boolean;\n onSelect: () => void;\n onInspect?: (() => void) | undefined;\n expandedContent: ReactNode;\n}) {\n const shouldShowLabelTooltip = entry.step.label.length > 32;\n const rowContent = (\n <>\n <Icon\n name=\"chevronRight\"\n aria-hidden=\"true\"\n className={cn(\n 'size-14 shrink-0 text-foreground-neutral-muted transition-transform',\n selected && 'rotate-90',\n )}\n />\n <StepStatusIcon entry={entry} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex min-w-0 items-center gap-inline\">\n <Text size=\"sm\" bold className=\"truncate text-foreground-neutral-base\">\n {entry.step.label}\n </Text>\n {entry.step.attempts.length > 1 ? <StepAttemptChip attempt={entry} /> : null}\n {entry.carriedOver ? <CarriedOverBadge /> : null}\n </div>\n </div>\n <StepAttemptDurationLabel attempt={entry} />\n </>\n );\n const rowClasses = cn(\n 'group flex min-h-44 min-w-0 flex-1 items-center gap-x-inline bg-transparent px-row py-row text-left transition-colors hover:bg-transparent active:bg-transparent focus-visible:shadow-border-interactive-with-active focus-visible:outline-none',\n entry.carriedOver && 'opacity-[0.55]',\n );\n const button = hasExpandedContent ? (\n <AccordionTrigger\n showIcon={false}\n aria-label={entryAccessibleLabel(entry)}\n className={rowClasses}\n >\n {rowContent}\n </AccordionTrigger>\n ) : (\n <button\n type=\"button\"\n aria-expanded={false}\n aria-label={entryAccessibleLabel(entry)}\n onClick={onSelect}\n className={rowClasses}\n >\n {rowContent}\n </button>\n );\n const triggerNode = shouldShowLabelTooltip ? (\n <Tooltip>\n <TooltipTrigger asChild>{button}</TooltipTrigger>\n <TooltipContent>\n <span className=\"block max-w-320 break-words\">{entry.step.label}</span>\n </TooltipContent>\n </Tooltip>\n ) : (\n button\n );\n const row = (\n <>\n <div\n className={cn(\n 'group flex min-w-0 items-center gap-inline pr-[8px] transition-colors hover:bg-background-components-hover active:bg-background-components-pressed',\n selected && 'bg-background-components-hover',\n !hasExpandedContent && ['border-b border-border-neutral-base', isLast && 'border-b-0'],\n )}\n >\n <div className=\"min-w-0 flex-1\">{triggerNode}</div>\n {onInspect ? (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={`Inspect ${entry.step.label}, attempt ${entry.attempt}`}\n onClick={onInspect}\n className=\"flex size-28 shrink-0 items-center justify-center rounded-4 bg-transparent text-foreground-neutral-muted outline-none transition-colors hover:bg-transparent hover:text-foreground-neutral-base active:bg-transparent focus-visible:shadow-button-neutral-focus\"\n >\n <Icon name=\"informationLine\" size={14} aria-hidden=\"true\" />\n </button>\n </TooltipTrigger>\n <TooltipContent>Inspect step details</TooltipContent>\n </Tooltip>\n ) : null}\n </div>\n {selected && expandedContent ? (\n <AccordionContent className=\"border-t border-border-neutral-base bg-transparent px-0 py-0\">\n <div className=\"min-w-0\">{expandedContent}</div>\n </AccordionContent>\n ) : null}\n </>\n );\n\n if (hasExpandedContent) {\n return (\n <AccordionItem value={entry.id} asChild>\n <li>{row}</li>\n </AccordionItem>\n );\n }\n\n return <li>{row}</li>;\n}\n\nfunction CarriedOverBadge() {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"shrink-0\">\n <Badge variant=\"neutral\" size=\"2xs\">\n reused\n </Badge>\n </span>\n </TooltipTrigger>\n <TooltipContent>\n Carried over from a previous attempt; did not run in this attempt.\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction toggleAttemptId(selectedAttemptIds: readonly string[], attemptId: string): string[] {\n if (selectedAttemptIds.includes(attemptId)) {\n return selectedAttemptIds.filter((selectedAttemptId) => selectedAttemptId !== attemptId);\n }\n return [...selectedAttemptIds, attemptId];\n}\n\nfunction nextSelectedAttemptId(\n selectedAttemptIds: readonly string[],\n nextAttemptIds: readonly string[],\n): string | undefined {\n if (nextAttemptIds.length === 0) return undefined;\n\n const openedAttemptId = nextAttemptIds.find(\n (attemptId) => !selectedAttemptIds.includes(attemptId),\n );\n return openedAttemptId ?? nextAttemptIds.at(-1);\n}\n\nfunction StepStatusIcon({entry}: {entry: StepListEntryModel}) {\n if (isWorkflowStatus(entry.statusVisual.kind)) {\n return (\n <WorkflowStatusIcon\n status={entry.statusVisual.kind}\n ripple={entry.statusVisual.ripple}\n size={14}\n />\n );\n }\n\n return (\n <span role=\"img\" aria-label={entry.statusVisual.label} className=\"inline-flex shrink-0\">\n <Dot\n variant={entry.statusVisual.dot}\n ripple={entry.statusVisual.ripple}\n className=\"size-12 shrink-0\"\n />\n </span>\n );\n}\n\nconst attemptChipClasses: Record<NonNullable<BadgeVariant>, string> = {\n neutral: 'bg-tag-neutral-bg border-tag-neutral-border',\n info: 'bg-tag-blue-bg border-tag-blue-border',\n feature: 'bg-tag-purple-bg border-tag-purple-border',\n success: 'bg-tag-success-bg border-tag-success-border',\n warning: 'bg-tag-warning-bg border-tag-warning-border',\n error: 'bg-tag-error-bg border-tag-error-border',\n};\n\nfunction StepAttemptChip({attempt}: {attempt: StepAttemptModel}) {\n return (\n <div className=\"flex shrink-0 items-center gap-tight\" aria-hidden=\"true\">\n <span\n className={cn(\n 'inline-flex h-18 min-w-24 items-center justify-center rounded-4 border px-tight font-code text-xs leading-16 text-foreground-neutral-base',\n attemptChipClasses[attempt.statusVisual.badge ?? 'neutral'],\n )}\n >\n #{attempt.attempt}\n </span>\n </div>\n );\n}\n\nfunction entryAccessibleLabel(entry: StepListEntryModel): string {\n const parts = [entry.step.label, entry.statusVisual.label, `attempt ${entry.attempt}`];\n if (entry.step.error?.category) parts.push(humanizeStatus(entry.step.error.category));\n return parts.join(', ');\n}\n\nfunction StepAttemptDurationLabel({attempt}: {attempt: StepAttemptModel}) {\n const duration = attempt.displayDuration;\n if (duration === null) return null;\n\n if (duration.state === 'live') {\n return <LiveDurationText fromIso={duration.fromIso} />;\n }\n\n return <DurationText>{formatJobExecutionTimeLabel(duration)}</DurationText>;\n}\n\nfunction LiveDurationText({fromIso}: {fromIso: string}) {\n useTimeTick();\n return <DurationText>{humanDuration(fromIso)}</DurationText>;\n}\n\nfunction DurationText({children}: {children: string}) {\n return (\n <Code as=\"span\" variant=\"label\" className=\"shrink-0 tabular-nums text-foreground-neutral-muted\">\n {children}\n </Code>\n );\n}\n"],"names":["Accordion","AccordionContent","AccordionItem","AccordionTrigger","Badge","Dot","EmptyState","Icon","TimeTickerProvider","useTimeTick","Tooltip","TooltipContent","TooltipTrigger","Code","Text","cn","humanDuration","useEffect","useId","useMemo","useRef","useState","WorkflowStatusIcon","isWorkflowStatus","formatJobExecutionTimeLabel","buildStepListModel","defaultStepListJobExecution","humanizeStatus","StepList","job","jobExecution","selectedAttemptId","defaultSelectedAttemptId","onSelectedAttemptChange","inspectorOpenAttemptId","onInspectorOpenChange","autoSelectActiveAttempt","emptyState","renderExpandedStep","renderInspector","showHeader","className","selectedJobExecution","model","StepListContent","jobExecutionId","titleId","localSelectedAttemptIds","setLocalSelectedAttemptIds","userSelectedAttempt","setUserSelectedAttempt","lastNotifiedSelectedAttemptId","autoSelectedAttemptIdRef","undefined","previousSelectedAttemptIdRef","shouldUseControlledCollapsedState","current","autoSelectedAttemptId","activeEntryId","autoSelectedAttemptIds","selectedAttemptIds","length","hasExpandedContent","wasControlled","nextSelectedAttemptId","entries","some","entry","id","carriedOver","selectAttempt","nextAttemptIds","nextAttemptId","inspectorEntry","find","intervalMs","reducedMotionIntervalMs","section","aria-labelledby","div","as","size","bold","jobName","StepListEmptyStateView","type","value","onValueChange","asChild","ol","map","index","selected","includes","StepRow","isLast","onSelect","toggleAttemptId","onInspect","expandedContent","step","stepId","stepLabel","label","sourceLocation","attempt","attemptId","attemptStartedAt","startedAt","attemptError","error","attemptStatus","statusVisual","kind","title","description","status","icon","variant","StepListEmptyStateIcon","tooltip","name","aria-hidden","shouldShowLabelTooltip","rowContent","StepStatusIcon","attempts","StepAttemptChip","CarriedOverBadge","StepAttemptDurationLabel","rowClasses","button","showIcon","aria-label","entryAccessibleLabel","aria-expanded","onClick","triggerNode","span","row","li","filter","openedAttemptId","at","ripple","role","dot","attemptChipClasses","neutral","info","feature","success","warning","badge","parts","category","push","join","duration","displayDuration","state","LiveDurationText","fromIso","DurationText","children"],"mappings":";AAAA,SACEA,SAAS,EACTC,gBAAgB,EAChBC,aAAa,EACbC,gBAAgB,QACX,8BAA8B;AACrC,SAAQC,KAAK,QAA0B,0BAA0B;AACjE,SAAQC,GAAG,QAAO,wBAAwB;AAC1C,SAAQC,UAAU,QAAO,gCAAgC;AACzD,SAAQC,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,kBAAkB,EAAEC,WAAW,QAAO,gCAAgC;AAC9E,SAAQC,OAAO,EAAEC,cAAc,EAAEC,cAAc,QAAO,4BAA4B;AAClF,SAAQC,IAAI,EAAEC,IAAI,QAAO,+BAA+B;AACxD,SAAQC,EAAE,EAAEC,aAAa,QAAO,0BAA0B;AAE1D,SAAQC,SAAS,EAAEC,KAAK,EAAEC,OAAO,EAAEC,MAAM,EAAEC,QAAQ,QAAO,QAAQ;AAClE,SAAQC,kBAAkB,QAAO,sDAAsD;AACvF,SACEC,gBAAgB,QAMX,wBAAwB;AAC/B,SAAQC,2BAA2B,QAAO,sCAAsC;AAChF,SACEC,kBAAkB,EAClBC,2BAA2B,EAC3BC,cAAc,QAIT,uBAAuB;AAqC9B,OAAO,SAASC,SAAS,EACvBC,GAAG,EACHC,YAAY,EACZC,iBAAiB,EACjBC,wBAAwB,EACxBC,uBAAuB,EACvBC,yBAAyB,IAAI,EAC7BC,qBAAqB,EACrBC,0BAA0B,KAAK,EAC/BC,UAAU,EACVC,kBAAkB,EAClBC,eAAe,EACfC,aAAa,IAAI,EACjBC,SAAS,EACK;IACd,MAAMC,uBAAuBZ,gBAAgBJ,4BAA4BG;IACzE,MAAMc,QAAQxB,QACZ,IAAMM,mBAAmB;YAACI;YAAKC,cAAcY;QAAoB,IACjE;QAACb;QAAKa;KAAqB;IAG7B,qBACE,KAACE;QAECD,OAAOA;QACPZ,mBAAmBA;QACnBC,0BAA0BA;QAC1BC,yBAAyBA;QACzBC,wBAAwBA;QACxBC,uBAAuBA;QACvBC,yBAAyBA;QACzBC,YAAYA;QACZC,oBAAoBA;QACpBC,iBAAiBA;QACjBC,YAAYA;QACZC,WAAWA;OAZNE,MAAME,cAAc;AAe/B;AAEA,SAASD,gBAAgB,EACvBD,KAAK,EACLZ,iBAAiB,EACjBC,wBAAwB,EACxBC,uBAAuB,EACvBC,yBAAyB,IAAI,EAC7BC,qBAAqB,EACrBC,uBAAuB,EACvBC,UAAU,EACVC,kBAAkB,EAClBC,eAAe,EACfC,UAAU,EACVC,SAAS,EAC4D;IACrE,MAAMK,UAAU5B;IAChB,MAAM,CAAC6B,yBAAyBC,2BAA2B,GAAG3B,SAAmB,IAC/EU,oBACI;YAACA;SAAkB,GACnBC,2BACE;YAACA;SAAyB,GAC1B,EAAE;IAEV,MAAM,CAACiB,qBAAqBC,uBAAuB,GAAG7B,SAAS;IAC/D,MAAM8B,gCAAgC/B,OAAsB;IAC5D,MAAMgC,2BAA2BhC,OAA2BiC;IAC5D,MAAMC,+BAA+BlC,OAAkCW;IACvE,MAAMwB,oCACJxB,sBAAsB,QAAQoB,8BAA8BK,OAAO,KAAK;IAC1E,MAAMC,wBACJ1B,sBAAsBsB,aAAajB,2BAA2B,CAACa,sBAC1DG,yBAAyBI,OAAO,IAAIb,MAAMe,aAAa,GACxDL;IACN,MAAMM,yBAAyBF,wBAAwB;QAACA;KAAsB,GAAG,EAAE;IACnF,MAAMG,qBAAqBL,oCACvB,EAAE,GACFR,wBAAwBc,MAAM,GAAG,IAC/Bd,0BACAY;IACN,MAAMG,qBAAqBxB,uBAAuBe;IAElDpC,UAAU;QACR,IAAIc,sBAAsBsB,WAAW;YACnCC,6BAA6BE,OAAO,GAAGzB;YACvC;QACF;QAEA,sFAAsF;QACtF,oFAAoF;QACpF,MAAMgC,gBAAgBT,6BAA6BE,OAAO,KAAKH;QAC/DC,6BAA6BE,OAAO,GAAGH;QACvC,IAAI,CAACU,eAAe;QAEpBf,2BAA2BhB,2BAA2B;YAACA;SAAyB,GAAG,EAAE;QACrFkB,uBAAuB;QACvBE,yBAAyBI,OAAO,GAAGH;IACrC,GAAG;QAACrB;QAA0BD;KAAkB;IAEhDd,UAAU;QACR,IACEc,sBAAsBsB,aACtB,CAACjB,2BACDa,uBACAF,wBAAwBc,MAAM,GAAG,KACjCT,yBAAyBI,OAAO,KAAKH,WACrC;YACA;QACF;QACA,IAAIV,MAAMe,aAAa,EAAEN,yBAAyBI,OAAO,GAAGb,MAAMe,aAAa;IACjF,GAAG;QACDtB;QACAW,wBAAwBc,MAAM;QAC9BlB,MAAMe,aAAa;QACnB3B;QACAkB;KACD;IAEDhC,UAAU;QACR,IAAIc,sBAAsBsB,WAAW;QAErC,MAAMW,wBAAwBjC,qBAAqB;QACnD,IAAIoB,8BAA8BK,OAAO,KAAKQ,uBAAuB;YACnEb,8BAA8BK,OAAO,GAAG;YACxC;QACF;QAEAR,2BAA2BjB,oBAAoB;YAACA;SAAkB,GAAG,EAAE;QACvEmB,uBAAuB;IACzB,GAAG;QAACnB;KAAkB;IAEtBd,UAAU;QACR,IACEiB,2BAA2B,QAC3B,CAACS,MAAMsB,OAAO,CAACC,IAAI,CAAC,CAACC,QAAUA,MAAMC,EAAE,KAAKlC,0BAA0B,CAACiC,MAAME,WAAW,GACxF;YACAlC,wBAAwB;QAC1B;IACF,GAAG;QAACD;QAAwBS,MAAMsB,OAAO;QAAE9B;KAAsB;IAEjE,SAASmC,cAAcC,cAAwB;QAC7C,MAAMC,gBAAgBR,sBAAsBJ,oBAAoBW;QAChErB,uBAAuB;QACvBF,2BAA2BuB;QAC3BpB,8BAA8BK,OAAO,GAAGgB,iBAAiB;QACzDvC,0BAA0BuC;IAC5B;IAEA,MAAMC,iBAAiB9B,MAAMsB,OAAO,CAACS,IAAI,CACvC,CAACP,QAAUA,MAAMC,EAAE,KAAKlC,0BAA0B,CAACiC,MAAME,WAAW;IAGtE,qBACE,MAAC7D;QAAmBmE,YAAY;QAAMC,yBAAyB;;0BAC7D,MAACC;gBACCC,mBAAiBtC,aAAaM,UAAUO;gBACxCZ,WAAW1B,GACT,mGACA0B;;oBAGDD,2BACC,KAACuC;wBAAItC,WAAU;kCACb,cAAA,KAAC3B;4BAAKkE,IAAG;4BAAKZ,IAAItB;4BAASmC,MAAK;4BAAKC,IAAI;4BAACzC,WAAU;sCACjDE,MAAMwC,OAAO;;yBAGhB;oBAEHxC,MAAMsB,OAAO,CAACJ,MAAM,KAAK,kBACxB,KAACuB;wBAAuB/C,YAAYA;uCAEpC,KAACrC;wBACCqF,MAAK;wBACLC,OAAO1B;wBACP2B,eAAejB;wBACfkB,OAAO;kCAEP,cAAA,KAACC;sCACE9C,MAAMsB,OAAO,CAACyB,GAAG,CAAC,CAACvB,OAAOwB;gCACzB,MAAMC,WAAWhC,mBAAmBiC,QAAQ,CAAC1B,MAAMC,EAAE;gCACrD,qBACE,KAAC0B;oCAEC3B,OAAOA;oCACPyB,UAAUA;oCACV9B,oBAAoBA;oCACpBiC,QAAQJ,UAAUhD,MAAMsB,OAAO,CAACJ,MAAM,GAAG;oCACzCmC,UAAU;wCACR1B,cACER,qBACImC,gBAAgBrC,oBAAoBO,MAAMC,EAAE,IAC5CwB,WACE,EAAE,GACF;4CAACzB,MAAMC,EAAE;yCAAC;oCAEpB;oCACA8B,WACE/D,yBAAyB,CAACgC,MAAME,WAAW,GACvC,IAAMlC,sBAAsBgC,MAAMC,EAAE,IACpCf;oCAEN8C,iBACEP,WACItD,qBAAqB;wCACnB8D,MAAMjC,MAAMiC,IAAI;wCAChBC,QAAQlC,MAAMiC,IAAI,CAAChC,EAAE;wCACrBkC,WAAWnC,MAAMiC,IAAI,CAACG,KAAK;wCAC3BC,gBAAgBrC,MAAMiC,IAAI,CAACI,cAAc;wCACzCC,SAAStC,MAAMsC,OAAO;wCACtBC,WAAWvC,MAAMC,EAAE;wCACnBuC,kBAAkBxC,MAAMyC,SAAS;wCACjCC,cAAc1C,MAAM2C,KAAK;wCACzBC,eAAe5C,MAAM6C,YAAY,CAACC,IAAI;wCACtC5C,aAAaF,MAAME,WAAW;oCAChC,KACA;mCAjCDF,MAAMC,EAAE;4BAqCnB;;;;;YAKPK,iBAAiBlC,kBAAkBkC,kBAAkB;;;AAG5D;AAEA,SAASW,uBAAuB,EAC9B/C,aAAa;IACX6E,OAAO;IACPC,aAAa;AACf,CAAC,EAGF;IACC,IAAI,CAAC9E,WAAW+E,MAAM,EAAE;QACtB,qBACE,KAAC9G;YACCmC,WAAU;YACV4E,MAAK;YACLH,OAAO7E,WAAW6E,KAAK;YACvBC,aAAa9E,WAAW8E,WAAW;YACnCG,SAAQ;;IAGd;IAEA,qBACE,MAACvC;QAAItC,WAAU;;0BACb,KAAC8E;gBAAuBH,QAAQ/E,WAAW+E,MAAM;;0BACjD,MAACrC;gBAAItC,WAAU;;kCACb,KAAC3B;wBAAKmE,MAAK;wBAAKxC,WAAU;kCACvBJ,WAAW6E,KAAK;;kCAEnB,KAACpG;wBAAKmE,MAAK;wBAAKxC,WAAU;kCACvBJ,WAAW8E,WAAW;;;;;;AAKjC;AAEA,SAASI,uBAAuB,EAACH,MAAM,EAA6B;IAClE,IAAIA,WAAW,WAAW;QACxB,qBACE,KAACrC;YAAItC,WAAU;sBACb,cAAA,KAACnB;gBAAmB8F,QAAQA;gBAAQnC,MAAM;gBAAIuC,SAAS;;;IAG7D;IAEA,qBACE,KAACzC;QAAItC,WAAU;kBACb,cAAA,KAAClC;YAAKkH,MAAK;YAAYxC,MAAM;YAAIyC,eAAY;;;AAGnD;AAEA,SAAS5B,QAAQ,EACf3B,KAAK,EACLyB,QAAQ,EACR9B,kBAAkB,EAClBiC,MAAM,EACNC,QAAQ,EACRE,SAAS,EACTC,eAAe,EAShB;IACC,MAAMwB,yBAAyBxD,MAAMiC,IAAI,CAACG,KAAK,CAAC1C,MAAM,GAAG;IACzD,MAAM+D,2BACJ;;0BACE,KAACrH;gBACCkH,MAAK;gBACLC,eAAY;gBACZjF,WAAW1B,GACT,uEACA6E,YAAY;;0BAGhB,KAACiC;gBAAe1D,OAAOA;;0BACvB,KAACY;gBAAItC,WAAU;0BACb,cAAA,MAACsC;oBAAItC,WAAU;;sCACb,KAAC3B;4BAAKmE,MAAK;4BAAKC,IAAI;4BAACzC,WAAU;sCAC5B0B,MAAMiC,IAAI,CAACG,KAAK;;wBAElBpC,MAAMiC,IAAI,CAAC0B,QAAQ,CAACjE,MAAM,GAAG,kBAAI,KAACkE;4BAAgBtB,SAAStC;6BAAY;wBACvEA,MAAME,WAAW,iBAAG,KAAC2D,wBAAsB;;;;0BAGhD,KAACC;gBAAyBxB,SAAStC;;;;IAGvC,MAAM+D,aAAanH,GACjB,mPACAoD,MAAME,WAAW,IAAI;IAEvB,MAAM8D,SAASrE,mCACb,KAAC3D;QACCiI,UAAU;QACVC,cAAYC,qBAAqBnE;QACjC1B,WAAWyF;kBAEVN;uBAGH,KAACO;QACC9C,MAAK;QACLkD,iBAAe;QACfF,cAAYC,qBAAqBnE;QACjCqE,SAASxC;QACTvD,WAAWyF;kBAEVN;;IAGL,MAAMa,cAAcd,uCAClB,MAACjH;;0BACC,KAACE;gBAAe4E,OAAO;0BAAE2C;;0BACzB,KAACxH;0BACC,cAAA,KAAC+H;oBAAKjG,WAAU;8BAA+B0B,MAAMiC,IAAI,CAACG,KAAK;;;;SAInE4B;IAEF,MAAMQ,oBACJ;;0BACE,MAAC5D;gBACCtC,WAAW1B,GACT,sJACA6E,YAAY,kCACZ,CAAC9B,sBAAsB;oBAAC;oBAAuCiC,UAAU;iBAAa;;kCAGxF,KAAChB;wBAAItC,WAAU;kCAAkBgG;;oBAChCvC,0BACC,MAACxF;;0CACC,KAACE;gCAAe4E,OAAO;0CACrB,cAAA,KAAC2C;oCACC9C,MAAK;oCACLgD,cAAY,CAAC,QAAQ,EAAElE,MAAMiC,IAAI,CAACG,KAAK,CAAC,UAAU,EAAEpC,MAAMsC,OAAO,EAAE;oCACnE+B,SAAStC;oCACTzD,WAAU;8CAEV,cAAA,KAAClC;wCAAKkH,MAAK;wCAAkBxC,MAAM;wCAAIyC,eAAY;;;;0CAGvD,KAAC/G;0CAAe;;;yBAEhB;;;YAELiF,YAAYO,gCACX,KAAClG;gBAAiBwC,WAAU;0BAC1B,cAAA,KAACsC;oBAAItC,WAAU;8BAAW0D;;iBAE1B;;;IAIR,IAAIrC,oBAAoB;QACtB,qBACE,KAAC5D;YAAcoF,OAAOnB,MAAMC,EAAE;YAAEoB,OAAO;sBACrC,cAAA,KAACoD;0BAAID;;;IAGX;IAEA,qBAAO,KAACC;kBAAID;;AACd;AAEA,SAASX;IACP,qBACE,MAACtH;;0BACC,KAACE;gBAAe4E,OAAO;0BACrB,cAAA,KAACkD;oBAAKjG,WAAU;8BACd,cAAA,KAACrC;wBAAMkH,SAAQ;wBAAUrC,MAAK;kCAAM;;;;0BAKxC,KAACtE;0BAAe;;;;AAKtB;AAEA,SAASsF,gBAAgBrC,kBAAqC,EAAE8C,SAAiB;IAC/E,IAAI9C,mBAAmBiC,QAAQ,CAACa,YAAY;QAC1C,OAAO9C,mBAAmBiF,MAAM,CAAC,CAAC9G,oBAAsBA,sBAAsB2E;IAChF;IACA,OAAO;WAAI9C;QAAoB8C;KAAU;AAC3C;AAEA,SAAS1C,sBACPJ,kBAAqC,EACrCW,cAAiC;IAEjC,IAAIA,eAAeV,MAAM,KAAK,GAAG,OAAOR;IAExC,MAAMyF,kBAAkBvE,eAAeG,IAAI,CACzC,CAACgC,YAAc,CAAC9C,mBAAmBiC,QAAQ,CAACa;IAE9C,OAAOoC,mBAAmBvE,eAAewE,EAAE,CAAC,CAAC;AAC/C;AAEA,SAASlB,eAAe,EAAC1D,KAAK,EAA8B;IAC1D,IAAI5C,iBAAiB4C,MAAM6C,YAAY,CAACC,IAAI,GAAG;QAC7C,qBACE,KAAC3F;YACC8F,QAAQjD,MAAM6C,YAAY,CAACC,IAAI;YAC/B+B,QAAQ7E,MAAM6C,YAAY,CAACgC,MAAM;YACjC/D,MAAM;;IAGZ;IAEA,qBACE,KAACyD;QAAKO,MAAK;QAAMZ,cAAYlE,MAAM6C,YAAY,CAACT,KAAK;QAAE9D,WAAU;kBAC/D,cAAA,KAACpC;YACCiH,SAASnD,MAAM6C,YAAY,CAACkC,GAAG;YAC/BF,QAAQ7E,MAAM6C,YAAY,CAACgC,MAAM;YACjCvG,WAAU;;;AAIlB;AAEA,MAAM0G,qBAAgE;IACpEC,SAAS;IACTC,MAAM;IACNC,SAAS;IACTC,SAAS;IACTC,SAAS;IACT1C,OAAO;AACT;AAEA,SAASiB,gBAAgB,EAACtB,OAAO,EAA8B;IAC7D,qBACE,KAAC1B;QAAItC,WAAU;QAAuCiF,eAAY;kBAChE,cAAA,MAACgB;YACCjG,WAAW1B,GACT,6IACAoI,kBAAkB,CAAC1C,QAAQO,YAAY,CAACyC,KAAK,IAAI,UAAU;;gBAE9D;gBACGhD,QAAQA,OAAO;;;;AAIzB;AAEA,SAAS6B,qBAAqBnE,KAAyB;IACrD,MAAMuF,QAAQ;QAACvF,MAAMiC,IAAI,CAACG,KAAK;QAAEpC,MAAM6C,YAAY,CAACT,KAAK;QAAE,CAAC,QAAQ,EAAEpC,MAAMsC,OAAO,EAAE;KAAC;IACtF,IAAItC,MAAMiC,IAAI,CAACU,KAAK,EAAE6C,UAAUD,MAAME,IAAI,CAACjI,eAAewC,MAAMiC,IAAI,CAACU,KAAK,CAAC6C,QAAQ;IACnF,OAAOD,MAAMG,IAAI,CAAC;AACpB;AAEA,SAAS5B,yBAAyB,EAACxB,OAAO,EAA8B;IACtE,MAAMqD,WAAWrD,QAAQsD,eAAe;IACxC,IAAID,aAAa,MAAM,OAAO;IAE9B,IAAIA,SAASE,KAAK,KAAK,QAAQ;QAC7B,qBAAO,KAACC;YAAiBC,SAASJ,SAASI,OAAO;;IACpD;IAEA,qBAAO,KAACC;kBAAc3I,4BAA4BsI;;AACpD;AAEA,SAASG,iBAAiB,EAACC,OAAO,EAAoB;IACpDzJ;IACA,qBAAO,KAAC0J;kBAAcnJ,cAAckJ;;AACtC;AAEA,SAASC,aAAa,EAACC,QAAQ,EAAqB;IAClD,qBACE,KAACvJ;QAAKmE,IAAG;QAAOsC,SAAQ;QAAQ7E,WAAU;kBACvC2H;;AAGP"}
1
+ {"version":3,"sources":["../../../src/components/step-list/step-list.tsx"],"sourcesContent":["import {\n Accordion,\n AccordionContent,\n AccordionItem,\n AccordionTrigger,\n} from '@shipfox/react-ui/accordion';\nimport {Badge, type BadgeVariant} from '@shipfox/react-ui/badge';\nimport {Dot} from '@shipfox/react-ui/dot';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {TimeTickerProvider, useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn, humanDuration} from '@shipfox/react-ui/utils';\nimport type {ReactNode} from 'react';\nimport {useEffect, useId, useMemo, useRef, useState} from 'react';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {\n isWorkflowStatus,\n type Job,\n type JobDisplayStatus,\n type JobExecution,\n type Step,\n type StepSourceLocation,\n} from '#core/workflow-run.js';\nimport {formatJobExecutionTimeLabel} from '../job-graph/job-duration-format.js';\nimport {\n buildStepListModel,\n defaultStepListJobExecution,\n humanizeStatus,\n type StepAttemptModel,\n type StepListEntryModel,\n type StepListModel,\n} from './step-list-model.js';\n\nexport interface StepExpandedContext {\n step: Step;\n stepId: string;\n stepLabel: string;\n sourceLocation: StepSourceLocation | null;\n attempt: number;\n attemptId: string;\n attemptStartedAt: string;\n attemptError: Record<string, unknown> | null;\n attemptStatus: string;\n carriedOver: boolean;\n}\n\nexport interface StepListEmptyState {\n title: string;\n description: string;\n status?: JobDisplayStatus | undefined;\n}\n\nexport interface StepListProps {\n job: Job;\n jobExecution?: JobExecution | undefined;\n selectedAttemptId?: string | null | undefined;\n defaultSelectedAttemptId?: string | undefined;\n onSelectedAttemptChange?: ((attemptId: string | undefined) => void) | undefined;\n onExpandedAttemptIdsChange?: ((attemptIds: readonly string[]) => void) | undefined;\n inspectorOpenAttemptId?: string | null | undefined;\n onInspectorOpenChange?: ((attemptId: string | null) => void) | undefined;\n autoSelectActiveAttempt?: boolean | undefined;\n emptyState?: StepListEmptyState | undefined;\n renderExpandedStep?: ((context: StepExpandedContext) => ReactNode) | undefined;\n renderInspector?: ((entry: StepListEntryModel) => ReactNode) | undefined;\n showHeader?: boolean | undefined;\n className?: string | undefined;\n}\n\nexport function StepList({\n job,\n jobExecution,\n selectedAttemptId,\n defaultSelectedAttemptId,\n onSelectedAttemptChange,\n onExpandedAttemptIdsChange,\n inspectorOpenAttemptId = null,\n onInspectorOpenChange,\n autoSelectActiveAttempt = false,\n emptyState,\n renderExpandedStep,\n renderInspector,\n showHeader = true,\n className,\n}: StepListProps) {\n const selectedJobExecution = jobExecution ?? defaultStepListJobExecution(job);\n const model = useMemo(\n () => buildStepListModel({job, jobExecution: selectedJobExecution}),\n [job, selectedJobExecution],\n );\n\n return (\n <StepListContent\n key={model.jobExecutionId}\n model={model}\n selectedAttemptId={selectedAttemptId}\n defaultSelectedAttemptId={defaultSelectedAttemptId}\n onSelectedAttemptChange={onSelectedAttemptChange}\n onExpandedAttemptIdsChange={onExpandedAttemptIdsChange}\n inspectorOpenAttemptId={inspectorOpenAttemptId}\n onInspectorOpenChange={onInspectorOpenChange}\n autoSelectActiveAttempt={autoSelectActiveAttempt}\n emptyState={emptyState}\n renderExpandedStep={renderExpandedStep}\n renderInspector={renderInspector}\n showHeader={showHeader}\n className={className}\n />\n );\n}\n\nfunction StepListContent({\n model,\n selectedAttemptId,\n defaultSelectedAttemptId,\n onSelectedAttemptChange,\n onExpandedAttemptIdsChange,\n inspectorOpenAttemptId = null,\n onInspectorOpenChange,\n autoSelectActiveAttempt,\n emptyState,\n renderExpandedStep,\n renderInspector,\n showHeader,\n className,\n}: Omit<StepListProps, 'job' | 'jobExecution'> & {model: StepListModel}) {\n const titleId = useId();\n const [localSelectedAttemptIds, setLocalSelectedAttemptIds] = useState<string[]>(() =>\n selectedAttemptId\n ? [selectedAttemptId]\n : defaultSelectedAttemptId\n ? [defaultSelectedAttemptId]\n : [],\n );\n const [userSelectedAttempt, setUserSelectedAttempt] = useState(false);\n const lastNotifiedSelectedAttemptId = useRef<string | null>(null);\n const autoSelectedAttemptIdRef = useRef<string | undefined>(undefined);\n const previousSelectedAttemptIdRef = useRef<string | null | undefined>(selectedAttemptId);\n const lastReportedExpandedAttemptIdsKeyRef = useRef<string | null>(null);\n const shouldUseControlledCollapsedState =\n selectedAttemptId === null && lastNotifiedSelectedAttemptId.current === null;\n const autoSelectedAttemptId =\n selectedAttemptId === undefined && autoSelectActiveAttempt && !userSelectedAttempt\n ? (autoSelectedAttemptIdRef.current ?? model.activeEntryId)\n : undefined;\n const autoSelectedAttemptIds = useMemo(\n () => (autoSelectedAttemptId ? [autoSelectedAttemptId] : []),\n [autoSelectedAttemptId],\n );\n const selectedAttemptIds = useMemo(\n () =>\n shouldUseControlledCollapsedState\n ? []\n : localSelectedAttemptIds.length > 0\n ? localSelectedAttemptIds\n : autoSelectedAttemptIds,\n [autoSelectedAttemptIds, localSelectedAttemptIds, shouldUseControlledCollapsedState],\n );\n const hasExpandedContent = renderExpandedStep !== undefined;\n\n useEffect(() => {\n const key = selectedAttemptIds.join('|');\n if (lastReportedExpandedAttemptIdsKeyRef.current === key) return;\n lastReportedExpandedAttemptIdsKeyRef.current = key;\n onExpandedAttemptIdsChange?.(selectedAttemptIds);\n }, [onExpandedAttemptIdsChange, selectedAttemptIds]);\n\n useEffect(() => {\n if (selectedAttemptId !== undefined) {\n previousSelectedAttemptIdRef.current = selectedAttemptId;\n return;\n }\n\n // A changing landing candidate is normal while a live job polls. Only reset the local\n // accordion when the parent has just cleared a previously controlled URL selection.\n const wasControlled = previousSelectedAttemptIdRef.current !== undefined;\n previousSelectedAttemptIdRef.current = undefined;\n if (!wasControlled) return;\n\n setLocalSelectedAttemptIds(defaultSelectedAttemptId ? [defaultSelectedAttemptId] : []);\n setUserSelectedAttempt(false);\n autoSelectedAttemptIdRef.current = undefined;\n }, [defaultSelectedAttemptId, selectedAttemptId]);\n\n useEffect(() => {\n if (\n selectedAttemptId !== undefined ||\n !autoSelectActiveAttempt ||\n userSelectedAttempt ||\n localSelectedAttemptIds.length > 0 ||\n autoSelectedAttemptIdRef.current !== undefined\n ) {\n return;\n }\n if (model.activeEntryId) autoSelectedAttemptIdRef.current = model.activeEntryId;\n }, [\n autoSelectActiveAttempt,\n localSelectedAttemptIds.length,\n model.activeEntryId,\n selectedAttemptId,\n userSelectedAttempt,\n ]);\n\n useEffect(() => {\n if (selectedAttemptId === undefined) return;\n\n const nextSelectedAttemptId = selectedAttemptId ?? null;\n if (lastNotifiedSelectedAttemptId.current === nextSelectedAttemptId) {\n lastNotifiedSelectedAttemptId.current = null;\n return;\n }\n\n setLocalSelectedAttemptIds(selectedAttemptId ? [selectedAttemptId] : []);\n setUserSelectedAttempt(true);\n }, [selectedAttemptId]);\n\n useEffect(() => {\n if (\n inspectorOpenAttemptId !== null &&\n !model.entries.some((entry) => entry.id === inspectorOpenAttemptId && !entry.carriedOver)\n ) {\n onInspectorOpenChange?.(null);\n }\n }, [inspectorOpenAttemptId, model.entries, onInspectorOpenChange]);\n\n function selectAttempt(nextAttemptIds: string[]) {\n const nextAttemptId = nextSelectedAttemptId(selectedAttemptIds, nextAttemptIds);\n setUserSelectedAttempt(true);\n setLocalSelectedAttemptIds(nextAttemptIds);\n lastNotifiedSelectedAttemptId.current = nextAttemptId ?? null;\n onSelectedAttemptChange?.(nextAttemptId);\n }\n\n const inspectorEntry = model.entries.find(\n (entry) => entry.id === inspectorOpenAttemptId && !entry.carriedOver,\n );\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <section\n aria-labelledby={showHeader ? titleId : undefined}\n className={cn(\n 'flex min-h-0 flex-col rounded-8 border border-border-neutral-base bg-background-neutral-base',\n className,\n )}\n >\n {showHeader ? (\n <div className=\"flex min-h-40 items-center border-b border-border-neutral-base px-row py-row\">\n <Text as=\"h2\" id={titleId} size=\"sm\" bold className=\"text-foreground-neutral-base\">\n {model.jobName}\n </Text>\n </div>\n ) : null}\n\n {model.entries.length === 0 ? (\n <StepListEmptyStateView emptyState={emptyState} />\n ) : (\n <Accordion\n type=\"multiple\"\n value={selectedAttemptIds}\n onValueChange={selectAttempt}\n asChild\n >\n <ol>\n {model.entries.map((entry, index) => {\n const selected = selectedAttemptIds.includes(entry.id);\n return (\n <StepRow\n key={entry.id}\n entry={entry}\n selected={selected}\n hasExpandedContent={hasExpandedContent}\n isLast={index === model.entries.length - 1}\n onSelect={() => {\n selectAttempt(\n hasExpandedContent\n ? toggleAttemptId(selectedAttemptIds, entry.id)\n : selected\n ? []\n : [entry.id],\n );\n }}\n onInspect={\n onInspectorOpenChange && !entry.carriedOver\n ? () => onInspectorOpenChange(entry.id)\n : undefined\n }\n expandedContent={\n selected\n ? renderExpandedStep?.({\n step: entry.step,\n stepId: entry.step.id,\n stepLabel: entry.step.label,\n sourceLocation: entry.step.sourceLocation,\n attempt: entry.attempt,\n attemptId: entry.id,\n attemptStartedAt: entry.startedAt,\n attemptError: entry.error,\n attemptStatus: entry.statusVisual.kind,\n carriedOver: entry.carriedOver,\n })\n : null\n }\n />\n );\n })}\n </ol>\n </Accordion>\n )}\n </section>\n {inspectorEntry ? renderInspector?.(inspectorEntry) : null}\n </TimeTickerProvider>\n );\n}\n\nfunction StepListEmptyStateView({\n emptyState = {\n title: 'No steps recorded',\n description: 'This job has not recorded any steps.',\n },\n}: {\n emptyState?: StepListEmptyState | undefined;\n}) {\n if (!emptyState.status) {\n return (\n <EmptyState\n className=\"min-h-120 p-panel\"\n icon=\"componentLine\"\n title={emptyState.title}\n description={emptyState.description}\n variant=\"compact\"\n />\n );\n }\n\n return (\n <div className=\"flex min-h-120 flex-col items-center justify-center gap-inline p-panel\">\n <StepListEmptyStateIcon status={emptyState.status} />\n <div className=\"text-center\">\n <Text size=\"sm\" className=\"text-foreground-neutral-subtle\">\n {emptyState.title}\n </Text>\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n {emptyState.description}\n </Text>\n </div>\n </div>\n );\n}\n\nfunction StepListEmptyStateIcon({status}: {status: JobDisplayStatus}) {\n if (status !== 'running') {\n return (\n <div className=\"flex size-32 items-center justify-center rounded-6 border border-border-neutral-strong bg-background-neutral-base p-tight\">\n <WorkflowStatusIcon status={status} size={20} tooltip={false} />\n </div>\n );\n }\n\n return (\n <div className=\"flex size-32 items-center justify-center rounded-6 border border-border-neutral-strong bg-background-neutral-base p-tight text-foreground-neutral-muted\">\n <Icon name=\"timerLine\" size={18} aria-hidden=\"true\" />\n </div>\n );\n}\n\nfunction StepRow({\n entry,\n selected,\n hasExpandedContent,\n isLast,\n onSelect,\n onInspect,\n expandedContent,\n}: {\n entry: StepListEntryModel;\n selected: boolean;\n hasExpandedContent: boolean;\n isLast: boolean;\n onSelect: () => void;\n onInspect?: (() => void) | undefined;\n expandedContent: ReactNode;\n}) {\n const shouldShowLabelTooltip = entry.step.label.length > 32;\n const rowContent = (\n <>\n <Icon\n name=\"chevronRight\"\n aria-hidden=\"true\"\n className={cn(\n 'size-14 shrink-0 text-foreground-neutral-muted transition-transform',\n selected && 'rotate-90',\n )}\n />\n <StepStatusIcon entry={entry} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex min-w-0 items-center gap-inline\">\n <Text size=\"sm\" bold className=\"truncate text-foreground-neutral-base\">\n {entry.step.label}\n </Text>\n {entry.step.attempts.length > 1 ? <StepAttemptChip attempt={entry} /> : null}\n {entry.carriedOver ? <CarriedOverBadge /> : null}\n </div>\n </div>\n <StepAttemptDurationLabel attempt={entry} />\n </>\n );\n const rowClasses = cn(\n 'group flex min-h-44 min-w-0 flex-1 items-center gap-x-inline bg-transparent px-row py-row text-left transition-colors hover:bg-transparent active:bg-transparent focus-visible:shadow-border-interactive-with-active focus-visible:outline-none',\n entry.carriedOver && 'opacity-[0.55]',\n );\n const button = hasExpandedContent ? (\n <AccordionTrigger\n showIcon={false}\n aria-label={entryAccessibleLabel(entry)}\n className={rowClasses}\n >\n {rowContent}\n </AccordionTrigger>\n ) : (\n <button\n type=\"button\"\n aria-expanded={false}\n aria-label={entryAccessibleLabel(entry)}\n onClick={onSelect}\n className={rowClasses}\n >\n {rowContent}\n </button>\n );\n const triggerNode = shouldShowLabelTooltip ? (\n <Tooltip>\n <TooltipTrigger asChild>{button}</TooltipTrigger>\n <TooltipContent>\n <span className=\"block max-w-320 break-words\">{entry.step.label}</span>\n </TooltipContent>\n </Tooltip>\n ) : (\n button\n );\n const row = (\n <>\n <div\n className={cn(\n 'group flex min-w-0 items-center gap-inline bg-background-neutral-base pr-[8px] transition-colors hover:bg-background-neutral-hover active:bg-background-neutral-pressed',\n selected && 'bg-background-neutral-hover',\n !hasExpandedContent && ['border-b border-border-neutral-base', isLast && 'border-b-0'],\n )}\n >\n <div className=\"min-w-0 flex-1\">{triggerNode}</div>\n {onInspect ? (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={`Inspect ${entry.step.label}, attempt ${entry.attempt}`}\n onClick={onInspect}\n className=\"flex size-28 shrink-0 items-center justify-center rounded-4 bg-transparent text-foreground-neutral-muted outline-none transition-colors hover:bg-transparent hover:text-foreground-neutral-base active:bg-transparent focus-visible:shadow-button-neutral-focus\"\n >\n <Icon name=\"informationLine\" size={14} aria-hidden=\"true\" />\n </button>\n </TooltipTrigger>\n <TooltipContent>Inspect step details</TooltipContent>\n </Tooltip>\n ) : null}\n </div>\n {selected && expandedContent ? (\n <AccordionContent className=\"border-t border-border-neutral-base bg-transparent px-0 py-0\">\n <div className=\"min-w-0\">{expandedContent}</div>\n </AccordionContent>\n ) : null}\n </>\n );\n\n if (hasExpandedContent) {\n return (\n <AccordionItem value={entry.id} asChild>\n <li>{row}</li>\n </AccordionItem>\n );\n }\n\n return <li>{row}</li>;\n}\n\nfunction CarriedOverBadge() {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"shrink-0\">\n <Badge variant=\"neutral\" size=\"2xs\">\n reused\n </Badge>\n </span>\n </TooltipTrigger>\n <TooltipContent>\n Carried over from a previous attempt; did not run in this attempt.\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction toggleAttemptId(selectedAttemptIds: readonly string[], attemptId: string): string[] {\n if (selectedAttemptIds.includes(attemptId)) {\n return selectedAttemptIds.filter((selectedAttemptId) => selectedAttemptId !== attemptId);\n }\n return [...selectedAttemptIds, attemptId];\n}\n\nfunction nextSelectedAttemptId(\n selectedAttemptIds: readonly string[],\n nextAttemptIds: readonly string[],\n): string | undefined {\n if (nextAttemptIds.length === 0) return undefined;\n\n const openedAttemptId = nextAttemptIds.find(\n (attemptId) => !selectedAttemptIds.includes(attemptId),\n );\n return openedAttemptId ?? nextAttemptIds.at(-1);\n}\n\nfunction StepStatusIcon({entry}: {entry: StepListEntryModel}) {\n if (isWorkflowStatus(entry.statusVisual.kind)) {\n return (\n <WorkflowStatusIcon\n status={entry.statusVisual.kind}\n ripple={entry.statusVisual.ripple}\n size={14}\n />\n );\n }\n\n return (\n <span role=\"img\" aria-label={entry.statusVisual.label} className=\"inline-flex shrink-0\">\n <Dot\n variant={entry.statusVisual.dot}\n ripple={entry.statusVisual.ripple}\n className=\"size-12 shrink-0\"\n />\n </span>\n );\n}\n\nconst attemptChipClasses: Record<NonNullable<BadgeVariant>, string> = {\n neutral: 'bg-tag-neutral-bg border-tag-neutral-border',\n info: 'bg-tag-blue-bg border-tag-blue-border',\n feature: 'bg-tag-purple-bg border-tag-purple-border',\n success: 'bg-tag-success-bg border-tag-success-border',\n warning: 'bg-tag-warning-bg border-tag-warning-border',\n error: 'bg-tag-error-bg border-tag-error-border',\n};\n\nfunction StepAttemptChip({attempt}: {attempt: StepAttemptModel}) {\n return (\n <div className=\"flex shrink-0 items-center gap-tight\" aria-hidden=\"true\">\n <span\n className={cn(\n 'inline-flex h-18 min-w-24 items-center justify-center rounded-4 border px-tight font-code text-xs leading-16 text-foreground-neutral-base',\n attemptChipClasses[attempt.statusVisual.badge ?? 'neutral'],\n )}\n >\n #{attempt.attempt}\n </span>\n </div>\n );\n}\n\nfunction entryAccessibleLabel(entry: StepListEntryModel): string {\n const parts = [entry.step.label, entry.statusVisual.label, `attempt ${entry.attempt}`];\n if (entry.step.error?.category) parts.push(humanizeStatus(entry.step.error.category));\n return parts.join(', ');\n}\n\nfunction StepAttemptDurationLabel({attempt}: {attempt: StepAttemptModel}) {\n const duration = attempt.displayDuration;\n if (duration === null) return null;\n\n if (duration.state === 'live') {\n return <LiveDurationText fromIso={duration.fromIso} />;\n }\n\n return <DurationText>{formatJobExecutionTimeLabel(duration)}</DurationText>;\n}\n\nfunction LiveDurationText({fromIso}: {fromIso: string}) {\n useTimeTick();\n return <DurationText>{humanDuration(fromIso)}</DurationText>;\n}\n\nfunction DurationText({children}: {children: string}) {\n return (\n <Code as=\"span\" variant=\"label\" className=\"shrink-0 tabular-nums text-foreground-neutral-muted\">\n {children}\n </Code>\n );\n}\n"],"names":["Accordion","AccordionContent","AccordionItem","AccordionTrigger","Badge","Dot","EmptyState","Icon","TimeTickerProvider","useTimeTick","Tooltip","TooltipContent","TooltipTrigger","Code","Text","cn","humanDuration","useEffect","useId","useMemo","useRef","useState","WorkflowStatusIcon","isWorkflowStatus","formatJobExecutionTimeLabel","buildStepListModel","defaultStepListJobExecution","humanizeStatus","StepList","job","jobExecution","selectedAttemptId","defaultSelectedAttemptId","onSelectedAttemptChange","onExpandedAttemptIdsChange","inspectorOpenAttemptId","onInspectorOpenChange","autoSelectActiveAttempt","emptyState","renderExpandedStep","renderInspector","showHeader","className","selectedJobExecution","model","StepListContent","jobExecutionId","titleId","localSelectedAttemptIds","setLocalSelectedAttemptIds","userSelectedAttempt","setUserSelectedAttempt","lastNotifiedSelectedAttemptId","autoSelectedAttemptIdRef","undefined","previousSelectedAttemptIdRef","lastReportedExpandedAttemptIdsKeyRef","shouldUseControlledCollapsedState","current","autoSelectedAttemptId","activeEntryId","autoSelectedAttemptIds","selectedAttemptIds","length","hasExpandedContent","key","join","wasControlled","nextSelectedAttemptId","entries","some","entry","id","carriedOver","selectAttempt","nextAttemptIds","nextAttemptId","inspectorEntry","find","intervalMs","reducedMotionIntervalMs","section","aria-labelledby","div","as","size","bold","jobName","StepListEmptyStateView","type","value","onValueChange","asChild","ol","map","index","selected","includes","StepRow","isLast","onSelect","toggleAttemptId","onInspect","expandedContent","step","stepId","stepLabel","label","sourceLocation","attempt","attemptId","attemptStartedAt","startedAt","attemptError","error","attemptStatus","statusVisual","kind","title","description","status","icon","variant","StepListEmptyStateIcon","tooltip","name","aria-hidden","shouldShowLabelTooltip","rowContent","StepStatusIcon","attempts","StepAttemptChip","CarriedOverBadge","StepAttemptDurationLabel","rowClasses","button","showIcon","aria-label","entryAccessibleLabel","aria-expanded","onClick","triggerNode","span","row","li","filter","openedAttemptId","at","ripple","role","dot","attemptChipClasses","neutral","info","feature","success","warning","badge","parts","category","push","duration","displayDuration","state","LiveDurationText","fromIso","DurationText","children"],"mappings":"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"}
@@ -1,4 +1,12 @@
1
1
  import type { WorkflowRunJobs } from '#core/workflow-run.js';
2
+ /**
3
+ * Reserves one full API preview and its overflow marker in a run row.
4
+ *
5
+ * Keep this derived from the API preview bound so the row reservation grows with the payload
6
+ * contract instead of silently drifting into the duration and timestamp columns.
7
+ */
8
+ export declare function jobStatusStripWidthForPreview(previewLimit: number): number;
9
+ export declare const JOB_STATUS_STRIP_WIDTH: number;
2
10
  export interface JobStatusStripProps {
3
11
  jobs: WorkflowRunJobs;
4
12
  className?: string | undefined;
@@ -14,7 +22,7 @@ export interface JobStatusStripProps {
14
22
  * job including those the server never sent. That split is what lets the overflow glyph
15
23
  * report a failure sitting past the preview instead of quietly dropping it.
16
24
  */
17
- export declare function JobStatusStrip({ jobs, className }: JobStatusStripProps): import("react").JSX.Element | null;
25
+ export declare const JobStatusStrip: import("react").MemoExoticComponent<({ jobs, className }: JobStatusStripProps) => import("react").JSX.Element | null>;
18
26
  /** "12 jobs: 9 succeeded, 2 failed, 1 running" — a count is not an accessible name on its own. */
19
27
  export declare function jobStatusSummary(jobs: WorkflowRunJobs): string;
20
28
  export declare function overflowCountLabel(hiddenCount: number): string;
@@ -1 +1 @@
1
- {"version":3,"file":"job-status-strip.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-list/job-status-strip.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAwB,eAAe,EAAC,MAAM,uBAAuB,CAAC;AA0ClF,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,eAAe,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,EAAC,IAAI,EAAE,SAAS,EAAC,EAAE,mBAAmB,sCAqDpE;AAgCD,kGAAkG;AAClG,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAO9D;AA0BD,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAI9D"}
1
+ {"version":3,"file":"job-status-strip.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-list/job-status-strip.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAwB,eAAe,EAAC,MAAM,uBAAuB,CAAC;AAsBlF;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAI1E;AAED,eAAO,MAAM,sBAAsB,QAAgE,CAAC;AAgBpG,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,eAAe,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,4DAAmD,mBAAmB,wCAwD/F,CAAC;AAgCH,kGAAkG;AAClG,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAO9D;AA0BD,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAI9D"}