@shipfox/client-workflows 16.0.0 → 18.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 +32 -0
- package/dist/components/identifier/identifier.js +2 -2
- package/dist/components/identifier/identifier.js.map +1 -1
- package/dist/components/job-detail/agent-config-failure-callout.d.ts.map +1 -1
- package/dist/components/job-detail/agent-config-failure-callout.js +1 -1
- package/dist/components/job-detail/agent-config-failure-callout.js.map +1 -1
- package/dist/components/job-detail/job-context-panel.d.ts +6 -0
- package/dist/components/job-detail/job-context-panel.d.ts.map +1 -0
- package/dist/components/job-detail/job-context-panel.js +228 -0
- package/dist/components/job-detail/job-context-panel.js.map +1 -0
- package/dist/components/job-detail/job-detail-header.d.ts +3 -1
- package/dist/components/job-detail/job-detail-header.d.ts.map +1 -1
- package/dist/components/job-detail/job-detail-header.js +54 -48
- package/dist/components/job-detail/job-detail-header.js.map +1 -1
- package/dist/components/job-detail/job-detail-view.d.ts +1 -1
- package/dist/components/job-detail/job-detail-view.d.ts.map +1 -1
- package/dist/components/job-detail/job-detail-view.js +100 -57
- package/dist/components/job-detail/job-detail-view.js.map +1 -1
- package/dist/components/job-detail/job-empty-states.d.ts +4 -0
- package/dist/components/job-detail/job-empty-states.d.ts.map +1 -1
- package/dist/components/job-detail/job-empty-states.js +60 -11
- package/dist/components/job-detail/job-empty-states.js.map +1 -1
- package/dist/components/job-detail/job-execution-switcher.js +4 -4
- package/dist/components/job-detail/job-execution-switcher.js.map +1 -1
- package/dist/components/job-detail/json-code.d.ts +14 -0
- package/dist/components/job-detail/json-code.d.ts.map +1 -0
- package/dist/components/job-detail/json-code.js +93 -0
- package/dist/components/job-detail/json-code.js.map +1 -0
- package/dist/components/job-detail/step-attempt-log-panel.d.ts.map +1 -1
- package/dist/components/job-detail/step-attempt-log-panel.js +9 -6
- package/dist/components/job-detail/step-attempt-log-panel.js.map +1 -1
- package/dist/components/job-detail/step-troubleshooting.d.ts +18 -0
- package/dist/components/job-detail/step-troubleshooting.d.ts.map +1 -0
- package/dist/components/job-detail/step-troubleshooting.js +497 -0
- package/dist/components/job-detail/step-troubleshooting.js.map +1 -0
- package/dist/components/job-graph/job-graph-content.d.ts.map +1 -1
- package/dist/components/job-graph/job-graph-content.js +5 -4
- package/dist/components/job-graph/job-graph-content.js.map +1 -1
- package/dist/components/job-graph/job-node.js +3 -3
- package/dist/components/job-graph/job-node.js.map +1 -1
- package/dist/components/step-list/index.d.ts +1 -0
- package/dist/components/step-list/index.d.ts.map +1 -1
- package/dist/components/step-list/index.js.map +1 -1
- package/dist/components/step-list/step-list-model.d.ts.map +1 -1
- package/dist/components/step-list/step-list-model.js +6 -0
- package/dist/components/step-list/step-list-model.js.map +1 -1
- package/dist/components/step-list/step-list.d.ts +5 -1
- package/dist/components/step-list/step-list.d.ts.map +1 -1
- package/dist/components/step-list/step-list.js +117 -82
- package/dist/components/step-list/step-list.js.map +1 -1
- package/dist/components/workflow-run-duration-label.js +1 -1
- package/dist/components/workflow-run-duration-label.js.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.js +6 -6
- package/dist/components/workflow-run-list/job-status-strip.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js +1 -1
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-filters.js +4 -4
- package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.js +6 -6
- package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.js +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.js +7 -7
- package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +1 -1
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts +0 -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 +8 -13
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-run-tabs/index.d.ts +1 -1
- package/dist/components/workflow-run-tabs/index.d.ts.map +1 -1
- package/dist/components/workflow-run-tabs/index.js.map +1 -1
- package/dist/components/workflow-run-tabs/run-annotation-count-chip.js +2 -2
- package/dist/components/workflow-run-tabs/run-annotation-count-chip.js.map +1 -1
- package/dist/components/workflow-run-tabs/run-annotation-list.d.ts +9 -2
- package/dist/components/workflow-run-tabs/run-annotation-list.d.ts.map +1 -1
- package/dist/components/workflow-run-tabs/run-annotation-list.js +28 -19
- package/dist/components/workflow-run-tabs/run-annotation-list.js.map +1 -1
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts.map +1 -1
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.js +5 -5
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.js.map +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.js +11 -10
- package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.js +6 -6
- 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 +72 -12
- package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
- package/dist/core/entities/job-execution.d.ts +9 -0
- package/dist/core/entities/job-execution.d.ts.map +1 -1
- package/dist/core/entities/job-execution.js.map +1 -1
- package/dist/core/entities/job.d.ts +10 -1
- package/dist/core/entities/job.d.ts.map +1 -1
- package/dist/core/entities/job.js.map +1 -1
- package/dist/core/entities/step-attempt.d.ts +29 -0
- package/dist/core/entities/step-attempt.d.ts.map +1 -1
- package/dist/core/entities/step-attempt.js.map +1 -1
- package/dist/core/entities/step.d.ts +3 -1
- package/dist/core/entities/step.d.ts.map +1 -1
- package/dist/core/entities/step.js.map +1 -1
- package/dist/core/run-annotation.d.ts +6 -0
- package/dist/core/run-annotation.d.ts.map +1 -1
- package/dist/core/run-annotation.js.map +1 -1
- package/dist/core/workflow-run.d.ts +1 -1
- package/dist/core/workflow-run.d.ts.map +1 -1
- package/dist/core/workflow-run.js.map +1 -1
- package/dist/hooks/api/annotations.d.ts +63 -0
- package/dist/hooks/api/annotations.d.ts.map +1 -0
- package/dist/hooks/api/annotations.js +145 -0
- package/dist/hooks/api/annotations.js.map +1 -0
- package/dist/hooks/api/step-attempt-detail.d.ts +14 -0
- package/dist/hooks/api/step-attempt-detail.d.ts.map +1 -0
- package/dist/hooks/api/step-attempt-detail.js +43 -0
- package/dist/hooks/api/step-attempt-detail.js.map +1 -0
- package/dist/hooks/api/workflow-run-mapper.d.ts +9 -2
- package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.js +48 -0
- package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
- package/dist/pages/project-workflows-page.js +19 -19
- package/dist/pages/project-workflows-page.js.map +1 -1
- package/dist/pages/workflow-run-list-page.js +1 -1
- package/dist/pages/workflow-run-list-page.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/components/identifier/identifier.tsx +2 -2
- package/src/components/job-detail/agent-config-failure-callout.tsx +5 -1
- package/src/components/job-detail/job-context-panel.test.tsx +72 -0
- package/src/components/job-detail/job-context-panel.tsx +241 -0
- package/src/components/job-detail/job-detail-header.tsx +52 -46
- package/src/components/job-detail/job-detail-view.stories.tsx +555 -53
- package/src/components/job-detail/job-detail-view.tsx +105 -59
- package/src/components/job-detail/job-empty-states.test.ts +105 -1
- package/src/components/job-detail/job-empty-states.tsx +77 -10
- package/src/components/job-detail/job-execution-switcher.tsx +5 -5
- package/src/components/job-detail/json-code.tsx +120 -0
- package/src/components/job-detail/step-attempt-log-panel.test.tsx +1 -0
- package/src/components/job-detail/step-attempt-log-panel.tsx +17 -6
- package/src/components/job-detail/step-troubleshooting.stories.tsx +120 -0
- package/src/components/job-detail/step-troubleshooting.test.tsx +337 -0
- package/src/components/job-detail/step-troubleshooting.tsx +541 -0
- package/src/components/job-graph/job-graph-content.tsx +4 -3
- package/src/components/job-graph/job-node.tsx +3 -3
- package/src/components/step-list/index.ts +1 -0
- package/src/components/step-list/step-list-model.ts +6 -0
- package/src/components/step-list/step-list.test.tsx +75 -2
- package/src/components/step-list/step-list.tsx +73 -20
- package/src/components/workflow-run-duration-label.tsx +1 -1
- package/src/components/workflow-run-list/job-status-strip.tsx +6 -6
- package/src/components/workflow-run-list/workflow-run-filter-menu.tsx +1 -1
- package/src/components/workflow-run-list/workflow-run-filters.tsx +4 -4
- package/src/components/workflow-run-list/workflow-run-list-states.tsx +6 -6
- package/src/components/workflow-run-list/workflow-run-list-view.tsx +1 -1
- package/src/components/workflow-run-list/workflow-run-row.tsx +7 -7
- package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +1 -1
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +1 -1
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +11 -11
- package/src/components/workflow-run-tabs/index.ts +5 -1
- package/src/components/workflow-run-tabs/run-annotation-count-chip.tsx +2 -2
- package/src/components/workflow-run-tabs/run-annotation-list.tsx +25 -10
- package/src/components/workflow-run-tabs/run-annotation-summary-line.tsx +5 -5
- package/src/components/workflow-run-view/run-workspace-nav.tsx +18 -11
- package/src/components/workflow-run-view/workflow-run-states.tsx +6 -6
- package/src/components/workflow-run-view/workflow-run-view.tsx +114 -14
- package/src/core/entities/job-execution.ts +9 -0
- package/src/core/entities/job.ts +12 -1
- package/src/core/entities/step-attempt.ts +33 -0
- package/src/core/entities/step.ts +3 -1
- package/src/core/run-annotation.ts +6 -0
- package/src/core/workflow-run.ts +4 -0
- package/src/hooks/api/annotations.test.ts +84 -0
- package/src/hooks/api/annotations.ts +240 -0
- package/src/hooks/api/step-attempt-detail.ts +67 -0
- package/src/hooks/api/workflow-run-mapper.ts +47 -0
- package/src/hooks/api/workflow-runs.test.tsx +61 -1
- package/src/pages/project-workflows-page.tsx +19 -19
- package/src/pages/workflow-job-detail-page.test.tsx +52 -1
- package/src/pages/workflow-run-list-page.tsx +1 -1
- package/test/fixtures/workflow-run.ts +7 -0
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -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, autoSelectActiveAttempt = false, emptyState, renderExpandedStep, showHeader = true, className }) {
|
|
16
|
+
export function StepList({ job, jobExecution, selectedAttemptId, defaultSelectedAttemptId, onSelectedAttemptChange, 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,14 +27,17 @@ export function StepList({ job, jobExecution, selectedAttemptId, defaultSelected
|
|
|
27
27
|
selectedAttemptId: selectedAttemptId,
|
|
28
28
|
defaultSelectedAttemptId: defaultSelectedAttemptId,
|
|
29
29
|
onSelectedAttemptChange: onSelectedAttemptChange,
|
|
30
|
+
inspectorOpenAttemptId: inspectorOpenAttemptId,
|
|
31
|
+
onInspectorOpenChange: onInspectorOpenChange,
|
|
30
32
|
autoSelectActiveAttempt: autoSelectActiveAttempt,
|
|
31
33
|
emptyState: emptyState,
|
|
32
34
|
renderExpandedStep: renderExpandedStep,
|
|
35
|
+
renderInspector: renderInspector,
|
|
33
36
|
showHeader: showHeader,
|
|
34
37
|
className: className
|
|
35
38
|
}, model.jobExecutionId);
|
|
36
39
|
}
|
|
37
|
-
function StepListContent({ model, selectedAttemptId, defaultSelectedAttemptId, onSelectedAttemptChange, autoSelectActiveAttempt, emptyState, renderExpandedStep, showHeader, className }) {
|
|
40
|
+
function StepListContent({ model, selectedAttemptId, defaultSelectedAttemptId, onSelectedAttemptChange, inspectorOpenAttemptId = null, onInspectorOpenChange, autoSelectActiveAttempt, emptyState, renderExpandedStep, renderInspector, showHeader, className }) {
|
|
38
41
|
const titleId = useId();
|
|
39
42
|
const [localSelectedAttemptIds, setLocalSelectedAttemptIds] = useState(()=>selectedAttemptId ? [
|
|
40
43
|
selectedAttemptId
|
|
@@ -97,6 +100,15 @@ function StepListContent({ model, selectedAttemptId, defaultSelectedAttemptId, o
|
|
|
97
100
|
}, [
|
|
98
101
|
selectedAttemptId
|
|
99
102
|
]);
|
|
103
|
+
useEffect(()=>{
|
|
104
|
+
if (inspectorOpenAttemptId !== null && !model.entries.some((entry)=>entry.id === inspectorOpenAttemptId && !entry.carriedOver)) {
|
|
105
|
+
onInspectorOpenChange?.(null);
|
|
106
|
+
}
|
|
107
|
+
}, [
|
|
108
|
+
inspectorOpenAttemptId,
|
|
109
|
+
model.entries,
|
|
110
|
+
onInspectorOpenChange
|
|
111
|
+
]);
|
|
100
112
|
function selectAttempt(nextAttemptIds) {
|
|
101
113
|
const nextAttemptId = nextSelectedAttemptId(selectedAttemptIds, nextAttemptIds);
|
|
102
114
|
setUserSelectedAttempt(true);
|
|
@@ -104,61 +116,67 @@ function StepListContent({ model, selectedAttemptId, defaultSelectedAttemptId, o
|
|
|
104
116
|
lastNotifiedSelectedAttemptId.current = nextAttemptId ?? null;
|
|
105
117
|
onSelectedAttemptChange?.(nextAttemptId);
|
|
106
118
|
}
|
|
107
|
-
|
|
119
|
+
const inspectorEntry = model.entries.find((entry)=>entry.id === inspectorOpenAttemptId && !entry.carriedOver);
|
|
120
|
+
return /*#__PURE__*/ _jsxs(TimeTickerProvider, {
|
|
108
121
|
intervalMs: 1000,
|
|
109
122
|
reducedMotionIntervalMs: 10_000,
|
|
110
|
-
children:
|
|
111
|
-
"
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
children:
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
123
|
+
children: [
|
|
124
|
+
/*#__PURE__*/ _jsxs("section", {
|
|
125
|
+
"aria-labelledby": showHeader ? titleId : undefined,
|
|
126
|
+
className: cn('flex min-h-0 flex-col rounded-8 border border-border-neutral-base bg-background-components-base', className),
|
|
127
|
+
children: [
|
|
128
|
+
showHeader ? /*#__PURE__*/ _jsx("div", {
|
|
129
|
+
className: "flex min-h-40 items-center border-b border-border-neutral-base px-row py-row",
|
|
130
|
+
children: /*#__PURE__*/ _jsx(Text, {
|
|
131
|
+
as: "h2",
|
|
132
|
+
id: titleId,
|
|
133
|
+
size: "sm",
|
|
134
|
+
bold: true,
|
|
135
|
+
className: "text-foreground-neutral-base",
|
|
136
|
+
children: model.jobName
|
|
137
|
+
})
|
|
138
|
+
}) : null,
|
|
139
|
+
model.entries.length === 0 ? /*#__PURE__*/ _jsx(StepListEmptyStateView, {
|
|
140
|
+
emptyState: emptyState
|
|
141
|
+
}) : /*#__PURE__*/ _jsx(Accordion, {
|
|
142
|
+
type: "multiple",
|
|
143
|
+
value: selectedAttemptIds,
|
|
144
|
+
onValueChange: selectAttempt,
|
|
145
|
+
asChild: true,
|
|
146
|
+
children: /*#__PURE__*/ _jsx("ol", {
|
|
147
|
+
children: model.entries.map((entry, index)=>{
|
|
148
|
+
const selected = selectedAttemptIds.includes(entry.id);
|
|
149
|
+
return /*#__PURE__*/ _jsx(StepRow, {
|
|
150
|
+
entry: entry,
|
|
151
|
+
selected: selected,
|
|
152
|
+
hasExpandedContent: hasExpandedContent,
|
|
153
|
+
isLast: index === model.entries.length - 1,
|
|
154
|
+
onSelect: ()=>{
|
|
155
|
+
selectAttempt(hasExpandedContent ? toggleAttemptId(selectedAttemptIds, entry.id) : selected ? [] : [
|
|
156
|
+
entry.id
|
|
157
|
+
]);
|
|
158
|
+
},
|
|
159
|
+
onInspect: onInspectorOpenChange && !entry.carriedOver ? ()=>onInspectorOpenChange(entry.id) : undefined,
|
|
160
|
+
expandedContent: selected ? renderExpandedStep?.({
|
|
161
|
+
step: entry.step,
|
|
162
|
+
stepId: entry.step.id,
|
|
163
|
+
stepLabel: entry.step.label,
|
|
164
|
+
sourceLocation: entry.step.sourceLocation,
|
|
165
|
+
attempt: entry.attempt,
|
|
166
|
+
attemptId: entry.id,
|
|
167
|
+
attemptStartedAt: entry.startedAt,
|
|
168
|
+
attemptError: entry.error,
|
|
169
|
+
attemptStatus: entry.statusVisual.kind,
|
|
170
|
+
carriedOver: entry.carriedOver
|
|
171
|
+
}) : null
|
|
172
|
+
}, entry.id);
|
|
173
|
+
})
|
|
157
174
|
})
|
|
158
175
|
})
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
176
|
+
]
|
|
177
|
+
}),
|
|
178
|
+
inspectorEntry ? renderInspector?.(inspectorEntry) : null
|
|
179
|
+
]
|
|
162
180
|
});
|
|
163
181
|
}
|
|
164
182
|
function StepListEmptyStateView({ emptyState = {
|
|
@@ -167,7 +185,7 @@ function StepListEmptyStateView({ emptyState = {
|
|
|
167
185
|
} }) {
|
|
168
186
|
if (!emptyState.status) {
|
|
169
187
|
return /*#__PURE__*/ _jsx(EmptyState, {
|
|
170
|
-
className: "min-h-120
|
|
188
|
+
className: "min-h-120 p-panel",
|
|
171
189
|
icon: "componentLine",
|
|
172
190
|
title: emptyState.title,
|
|
173
191
|
description: emptyState.description,
|
|
@@ -175,7 +193,7 @@ function StepListEmptyStateView({ emptyState = {
|
|
|
175
193
|
});
|
|
176
194
|
}
|
|
177
195
|
return /*#__PURE__*/ _jsxs("div", {
|
|
178
|
-
className: "flex min-h-120 flex-col items-center justify-center gap-
|
|
196
|
+
className: "flex min-h-120 flex-col items-center justify-center gap-inline p-panel",
|
|
179
197
|
children: [
|
|
180
198
|
/*#__PURE__*/ _jsx(StepListEmptyStateIcon, {
|
|
181
199
|
status: emptyState.status
|
|
@@ -201,7 +219,7 @@ function StepListEmptyStateView({ emptyState = {
|
|
|
201
219
|
function StepListEmptyStateIcon({ status }) {
|
|
202
220
|
if (status !== 'running') {
|
|
203
221
|
return /*#__PURE__*/ _jsx("div", {
|
|
204
|
-
className: "flex size-32 items-center justify-center rounded-6 border border-border-neutral-strong bg-background-neutral-base p-
|
|
222
|
+
className: "flex size-32 items-center justify-center rounded-6 border border-border-neutral-strong bg-background-neutral-base p-tight",
|
|
205
223
|
children: /*#__PURE__*/ _jsx(WorkflowStatusIcon, {
|
|
206
224
|
status: status,
|
|
207
225
|
size: 20,
|
|
@@ -210,7 +228,7 @@ function StepListEmptyStateIcon({ status }) {
|
|
|
210
228
|
});
|
|
211
229
|
}
|
|
212
230
|
return /*#__PURE__*/ _jsx("div", {
|
|
213
|
-
className: "flex size-32 items-center justify-center rounded-6 border border-border-neutral-strong bg-background-neutral-base p-
|
|
231
|
+
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",
|
|
214
232
|
children: /*#__PURE__*/ _jsx(Icon, {
|
|
215
233
|
name: "timerLine",
|
|
216
234
|
size: 18,
|
|
@@ -218,7 +236,7 @@ function StepListEmptyStateIcon({ status }) {
|
|
|
218
236
|
})
|
|
219
237
|
});
|
|
220
238
|
}
|
|
221
|
-
function StepRow({ entry, selected, hasExpandedContent, onSelect, expandedContent }) {
|
|
239
|
+
function StepRow({ entry, selected, hasExpandedContent, isLast, onSelect, onInspect, expandedContent }) {
|
|
222
240
|
const shouldShowLabelTooltip = entry.step.label.length > 32;
|
|
223
241
|
const rowContent = /*#__PURE__*/ _jsxs(_Fragment, {
|
|
224
242
|
children: [
|
|
@@ -233,7 +251,7 @@ function StepRow({ entry, selected, hasExpandedContent, onSelect, expandedConten
|
|
|
233
251
|
/*#__PURE__*/ _jsx("div", {
|
|
234
252
|
className: "min-w-0 flex-1",
|
|
235
253
|
children: /*#__PURE__*/ _jsxs("div", {
|
|
236
|
-
className: "flex min-w-0 items-center gap-
|
|
254
|
+
className: "flex min-w-0 items-center gap-inline",
|
|
237
255
|
children: [
|
|
238
256
|
/*#__PURE__*/ _jsx(Text, {
|
|
239
257
|
size: "sm",
|
|
@@ -253,7 +271,7 @@ function StepRow({ entry, selected, hasExpandedContent, onSelect, expandedConten
|
|
|
253
271
|
})
|
|
254
272
|
]
|
|
255
273
|
});
|
|
256
|
-
const rowClasses = cn('group
|
|
274
|
+
const rowClasses = cn('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', entry.carriedOver && 'opacity-[0.55]');
|
|
257
275
|
const button = hasExpandedContent ? /*#__PURE__*/ _jsx(AccordionTrigger, {
|
|
258
276
|
showIcon: false,
|
|
259
277
|
"aria-label": entryAccessibleLabel(entry),
|
|
@@ -283,26 +301,44 @@ function StepRow({ entry, selected, hasExpandedContent, onSelect, expandedConten
|
|
|
283
301
|
}) : button;
|
|
284
302
|
const row = /*#__PURE__*/ _jsxs(_Fragment, {
|
|
285
303
|
children: [
|
|
286
|
-
|
|
304
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
305
|
+
className: cn('group flex min-w-0 items-center gap-inline pr-[8px] transition-colors hover:bg-background-components-hover active:bg-background-components-pressed', selected && 'bg-background-components-hover', !hasExpandedContent && [
|
|
306
|
+
'border-b border-border-neutral-base',
|
|
307
|
+
isLast && 'border-b-0'
|
|
308
|
+
]),
|
|
309
|
+
children: [
|
|
310
|
+
/*#__PURE__*/ _jsx("div", {
|
|
311
|
+
className: "min-w-0 flex-1",
|
|
312
|
+
children: triggerNode
|
|
313
|
+
}),
|
|
314
|
+
onInspect ? /*#__PURE__*/ _jsxs(Tooltip, {
|
|
315
|
+
children: [
|
|
316
|
+
/*#__PURE__*/ _jsx(TooltipTrigger, {
|
|
317
|
+
asChild: true,
|
|
318
|
+
children: /*#__PURE__*/ _jsx("button", {
|
|
319
|
+
type: "button",
|
|
320
|
+
"aria-label": `Inspect ${entry.step.label}, attempt ${entry.attempt}`,
|
|
321
|
+
onClick: onInspect,
|
|
322
|
+
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",
|
|
323
|
+
children: /*#__PURE__*/ _jsx(Icon, {
|
|
324
|
+
name: "informationLine",
|
|
325
|
+
size: 14,
|
|
326
|
+
"aria-hidden": "true"
|
|
327
|
+
})
|
|
328
|
+
})
|
|
329
|
+
}),
|
|
330
|
+
/*#__PURE__*/ _jsx(TooltipContent, {
|
|
331
|
+
children: "Inspect step details"
|
|
332
|
+
})
|
|
333
|
+
]
|
|
334
|
+
}) : null
|
|
335
|
+
]
|
|
336
|
+
}),
|
|
287
337
|
selected && expandedContent ? /*#__PURE__*/ _jsx(AccordionContent, {
|
|
288
|
-
className: "border-t border-border-neutral-base bg-
|
|
289
|
-
children: /*#__PURE__*/
|
|
290
|
-
className: "
|
|
291
|
-
children:
|
|
292
|
-
/*#__PURE__*/ _jsx("span", {
|
|
293
|
-
"aria-hidden": "true"
|
|
294
|
-
}),
|
|
295
|
-
/*#__PURE__*/ _jsx("span", {
|
|
296
|
-
"aria-hidden": "true"
|
|
297
|
-
}),
|
|
298
|
-
/*#__PURE__*/ _jsx("div", {
|
|
299
|
-
className: "min-w-0",
|
|
300
|
-
children: expandedContent
|
|
301
|
-
}),
|
|
302
|
-
/*#__PURE__*/ _jsx("span", {
|
|
303
|
-
"aria-hidden": "true"
|
|
304
|
-
})
|
|
305
|
-
]
|
|
338
|
+
className: "border-t border-border-neutral-base bg-transparent px-0 py-0",
|
|
339
|
+
children: /*#__PURE__*/ _jsx("div", {
|
|
340
|
+
className: "min-w-0",
|
|
341
|
+
children: expandedContent
|
|
306
342
|
})
|
|
307
343
|
}) : null
|
|
308
344
|
]
|
|
@@ -317,7 +353,6 @@ function StepRow({ entry, selected, hasExpandedContent, onSelect, expandedConten
|
|
|
317
353
|
});
|
|
318
354
|
}
|
|
319
355
|
return /*#__PURE__*/ _jsx("li", {
|
|
320
|
-
className: "border-b border-border-neutral-base last:border-b-0",
|
|
321
356
|
children: row
|
|
322
357
|
});
|
|
323
358
|
}
|
|
@@ -384,10 +419,10 @@ const attemptChipClasses = {
|
|
|
384
419
|
};
|
|
385
420
|
function StepAttemptChip({ attempt }) {
|
|
386
421
|
return /*#__PURE__*/ _jsx("div", {
|
|
387
|
-
className: "flex shrink-0 items-center gap-
|
|
422
|
+
className: "flex shrink-0 items-center gap-tight",
|
|
388
423
|
"aria-hidden": "true",
|
|
389
424
|
children: /*#__PURE__*/ _jsxs("span", {
|
|
390
|
-
className: cn('inline-flex h-18 min-w-24 items-center justify-center rounded-4 border px-
|
|
425
|
+
className: cn('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', attemptChipClasses[attempt.statusVisual.badge ?? 'neutral']),
|
|
391
426
|
children: [
|
|
392
427
|
"#",
|
|
393
428
|
attempt.attempt
|
|
@@ -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 autoSelectActiveAttempt?: boolean | undefined;\n emptyState?: StepListEmptyState | undefined;\n renderExpandedStep?: ((context: StepExpandedContext) => 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 autoSelectActiveAttempt = false,\n emptyState,\n renderExpandedStep,\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 autoSelectActiveAttempt={autoSelectActiveAttempt}\n emptyState={emptyState}\n renderExpandedStep={renderExpandedStep}\n showHeader={showHeader}\n className={className}\n />\n );\n}\n\nfunction StepListContent({\n model,\n selectedAttemptId,\n defaultSelectedAttemptId,\n onSelectedAttemptChange,\n autoSelectActiveAttempt,\n emptyState,\n renderExpandedStep,\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 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 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-16 py-8\">\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) => {\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 onSelect={() => {\n selectAttempt(\n hasExpandedContent\n ? toggleAttemptId(selectedAttemptIds, entry.id)\n : selected\n ? []\n : [entry.id],\n );\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 </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 px-16 py-20\"\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-10 px-16 py-20\">\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-8\">\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-8 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 onSelect,\n expandedContent,\n}: {\n entry: StepListEntryModel;\n selected: boolean;\n hasExpandedContent: boolean;\n onSelect: () => void;\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-8\">\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 grid min-h-44 w-full grid-cols-[14px_14px_minmax(0,1fr)_auto] items-center gap-x-8 px-12 py-6 text-left transition-colors hover:bg-background-components-hover focus-visible:shadow-border-interactive-with-active focus-visible:outline-none',\n selected && 'bg-background-components-hover',\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 {triggerNode}\n {selected && expandedContent ? (\n <AccordionContent className=\"border-t border-border-neutral-base bg-background-neutral-base px-12 py-12\">\n <div className=\"grid grid-cols-[14px_14px_minmax(0,1fr)_auto] gap-x-8\">\n <span aria-hidden=\"true\" />\n <span aria-hidden=\"true\" />\n <div className=\"min-w-0\">{expandedContent}</div>\n <span aria-hidden=\"true\" />\n </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 className=\"border-b border-border-neutral-base last:border-b-0\">{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-4\" aria-hidden=\"true\">\n <span\n className={cn(\n 'inline-flex h-18 min-w-24 items-center justify-center rounded-4 border px-6 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","autoSelectActiveAttempt","emptyState","renderExpandedStep","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","selectAttempt","nextAttemptIds","nextAttemptId","intervalMs","reducedMotionIntervalMs","section","aria-labelledby","div","as","id","size","bold","jobName","entries","StepListEmptyStateView","type","value","onValueChange","asChild","ol","map","entry","selected","includes","StepRow","onSelect","toggleAttemptId","expandedContent","step","stepId","stepLabel","label","sourceLocation","attempt","attemptId","attemptStartedAt","startedAt","attemptError","error","attemptStatus","statusVisual","kind","carriedOver","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","find","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 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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"}
|
|
@@ -79,7 +79,7 @@ function DurationText({ children, className, kind, ariaLabel }) {
|
|
|
79
79
|
as: "span",
|
|
80
80
|
variant: "label",
|
|
81
81
|
"aria-label": ariaLabel,
|
|
82
|
-
className: cn('inline-flex shrink-0 items-center gap-
|
|
82
|
+
className: cn('inline-flex shrink-0 items-center gap-tight tabular-nums text-foreground-neutral-subtle', className),
|
|
83
83
|
children: [
|
|
84
84
|
/*#__PURE__*/ _jsx(Icon, {
|
|
85
85
|
name: GLYPH_BY_KIND[kind],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/workflow-run-duration-label.tsx"],"sourcesContent":["import {Icon} from '@shipfox/react-ui/icon';\nimport {useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Code} from '@shipfox/react-ui/typography';\nimport {cn, humanDuration} from '@shipfox/react-ui/utils';\nimport type {\n WorkflowRunAttemptDisplayDuration,\n WorkflowRunDisplayDuration,\n} from '#core/workflow-run.js';\n\n/**\n * The run list reads an attempt's own marks and cannot tell queue from run, so an unkinded\n * duration still renders as run time. Only surfaces holding the run's jobs pass a kind.\n */\nexport type WorkflowRunDurationDisplay =\n | WorkflowRunAttemptDisplayDuration\n | WorkflowRunDisplayDuration;\n\ntype DurationKind = 'queue' | 'run';\n\nconst GLYPH_BY_KIND = {queue: 'hourglassLine', run: 'timerLine'} as const;\nconst LIVE_VERB_BY_KIND = {queue: 'queued', run: 'running'} as const;\nconst FIXED_VERB_BY_KIND = {queue: 'queued', run: 'ran'} as const;\n\nexport function WorkflowRunDurationLabel({\n duration,\n className,\n}: {\n duration: WorkflowRunDurationDisplay | null;\n className?: string | undefined;\n}) {\n if (duration === null) return null;\n const kind = durationKind(duration);\n\n switch (duration.state) {\n case 'fixed': {\n const display = formatFixedDurationLabel(duration.elapsed);\n return (\n <DurationText\n className={className}\n kind={kind}\n ariaLabel={`${FIXED_VERB_BY_KIND[kind]} ${display}`}\n >\n {display}\n </DurationText>\n );\n }\n case 'live':\n return <LiveDurationText duration={duration} kind={kind} className={className} />;\n default: {\n const exhaustive: never = duration;\n return exhaustive;\n }\n }\n}\n\nexport function useWorkflowRunDurationAccessibleLabel(\n duration: WorkflowRunDurationDisplay | null,\n): string | undefined {\n useTimeTick();\n return workflowRunDurationAccessibleLabel(duration);\n}\n\nexport function workflowRunDurationAccessibleLabel(\n duration: WorkflowRunDurationDisplay | null,\n): string | undefined {\n if (duration === null) return undefined;\n const kind = durationKind(duration);\n\n switch (duration.state) {\n case 'live':\n return `${LIVE_VERB_BY_KIND[kind]} ${humanDuration(duration.fromIso)}`;\n case 'fixed':\n return `${FIXED_VERB_BY_KIND[kind]} ${formatFixedDurationLabel(duration.elapsed)}`;\n default: {\n const exhaustive: never = duration;\n return exhaustive;\n }\n }\n}\n\nfunction durationKind(duration: WorkflowRunDurationDisplay): DurationKind {\n return 'kind' in duration ? duration.kind : 'run';\n}\n\nfunction LiveDurationText({\n duration,\n kind,\n className,\n}: {\n duration: Extract<WorkflowRunDurationDisplay, {state: 'live'}>;\n kind: DurationKind;\n className?: string | undefined;\n}) {\n useTimeTick();\n const display = humanDuration(duration.fromIso);\n return (\n <DurationText\n className={className}\n kind={kind}\n ariaLabel={`${LIVE_VERB_BY_KIND[kind]} ${display}`}\n >\n {display}\n </DurationText>\n );\n}\n\nfunction DurationText({\n children,\n className,\n kind,\n ariaLabel,\n}: {\n children: string;\n className?: string | undefined;\n kind: DurationKind;\n ariaLabel?: string | undefined;\n}) {\n return (\n <Code\n as=\"span\"\n variant=\"label\"\n aria-label={ariaLabel}\n className={cn(\n 'inline-flex shrink-0 items-center gap-
|
|
1
|
+
{"version":3,"sources":["../../src/components/workflow-run-duration-label.tsx"],"sourcesContent":["import {Icon} from '@shipfox/react-ui/icon';\nimport {useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Code} from '@shipfox/react-ui/typography';\nimport {cn, humanDuration} from '@shipfox/react-ui/utils';\nimport type {\n WorkflowRunAttemptDisplayDuration,\n WorkflowRunDisplayDuration,\n} from '#core/workflow-run.js';\n\n/**\n * The run list reads an attempt's own marks and cannot tell queue from run, so an unkinded\n * duration still renders as run time. Only surfaces holding the run's jobs pass a kind.\n */\nexport type WorkflowRunDurationDisplay =\n | WorkflowRunAttemptDisplayDuration\n | WorkflowRunDisplayDuration;\n\ntype DurationKind = 'queue' | 'run';\n\nconst GLYPH_BY_KIND = {queue: 'hourglassLine', run: 'timerLine'} as const;\nconst LIVE_VERB_BY_KIND = {queue: 'queued', run: 'running'} as const;\nconst FIXED_VERB_BY_KIND = {queue: 'queued', run: 'ran'} as const;\n\nexport function WorkflowRunDurationLabel({\n duration,\n className,\n}: {\n duration: WorkflowRunDurationDisplay | null;\n className?: string | undefined;\n}) {\n if (duration === null) return null;\n const kind = durationKind(duration);\n\n switch (duration.state) {\n case 'fixed': {\n const display = formatFixedDurationLabel(duration.elapsed);\n return (\n <DurationText\n className={className}\n kind={kind}\n ariaLabel={`${FIXED_VERB_BY_KIND[kind]} ${display}`}\n >\n {display}\n </DurationText>\n );\n }\n case 'live':\n return <LiveDurationText duration={duration} kind={kind} className={className} />;\n default: {\n const exhaustive: never = duration;\n return exhaustive;\n }\n }\n}\n\nexport function useWorkflowRunDurationAccessibleLabel(\n duration: WorkflowRunDurationDisplay | null,\n): string | undefined {\n useTimeTick();\n return workflowRunDurationAccessibleLabel(duration);\n}\n\nexport function workflowRunDurationAccessibleLabel(\n duration: WorkflowRunDurationDisplay | null,\n): string | undefined {\n if (duration === null) return undefined;\n const kind = durationKind(duration);\n\n switch (duration.state) {\n case 'live':\n return `${LIVE_VERB_BY_KIND[kind]} ${humanDuration(duration.fromIso)}`;\n case 'fixed':\n return `${FIXED_VERB_BY_KIND[kind]} ${formatFixedDurationLabel(duration.elapsed)}`;\n default: {\n const exhaustive: never = duration;\n return exhaustive;\n }\n }\n}\n\nfunction durationKind(duration: WorkflowRunDurationDisplay): DurationKind {\n return 'kind' in duration ? duration.kind : 'run';\n}\n\nfunction LiveDurationText({\n duration,\n kind,\n className,\n}: {\n duration: Extract<WorkflowRunDurationDisplay, {state: 'live'}>;\n kind: DurationKind;\n className?: string | undefined;\n}) {\n useTimeTick();\n const display = humanDuration(duration.fromIso);\n return (\n <DurationText\n className={className}\n kind={kind}\n ariaLabel={`${LIVE_VERB_BY_KIND[kind]} ${display}`}\n >\n {display}\n </DurationText>\n );\n}\n\nfunction DurationText({\n children,\n className,\n kind,\n ariaLabel,\n}: {\n children: string;\n className?: string | undefined;\n kind: DurationKind;\n ariaLabel?: string | undefined;\n}) {\n return (\n <Code\n as=\"span\"\n variant=\"label\"\n aria-label={ariaLabel}\n className={cn(\n 'inline-flex shrink-0 items-center gap-tight tabular-nums text-foreground-neutral-subtle',\n className,\n )}\n >\n {/* The glyph is the only visible mark of queue vs run: the status badge already writes\n \"Queued\", and the numeric columns are fixed width. The verb rides the accessible\n name instead. */}\n <Icon name={GLYPH_BY_KIND[kind]} className=\"size-12 shrink-0\" aria-hidden=\"true\" />\n {children}\n </Code>\n );\n}\n\nfunction formatFixedDurationLabel({\n years = 0,\n months = 0,\n weeks = 0,\n days = 0,\n hours = 0,\n minutes = 0,\n seconds = 0,\n}: Extract<WorkflowRunDurationDisplay, {state: 'fixed'}>['elapsed']): string {\n const totalDays = years * 365 + months * 30 + weeks * 7 + days;\n const totalHours = totalDays * 24 + hours;\n\n if (totalHours > 0) return `${totalHours}h ${pad2(minutes)}m`;\n if (minutes > 0) return `${minutes}m ${pad2(seconds)}s`;\n return `${seconds}s`;\n}\n\nfunction pad2(value: number): string {\n return value.toString().padStart(2, '0');\n}\n"],"names":["Icon","useTimeTick","Code","cn","humanDuration","GLYPH_BY_KIND","queue","run","LIVE_VERB_BY_KIND","FIXED_VERB_BY_KIND","WorkflowRunDurationLabel","duration","className","kind","durationKind","state","display","formatFixedDurationLabel","elapsed","DurationText","ariaLabel","LiveDurationText","exhaustive","useWorkflowRunDurationAccessibleLabel","workflowRunDurationAccessibleLabel","undefined","fromIso","children","as","variant","aria-label","name","aria-hidden","years","months","weeks","days","hours","minutes","seconds","totalDays","totalHours","pad2","value","toString","padStart"],"mappings":";AAAA,SAAQA,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,EAAE,EAAEC,aAAa,QAAO,0BAA0B;AAgB1D,MAAMC,gBAAgB;IAACC,OAAO;IAAiBC,KAAK;AAAW;AAC/D,MAAMC,oBAAoB;IAACF,OAAO;IAAUC,KAAK;AAAS;AAC1D,MAAME,qBAAqB;IAACH,OAAO;IAAUC,KAAK;AAAK;AAEvD,OAAO,SAASG,yBAAyB,EACvCC,QAAQ,EACRC,SAAS,EAIV;IACC,IAAID,aAAa,MAAM,OAAO;IAC9B,MAAME,OAAOC,aAAaH;IAE1B,OAAQA,SAASI,KAAK;QACpB,KAAK;YAAS;gBACZ,MAAMC,UAAUC,yBAAyBN,SAASO,OAAO;gBACzD,qBACE,KAACC;oBACCP,WAAWA;oBACXC,MAAMA;oBACNO,WAAW,GAAGX,kBAAkB,CAACI,KAAK,CAAC,CAAC,EAAEG,SAAS;8BAElDA;;YAGP;QACA,KAAK;YACH,qBAAO,KAACK;gBAAiBV,UAAUA;gBAAUE,MAAMA;gBAAMD,WAAWA;;QACtE;YAAS;gBACP,MAAMU,aAAoBX;gBAC1B,OAAOW;YACT;IACF;AACF;AAEA,OAAO,SAASC,sCACdZ,QAA2C;IAE3CV;IACA,OAAOuB,mCAAmCb;AAC5C;AAEA,OAAO,SAASa,mCACdb,QAA2C;IAE3C,IAAIA,aAAa,MAAM,OAAOc;IAC9B,MAAMZ,OAAOC,aAAaH;IAE1B,OAAQA,SAASI,KAAK;QACpB,KAAK;YACH,OAAO,GAAGP,iBAAiB,CAACK,KAAK,CAAC,CAAC,EAAET,cAAcO,SAASe,OAAO,GAAG;QACxE,KAAK;YACH,OAAO,GAAGjB,kBAAkB,CAACI,KAAK,CAAC,CAAC,EAAEI,yBAAyBN,SAASO,OAAO,GAAG;QACpF;YAAS;gBACP,MAAMI,aAAoBX;gBAC1B,OAAOW;YACT;IACF;AACF;AAEA,SAASR,aAAaH,QAAoC;IACxD,OAAO,UAAUA,WAAWA,SAASE,IAAI,GAAG;AAC9C;AAEA,SAASQ,iBAAiB,EACxBV,QAAQ,EACRE,IAAI,EACJD,SAAS,EAKV;IACCX;IACA,MAAMe,UAAUZ,cAAcO,SAASe,OAAO;IAC9C,qBACE,KAACP;QACCP,WAAWA;QACXC,MAAMA;QACNO,WAAW,GAAGZ,iBAAiB,CAACK,KAAK,CAAC,CAAC,EAAEG,SAAS;kBAEjDA;;AAGP;AAEA,SAASG,aAAa,EACpBQ,QAAQ,EACRf,SAAS,EACTC,IAAI,EACJO,SAAS,EAMV;IACC,qBACE,MAAClB;QACC0B,IAAG;QACHC,SAAQ;QACRC,cAAYV;QACZR,WAAWT,GACT,2FACAS;;0BAMF,KAACZ;gBAAK+B,MAAM1B,aAAa,CAACQ,KAAK;gBAAED,WAAU;gBAAmBoB,eAAY;;YACzEL;;;AAGP;AAEA,SAASV,yBAAyB,EAChCgB,QAAQ,CAAC,EACTC,SAAS,CAAC,EACVC,QAAQ,CAAC,EACTC,OAAO,CAAC,EACRC,QAAQ,CAAC,EACTC,UAAU,CAAC,EACXC,UAAU,CAAC,EACsD;IACjE,MAAMC,YAAYP,QAAQ,MAAMC,SAAS,KAAKC,QAAQ,IAAIC;IAC1D,MAAMK,aAAaD,YAAY,KAAKH;IAEpC,IAAII,aAAa,GAAG,OAAO,GAAGA,WAAW,EAAE,EAAEC,KAAKJ,SAAS,CAAC,CAAC;IAC7D,IAAIA,UAAU,GAAG,OAAO,GAAGA,QAAQ,EAAE,EAAEI,KAAKH,SAAS,CAAC,CAAC;IACvD,OAAO,GAAGA,QAAQ,CAAC,CAAC;AACtB;AAEA,SAASG,KAAKC,KAAa;IACzB,OAAOA,MAAMC,QAAQ,GAAGC,QAAQ,CAAC,GAAG;AACtC"}
|
|
@@ -63,10 +63,10 @@ const NOTABLE_STATUSES = [
|
|
|
63
63
|
children: /*#__PURE__*/ _jsx("span", {
|
|
64
64
|
role: "img",
|
|
65
65
|
"aria-label": summary,
|
|
66
|
-
className: cn('inline-flex items-center gap-
|
|
66
|
+
className: cn('inline-flex items-center gap-[3px]', className),
|
|
67
67
|
children: /*#__PURE__*/ _jsxs("span", {
|
|
68
68
|
"aria-hidden": "true",
|
|
69
|
-
className: "inline-flex items-center gap-
|
|
69
|
+
className: "inline-flex items-center gap-[3px]",
|
|
70
70
|
children: [
|
|
71
71
|
visible.map((job)=>/*#__PURE__*/ _jsx(WorkflowStatusIcon, {
|
|
72
72
|
status: job.status,
|
|
@@ -77,7 +77,7 @@ const NOTABLE_STATUSES = [
|
|
|
77
77
|
tooltip: false
|
|
78
78
|
}, job.id)),
|
|
79
79
|
hiddenCount > 0 && overflowStatus ? /*#__PURE__*/ _jsxs("span", {
|
|
80
|
-
className: "inline-flex items-center gap-
|
|
80
|
+
className: "inline-flex items-center gap-[2px]",
|
|
81
81
|
children: [
|
|
82
82
|
/*#__PURE__*/ _jsx(WorkflowStatusIcon, {
|
|
83
83
|
status: overflowStatus,
|
|
@@ -118,7 +118,7 @@ function JobStatusStripTooltip({ jobs, summary }) {
|
|
|
118
118
|
const notableTotal = NOTABLE_STATUSES.reduce((total, status)=>total + countOf(jobs, status), 0);
|
|
119
119
|
const remaining = notableTotal - named.length;
|
|
120
120
|
return /*#__PURE__*/ _jsxs("span", {
|
|
121
|
-
className: "
|
|
121
|
+
className: "flex max-w-[280px] flex-col gap-tight",
|
|
122
122
|
children: [
|
|
123
123
|
/*#__PURE__*/ _jsx(Text, {
|
|
124
124
|
as: "span",
|
|
@@ -129,7 +129,7 @@ function JobStatusStripTooltip({ jobs, summary }) {
|
|
|
129
129
|
named.map((job)=>/*#__PURE__*/ _jsxs(Code, {
|
|
130
130
|
as: "span",
|
|
131
131
|
variant: "label",
|
|
132
|
-
className: "
|
|
132
|
+
className: "block truncate",
|
|
133
133
|
children: [
|
|
134
134
|
getWorkflowStatusVisual(job.status).label.toLowerCase(),
|
|
135
135
|
" · ",
|
|
@@ -139,7 +139,7 @@ function JobStatusStripTooltip({ jobs, summary }) {
|
|
|
139
139
|
remaining > 0 ? /*#__PURE__*/ _jsxs(Text, {
|
|
140
140
|
as: "span",
|
|
141
141
|
size: "xs",
|
|
142
|
-
className: "
|
|
142
|
+
className: "block text-foreground-neutral-muted",
|
|
143
143
|
children: [
|
|
144
144
|
"and ",
|
|
145
145
|
remaining,
|