@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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +29 -0
- package/dist/components/job-detail/job-detail-view.d.ts.map +1 -1
- package/dist/components/job-detail/job-detail-view.js +231 -42
- package/dist/components/job-detail/job-detail-view.js.map +1 -1
- package/dist/components/job-detail/step-attempt-log-panel.d.ts +7 -1
- package/dist/components/job-detail/step-attempt-log-panel.d.ts.map +1 -1
- package/dist/components/job-detail/step-attempt-log-panel.js +37 -10
- package/dist/components/job-detail/step-attempt-log-panel.js.map +1 -1
- package/dist/components/step-list/step-list-model.d.ts +1 -0
- package/dist/components/step-list/step-list-model.d.ts.map +1 -1
- package/dist/components/step-list/step-list-model.js +1 -1
- package/dist/components/step-list/step-list-model.js.map +1 -1
- package/dist/components/step-list/step-list.d.ts +2 -1
- package/dist/components/step-list/step-list.d.ts.map +1 -1
- package/dist/components/step-list/step-list.js +24 -7
- package/dist/components/step-list/step-list.js.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.d.ts +9 -1
- package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.js +22 -11
- package/dist/components/workflow-run-list/job-status-strip.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-filters.js +1 -1
- package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.js +2 -2
- package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.js +34 -22
- package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.d.ts +3 -3
- package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.js +13 -10
- package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.js +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.js +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.js +43 -36
- package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
- package/dist/components/workflow-source-panel/workflow-source-content.js +1 -1
- package/dist/components/workflow-source-panel/workflow-source-content.js.map +1 -1
- package/dist/pages/project-workflows-page.d.ts.map +1 -1
- package/dist/pages/project-workflows-page.js +3 -2
- package/dist/pages/project-workflows-page.js.map +1 -1
- package/dist/pages/workflow-job-detail-page.d.ts.map +1 -1
- package/dist/pages/workflow-job-detail-page.js +3 -2
- package/dist/pages/workflow-job-detail-page.js.map +1 -1
- package/dist/pages/workflow-run-detail-page.js +2 -1
- package/dist/pages/workflow-run-detail-page.js.map +1 -1
- package/dist/pages/workflow-run-list-page.d.ts.map +1 -1
- package/dist/pages/workflow-run-list-page.js +9 -11
- package/dist/pages/workflow-run-list-page.js.map +1 -1
- package/dist/routes/job-detail.d.ts +1 -1
- package/dist/routes/job-detail.js +1 -1
- package/dist/routes/job-detail.js.map +1 -1
- package/dist/routes/run-detail.d.ts +1 -1
- package/dist/routes/run-detail.js +1 -1
- package/dist/routes/run-detail.js.map +1 -1
- package/dist/routes/runs.d.ts +1 -1
- package/dist/routes/runs.js +1 -1
- package/dist/routes/runs.js.map +1 -1
- package/dist/routes/workflows.d.ts +3 -0
- package/dist/routes/workflows.js +3 -0
- package/dist/routes/workflows.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/src/components/job-detail/job-detail-view.tsx +263 -47
- package/src/components/job-detail/step-attempt-log-panel.test.tsx +30 -12
- package/src/components/job-detail/step-attempt-log-panel.tsx +68 -10
- package/src/components/job-graph/job-graph.stories.tsx +1 -1
- package/src/components/job-graph/job-node.stories.tsx +1 -1
- package/src/components/step-list/step-list-model.ts +1 -1
- package/src/components/step-list/step-list.test.tsx +29 -1
- package/src/components/step-list/step-list.tsx +28 -9
- package/src/components/workflow-run-list/job-status-strip.tsx +25 -12
- package/src/components/workflow-run-list/workflow-run-filters.tsx +1 -1
- package/src/components/workflow-run-list/workflow-run-list-states.tsx +2 -2
- package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +53 -4
- package/src/components/workflow-run-list/workflow-run-list-view.tsx +28 -22
- package/src/components/workflow-run-list/workflow-run-list.stories.tsx +9 -20
- package/src/components/workflow-run-list/workflow-run-row.tsx +17 -13
- package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +2 -2
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +2 -1
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +1 -4
- package/src/components/workflow-run-view/run-workspace-nav.tsx +1 -1
- package/src/components/workflow-run-view/workflow-run-states.tsx +1 -4
- package/src/components/workflow-run-view/workflow-run-view.stories.tsx +140 -0
- package/src/components/workflow-run-view/workflow-run-view.test.tsx +38 -7
- package/src/components/workflow-run-view/workflow-run-view.tsx +45 -37
- package/src/components/workflow-source-panel/workflow-source-content.tsx +2 -2
- package/src/pages/project-workflows-page.tsx +3 -2
- package/src/pages/workflow-job-detail-page.test.tsx +32 -0
- package/src/pages/workflow-job-detail-page.tsx +5 -1
- package/src/pages/workflow-run-detail-page.tsx +1 -1
- package/src/pages/workflow-run-list-page.tsx +9 -11
- package/src/pages/workflow-run-pages.test.tsx +26 -0
- package/src/routes/job-detail.tsx +1 -1
- package/src/routes/run-detail.tsx +1 -1
- package/src/routes/runs.tsx +1 -1
- package/src/routes/workflows.tsx +1 -0
- 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,+
|
|
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-
|
|
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.
|
|
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.
|
|
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-
|
|
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"}
|
|
@@ -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":"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"}
|
|
@@ -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,+
|
|
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-
|
|
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-
|
|
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":"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"}
|
|
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
|
|
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":"
|
|
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"}
|