@shipfox/client-workflows 13.0.0 → 14.0.1
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/.storybook/main.ts +16 -1
- package/.storybook/workspace-source.ts +31 -0
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +37 -0
- package/dist/components/job-graph/job-duration-label.js +1 -1
- package/dist/components/job-graph/job-duration-label.js.map +1 -1
- package/dist/components/job-graph/job-graph-content.d.ts +2 -1
- package/dist/components/job-graph/job-graph-content.d.ts.map +1 -1
- package/dist/components/job-graph/job-graph-content.js +3 -3
- package/dist/components/job-graph/job-graph-content.js.map +1 -1
- package/dist/components/job-graph/job-graph-view.d.ts +2 -1
- package/dist/components/job-graph/job-graph-view.d.ts.map +1 -1
- package/dist/components/job-graph/job-graph-view.js +2 -2
- package/dist/components/job-graph/job-graph-view.js.map +1 -1
- package/dist/components/job-graph/job-node.js +5 -5
- package/dist/components/job-graph/job-node.js.map +1 -1
- package/dist/components/job-graph/types.d.ts +2 -1
- package/dist/components/job-graph/types.d.ts.map +1 -1
- package/dist/components/job-graph/types.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-number-label.js +1 -1
- package/dist/components/workflow-run-number-label.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +2 -2
- 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 +2 -7
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js +92 -68
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-run-tabs/index.d.ts +6 -0
- package/dist/components/workflow-run-tabs/index.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/index.js +7 -0
- package/dist/components/workflow-run-tabs/index.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts +11 -0
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.js +71 -0
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts +2 -0
- package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotations-empty.js +11 -0
- package/dist/components/workflow-run-tabs/run-annotations-empty.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-jobs-list.d.ts +18 -0
- package/dist/components/workflow-run-tabs/run-jobs-list.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-jobs-list.js +107 -0
- package/dist/components/workflow-run-tabs/run-jobs-list.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-tab-count.d.ts +6 -0
- package/dist/components/workflow-run-tabs/run-tab-count.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-tab-count.js +27 -0
- package/dist/components/workflow-run-tabs/run-tab-count.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-tab-strip.d.ts +8 -0
- package/dist/components/workflow-run-tabs/run-tab-strip.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-tab-strip.js +82 -0
- package/dist/components/workflow-run-tabs/run-tab-strip.js.map +1 -0
- package/dist/components/workflow-run-view/workflow-run-selection.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-selection.js +40 -1
- package/dist/components/workflow-run-view/workflow-run-selection.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.d.ts +1 -0
- package/dist/components/workflow-run-view/workflow-run-states.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.js +10 -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 +8 -4
- package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.js +125 -78
- package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
- package/dist/components/workflow-source-panel/index.d.ts +1 -0
- package/dist/components/workflow-source-panel/index.d.ts.map +1 -1
- package/dist/components/workflow-source-panel/index.js +1 -0
- package/dist/components/workflow-source-panel/index.js.map +1 -1
- package/dist/components/workflow-source-panel/workflow-source-content.d.ts +12 -0
- package/dist/components/workflow-source-panel/workflow-source-content.d.ts.map +1 -0
- package/dist/components/workflow-source-panel/workflow-source-content.js +53 -0
- package/dist/components/workflow-source-panel/workflow-source-content.js.map +1 -0
- package/dist/components/workflow-source-panel/workflow-source-panel.d.ts +1 -1
- package/dist/components/workflow-source-panel/workflow-source-panel.d.ts.map +1 -1
- package/dist/components/workflow-source-panel/workflow-source-panel.js +7 -54
- package/dist/components/workflow-source-panel/workflow-source-panel.js.map +1 -1
- package/dist/core/entities/step.d.ts +1 -1
- package/dist/core/entities/step.d.ts.map +1 -1
- package/dist/core/entities/step.js +2 -0
- package/dist/core/entities/step.js.map +1 -1
- package/dist/core/workflow-run-tabs.d.ts +9 -0
- package/dist/core/workflow-run-tabs.d.ts.map +1 -0
- package/dist/core/workflow-run-tabs.js +3 -0
- package/dist/core/workflow-run-tabs.js.map +1 -0
- package/dist/pages/workflow-run-detail-page.d.ts.map +1 -1
- package/dist/pages/workflow-run-detail-page.js +28 -6
- package/dist/pages/workflow-run-detail-page.js.map +1 -1
- package/dist/routes/inputs.d.ts +33 -0
- package/dist/routes/inputs.d.ts.map +1 -1
- package/dist/routes/inputs.js +49 -0
- package/dist/routes/inputs.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/components/job-graph/job-duration-label.tsx +5 -1
- package/src/components/job-graph/job-graph-content.tsx +7 -4
- package/src/components/job-graph/job-graph-view.tsx +6 -3
- 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/job-graph/job-node.test.tsx +27 -2
- package/src/components/job-graph/job-node.tsx +6 -6
- package/src/components/job-graph/types.ts +5 -1
- package/src/components/workflow-run-duration-label.tsx +1 -1
- package/src/components/workflow-run-number-label.tsx +1 -1
- package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +2 -2
- package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +23 -30
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +3 -22
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +84 -69
- package/src/components/workflow-run-tabs/index.ts +5 -0
- package/src/components/workflow-run-tabs/run-annotation-summary-line.test.tsx +69 -0
- package/src/components/workflow-run-tabs/run-annotation-summary-line.tsx +97 -0
- package/src/components/workflow-run-tabs/run-annotations-empty.tsx +11 -0
- package/src/components/workflow-run-tabs/run-jobs-list.tsx +151 -0
- package/src/components/workflow-run-tabs/run-tab-count.tsx +28 -0
- package/src/components/workflow-run-tabs/run-tab-strip.stories.tsx +52 -0
- package/src/components/workflow-run-tabs/run-tab-strip.test.tsx +117 -0
- package/src/components/workflow-run-tabs/run-tab-strip.tsx +97 -0
- package/src/components/workflow-run-view/workflow-run-selection.test.ts +48 -0
- package/src/components/workflow-run-view/workflow-run-selection.ts +48 -1
- package/src/components/workflow-run-view/workflow-run-states.tsx +12 -1
- package/src/components/workflow-run-view/workflow-run-view.test.tsx +64 -64
- package/src/components/workflow-run-view/workflow-run-view.tsx +199 -85
- package/src/components/workflow-source-panel/index.ts +1 -0
- package/src/components/workflow-source-panel/workflow-source-content.tsx +84 -0
- package/src/components/workflow-source-panel/workflow-source-panel.tsx +14 -81
- package/src/core/entities/step.ts +4 -0
- package/src/core/workflow-run-tabs.ts +8 -0
- package/src/pages/workflow-run-detail-page.tsx +31 -4
- package/src/pages/workflow-run-pages.test.tsx +185 -45
- package/src/routes/inputs.test.ts +47 -0
- package/src/routes/inputs.ts +45 -0
- package/src/storybook-config.test.ts +27 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.test.json +7 -1
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { ApiError } from '@shipfox/client-api';
|
|
3
3
|
import { QueryLoadError } from '@shipfox/client-ui';
|
|
4
|
+
import { EmptyState } from '@shipfox/react-ui/empty-state';
|
|
4
5
|
import { RelativeTimeProvider } from '@shipfox/react-ui/relative-time';
|
|
6
|
+
import { Tabs, TabsContent, TabsContents } from '@shipfox/react-ui/tabs';
|
|
5
7
|
import { toast } from '@shipfox/react-ui/toast';
|
|
6
8
|
import { useNavigate } from '@tanstack/react-router';
|
|
7
9
|
import { useEffect, useId, useRef, useState } from 'react';
|
|
8
10
|
import { resolveJobExecution } from '#core/workflow-run.js';
|
|
9
11
|
import { withoutWorkflowRunSelectionSearch } from '#core/workflow-run-url-state.js';
|
|
10
12
|
import { useCancelWorkflowRunMutation, useRerunWorkflowRunMutation, useWorkflowRunAttemptQuery } from '#hooks/api/workflow-runs.js';
|
|
13
|
+
import { WORKFLOW_RUN_TABS } from '#routes/inputs.js';
|
|
11
14
|
import { JobGraph } from '../job-graph/index.js';
|
|
12
15
|
import { WorkflowRunSummary } from '../workflow-run-summary/index.js';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
16
|
+
import { RunAnnotationSummaryLine, RunAnnotationsEmpty, RunJobsList, RunTabStrip } from '../workflow-run-tabs/index.js';
|
|
17
|
+
import { WorkflowSourceContent, WorkflowSourcePanel } from '../workflow-source-panel/index.js';
|
|
15
18
|
import { resolveWorkflowRunSelection } from './workflow-run-selection.js';
|
|
16
|
-
import { WorkflowRunNotFound, WorkflowRunSkeleton, WorkflowRunStaleError } from './workflow-run-states.js';
|
|
19
|
+
import { WorkflowRunContentSkeleton, WorkflowRunNotFound, WorkflowRunSkeleton, WorkflowRunStaleError } from './workflow-run-states.js';
|
|
17
20
|
/**
|
|
18
|
-
* Renders the run for `workflowRunId`,
|
|
19
|
-
*
|
|
20
|
-
|
|
21
|
-
*/ export function WorkflowRunView({ projectId, workspaceSlug, projectSlug, workflowRunId, selection, onSelectionChange }) {
|
|
21
|
+
* Renders the run for `workflowRunId`, keeping the local tab strip mounted while the run
|
|
22
|
+
* request is pending so polling never inserts navigation under the user's cursor.
|
|
23
|
+
*/ export function WorkflowRunView({ projectId, workspaceSlug, projectSlug, workflowRunId, selection, onSelectionChange, tab, onTabChange, annotationSummary }) {
|
|
22
24
|
const runQuery = useWorkflowRunAttemptQuery({
|
|
23
25
|
workflowRunId,
|
|
24
26
|
runAttempt: selection?.runAttempt
|
|
@@ -33,23 +35,27 @@ import { WorkflowRunNotFound, WorkflowRunSkeleton, WorkflowRunStaleError } from
|
|
|
33
35
|
query: runQuery,
|
|
34
36
|
rerunMutation: rerunMutation,
|
|
35
37
|
selection: selection,
|
|
36
|
-
onSelectionChange: onSelectionChange
|
|
38
|
+
onSelectionChange: onSelectionChange,
|
|
39
|
+
tab: tab,
|
|
40
|
+
onTabChange: onTabChange,
|
|
41
|
+
annotationSummary: annotationSummary
|
|
37
42
|
})
|
|
38
43
|
})
|
|
39
44
|
});
|
|
40
45
|
}
|
|
41
|
-
function RunViewContent({ workspaceSlug, projectSlug, query, rerunMutation, selection, onSelectionChange }) {
|
|
46
|
+
function RunViewContent({ workspaceSlug, projectSlug, query, rerunMutation, selection, onSelectionChange, tab, onTabChange, annotationSummary }) {
|
|
42
47
|
const navigate = useNavigate();
|
|
43
48
|
const [selectedJobId, setSelectedJobId] = useState();
|
|
44
49
|
const [selectedJobExecutionId, setSelectedJobExecutionId] = useState();
|
|
50
|
+
const [localTab, setLocalTab] = useState(tab ?? 'summary');
|
|
45
51
|
const [sourcePanelOpen, setSourcePanelOpen] = useState(false);
|
|
46
52
|
const [sourceFocus, setSourceFocus] = useState(null);
|
|
47
53
|
const sourcePanelId = useId();
|
|
48
|
-
|
|
49
|
-
// The button that last opened the panel (summary or a step detail), so Escape /
|
|
50
|
-
// Close returns focus to whoever opened it.
|
|
54
|
+
// The step Source button that last opened the panel, so Escape / Close returns focus to it.
|
|
51
55
|
const lastSourceTriggerRef = useRef(null);
|
|
52
56
|
const selectionControlled = selection !== undefined;
|
|
57
|
+
const tabControlled = onTabChange !== undefined;
|
|
58
|
+
const activeTab = tabControlled ? tab ?? 'summary' : localTab;
|
|
53
59
|
const sourceAvailable = query.data?.sourceSnapshot !== null && query.data?.sourceSnapshot !== undefined;
|
|
54
60
|
const cancelMutation = useCancelWorkflowRunMutation(query.data);
|
|
55
61
|
useEffect(()=>{
|
|
@@ -60,48 +66,49 @@ function RunViewContent({ workspaceSlug, projectSlug, query, rerunMutation, sele
|
|
|
60
66
|
}, [
|
|
61
67
|
sourceAvailable
|
|
62
68
|
]);
|
|
63
|
-
// If a refetch drops the focused step or its location,
|
|
64
|
-
//
|
|
69
|
+
// If a refetch drops the focused step or its location, close the panel so it never points at
|
|
70
|
+
// an unmounted Source button.
|
|
65
71
|
useEffect(()=>{
|
|
66
72
|
if (!sourceFocus) return;
|
|
67
73
|
const stillLocated = query.data?.jobs.some((job)=>job.jobExecutions.some((execution)=>execution.steps.some((step)=>step.id === sourceFocus.stepId && step.sourceLocation)));
|
|
68
74
|
if (!stillLocated) {
|
|
69
75
|
setSourceFocus(null);
|
|
70
|
-
|
|
76
|
+
setSourcePanelOpen(false);
|
|
71
77
|
}
|
|
72
78
|
}, [
|
|
73
79
|
sourceFocus,
|
|
74
80
|
query.data
|
|
75
81
|
]);
|
|
76
|
-
if (query.isPending) return /*#__PURE__*/ _jsx(WorkflowRunSkeleton, {});
|
|
77
|
-
// Only show the full error placeholder when nothing ever loaded. A refetch that fails after
|
|
78
|
-
// a prior success keeps the loaded run on screen (see below) instead of wiping the pane,
|
|
79
|
-
// since active-run polling can hit a transient API error.
|
|
80
|
-
if (query.isError && query.data === undefined) {
|
|
81
|
-
if (query.error instanceof ApiError && query.error.status === 404) {
|
|
82
|
-
return /*#__PURE__*/ _jsx(WorkflowRunNotFound, {});
|
|
83
|
-
}
|
|
84
|
-
return /*#__PURE__*/ _jsx(QueryLoadError, {
|
|
85
|
-
query: query,
|
|
86
|
-
subject: "workflow run",
|
|
87
|
-
icon: "pulseLine"
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
if (query.data === undefined) return /*#__PURE__*/ _jsx(WorkflowRunSkeleton, {});
|
|
91
82
|
const runData = query.data;
|
|
92
|
-
const resolvedSelection = selectionControlled ? resolveWorkflowRunSelection({
|
|
83
|
+
const resolvedSelection = selectionControlled && runData ? resolveWorkflowRunSelection({
|
|
93
84
|
run: runData,
|
|
94
|
-
selection
|
|
85
|
+
selection: selection
|
|
95
86
|
}) : undefined;
|
|
96
|
-
const
|
|
87
|
+
const hasExplicitJobSelection = Boolean(selection?.jobId || selection?.jobExecutionId || selection?.stepId || selection?.stepAttemptId);
|
|
88
|
+
const selectedJob = selectionControlled ? hasExplicitJobSelection ? resolvedSelection?.job : undefined : runData?.jobs.find((job)=>job.id === selectedJobId) ?? runData?.jobs.at(0);
|
|
97
89
|
const selectedJobExecution = selectionControlled ? resolvedSelection?.jobExecution : selectedJob ? resolveJobExecution(selectedJob, selectedJobExecutionId) : undefined;
|
|
98
90
|
const selectedAttemptId = selectionControlled ? resolvedSelection?.selectedAttemptId ?? null : undefined;
|
|
99
|
-
// Explicit per-step focus wins; fall back to the URL-selected step so deep links
|
|
100
|
-
//
|
|
91
|
+
// Explicit per-step focus wins; fall back to the URL-selected step so deep links still
|
|
92
|
+
// pre-highlight source in the Source tab.
|
|
101
93
|
const highlightedLineRange = sourceFocus?.location ?? resolvedSelection?.step?.sourceLocation ?? null;
|
|
102
|
-
const sourceSnapshot = runData
|
|
94
|
+
const sourceSnapshot = runData?.sourceSnapshot ?? null;
|
|
95
|
+
function changeTab(nextTab) {
|
|
96
|
+
if (!tabControlled) {
|
|
97
|
+
setLocalTab(nextTab);
|
|
98
|
+
} else if (activeTab !== nextTab) {
|
|
99
|
+
onTabChange?.(nextTab);
|
|
100
|
+
}
|
|
101
|
+
if (nextTab !== 'jobs') {
|
|
102
|
+
setSourcePanelOpen(false);
|
|
103
|
+
setSourceFocus(null);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function handleTabChange(nextTab) {
|
|
107
|
+
if (!WORKFLOW_RUN_TABS.some((value)=>value === nextTab)) return;
|
|
108
|
+
changeTab(nextTab);
|
|
109
|
+
}
|
|
103
110
|
async function rerun(mode) {
|
|
104
|
-
if (!workspaceSlug || !projectSlug) {
|
|
111
|
+
if (!runData || !workspaceSlug || !projectSlug) {
|
|
105
112
|
toast.error('Could not start re-run from this route.');
|
|
106
113
|
return;
|
|
107
114
|
}
|
|
@@ -124,12 +131,16 @@ function RunViewContent({ workspaceSlug, projectSlug, query, rerunMutation, sele
|
|
|
124
131
|
toast.error(error instanceof ApiError ? error.message : 'Could not start re-run');
|
|
125
132
|
}
|
|
126
133
|
}
|
|
127
|
-
function selectJob(jobId) {
|
|
134
|
+
function selectJob(jobId, source = 'pointer') {
|
|
135
|
+
if (source === 'pointer') changeTab('jobs');
|
|
128
136
|
if (!selectionControlled) {
|
|
129
137
|
setSelectedJobId(jobId);
|
|
130
138
|
setSelectedJobExecutionId(undefined);
|
|
131
139
|
return;
|
|
132
140
|
}
|
|
141
|
+
// Keyboard roving focus is an interaction within the Summary graph. Keep that panel mounted
|
|
142
|
+
// and let JobGraphView own the visual selection so focus is not moved to a different tab.
|
|
143
|
+
if (source === 'keyboard') return;
|
|
133
144
|
onSelectionChange?.(jobId ? {
|
|
134
145
|
jobId,
|
|
135
146
|
runAttempt: selection?.runAttempt
|
|
@@ -169,18 +180,6 @@ function RunViewContent({ workspaceSlug, projectSlug, query, rerunMutation, sele
|
|
|
169
180
|
runAttempt: selection?.runAttempt
|
|
170
181
|
});
|
|
171
182
|
}
|
|
172
|
-
function openWholeWorkflowSource() {
|
|
173
|
-
setSourceFocus(null);
|
|
174
|
-
lastSourceTriggerRef.current = sourceButtonRef.current;
|
|
175
|
-
setSourcePanelOpen(true);
|
|
176
|
-
}
|
|
177
|
-
function toggleWholeWorkflowSource() {
|
|
178
|
-
if (sourcePanelOpen && sourceFocus === null) {
|
|
179
|
-
closeSourcePanel();
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
openWholeWorkflowSource();
|
|
183
|
-
}
|
|
184
183
|
function openStepSource(stepId, location, trigger) {
|
|
185
184
|
setSourceFocus({
|
|
186
185
|
stepId,
|
|
@@ -191,13 +190,11 @@ function RunViewContent({ workspaceSlug, projectSlug, query, rerunMutation, sele
|
|
|
191
190
|
}
|
|
192
191
|
function closeSourcePanel() {
|
|
193
192
|
const trigger = lastSourceTriggerRef.current;
|
|
194
|
-
const fallbackTrigger = sourceButtonRef.current;
|
|
195
193
|
setSourcePanelOpen(false);
|
|
196
|
-
// Defer so focus lands after the panel unmounts; clear the focus only after
|
|
197
|
-
//
|
|
194
|
+
// Defer so focus lands after the panel unmounts; clear the focus only after focusing so the
|
|
195
|
+
// opener button is still expanded (force-visible) on return.
|
|
198
196
|
window.setTimeout(()=>{
|
|
199
|
-
|
|
200
|
-
focusTarget?.focus();
|
|
197
|
+
if (trigger?.isConnected) trigger.focus();
|
|
201
198
|
setSourceFocus(null);
|
|
202
199
|
}, 0);
|
|
203
200
|
}
|
|
@@ -208,53 +205,103 @@ function RunViewContent({ workspaceSlug, projectSlug, query, rerunMutation, sele
|
|
|
208
205
|
}
|
|
209
206
|
});
|
|
210
207
|
}
|
|
208
|
+
const fatalError = query.isError && runData === undefined;
|
|
209
|
+
const tabState = fatalError ? query.error instanceof ApiError && query.error.status === 404 ? /*#__PURE__*/ _jsx(WorkflowRunNotFound, {}) : /*#__PURE__*/ _jsx(QueryLoadError, {
|
|
210
|
+
query: query,
|
|
211
|
+
subject: "workflow run",
|
|
212
|
+
icon: "pulseLine"
|
|
213
|
+
}) : query.isPending || runData === undefined ? /*#__PURE__*/ _jsx(WorkflowRunContentSkeleton, {}) : null;
|
|
211
214
|
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
212
215
|
children: [
|
|
213
|
-
/*#__PURE__*/ _jsxs(
|
|
214
|
-
|
|
216
|
+
/*#__PURE__*/ _jsxs(Tabs, {
|
|
217
|
+
value: activeTab,
|
|
218
|
+
onValueChange: handleTabChange,
|
|
219
|
+
className: "flex min-w-0 flex-1 flex-col gap-0",
|
|
215
220
|
children: [
|
|
216
|
-
/*#__PURE__*/ _jsx(WorkflowRunSummary, {
|
|
221
|
+
runData ? /*#__PURE__*/ _jsx(WorkflowRunSummary, {
|
|
217
222
|
workspaceSlug: workspaceSlug,
|
|
218
223
|
projectSlug: projectSlug,
|
|
219
224
|
run: runData,
|
|
220
|
-
sourceAvailable: sourceAvailable,
|
|
221
|
-
sourceOpen: sourcePanelOpen && sourceFocus === null,
|
|
222
|
-
sourcePanelId: sourcePanelId,
|
|
223
|
-
sourceButtonRef: sourceButtonRef,
|
|
224
|
-
onSourceToggle: toggleWholeWorkflowSource,
|
|
225
225
|
cancelling: cancelMutation.isPending,
|
|
226
226
|
onCancel: cancelRun,
|
|
227
227
|
rerunPending: rerunMutation.isPending,
|
|
228
228
|
onRerun: (mode)=>void rerun(mode),
|
|
229
229
|
latestAttempt: runData.latestAttempt
|
|
230
|
-
}),
|
|
231
|
-
query.isError ? /*#__PURE__*/ _jsx(WorkflowRunStaleError, {
|
|
230
|
+
}) : /*#__PURE__*/ _jsx(WorkflowRunSkeleton, {}),
|
|
231
|
+
runData && query.isError ? /*#__PURE__*/ _jsx(WorkflowRunStaleError, {
|
|
232
232
|
query: query
|
|
233
233
|
}) : null,
|
|
234
|
-
/*#__PURE__*/ _jsx(
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
234
|
+
/*#__PURE__*/ _jsx(RunTabStrip, {
|
|
235
|
+
jobCount: runData?.jobs.length,
|
|
236
|
+
jobsFailed: runData?.jobs.filter((job)=>job.status === 'failed').length,
|
|
237
|
+
annotationSummary: annotationSummary
|
|
238
|
+
}),
|
|
239
|
+
/*#__PURE__*/ _jsxs(TabsContents, {
|
|
240
|
+
className: "min-h-0 flex-1 overflow-auto bg-background-neutral-background pb-24 pt-16",
|
|
241
|
+
children: [
|
|
242
|
+
/*#__PURE__*/ _jsx(TabsContent, {
|
|
243
|
+
value: "summary",
|
|
244
|
+
tabIndex: -1,
|
|
245
|
+
className: "mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24 outline-none",
|
|
246
|
+
children: runData ? /*#__PURE__*/ _jsx(JobGraph, {
|
|
240
247
|
run: runData,
|
|
241
248
|
selectedJobId: selectedJob?.id,
|
|
242
249
|
onSelectedJobChange: selectJob
|
|
243
|
-
})
|
|
244
|
-
|
|
250
|
+
}) : tabState
|
|
251
|
+
}),
|
|
252
|
+
/*#__PURE__*/ _jsx(TabsContent, {
|
|
253
|
+
value: "jobs",
|
|
254
|
+
tabIndex: -1,
|
|
255
|
+
className: "mx-auto w-full max-w-[1120px] px-24 outline-none",
|
|
256
|
+
children: runData ? /*#__PURE__*/ _jsx(RunJobsList, {
|
|
257
|
+
jobs: runData.jobs,
|
|
258
|
+
selectedJobId: selectedJob?.id,
|
|
259
|
+
onSelectedJobChange: selectJob,
|
|
245
260
|
workspaceSlug: workspaceSlug,
|
|
246
|
-
job: selectedJob,
|
|
247
261
|
selectedJobExecution: selectedJobExecution,
|
|
248
|
-
selectedAttemptId:
|
|
262
|
+
selectedAttemptId: selectedAttemptId,
|
|
249
263
|
onSelectedJobExecutionChange: selectJobExecution,
|
|
250
264
|
onSelectedAttemptChange: selectionControlled ? selectAttempt : undefined,
|
|
251
265
|
sourcePanelId: sourcePanelId,
|
|
252
266
|
sourceAvailable: sourceAvailable,
|
|
253
267
|
focusedSourceStepId: sourceFocus?.stepId ?? null,
|
|
254
268
|
onOpenStepSource: openStepSource
|
|
255
|
-
}) :
|
|
256
|
-
|
|
257
|
-
|
|
269
|
+
}) : tabState
|
|
270
|
+
}),
|
|
271
|
+
/*#__PURE__*/ _jsx(TabsContent, {
|
|
272
|
+
value: "annotations",
|
|
273
|
+
tabIndex: -1,
|
|
274
|
+
className: "mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24 outline-none",
|
|
275
|
+
children: runData ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
276
|
+
children: [
|
|
277
|
+
/*#__PURE__*/ _jsx(RunAnnotationSummaryLine, {
|
|
278
|
+
summary: annotationSummary,
|
|
279
|
+
workspaceSlug: workspaceSlug,
|
|
280
|
+
projectSlug: projectSlug,
|
|
281
|
+
workflowRunId: runData.id,
|
|
282
|
+
search: selection
|
|
283
|
+
}),
|
|
284
|
+
/*#__PURE__*/ _jsx(RunAnnotationsEmpty, {})
|
|
285
|
+
]
|
|
286
|
+
}) : tabState
|
|
287
|
+
}),
|
|
288
|
+
/*#__PURE__*/ _jsx(TabsContent, {
|
|
289
|
+
value: "source",
|
|
290
|
+
keepMounted: sourceSnapshot !== null,
|
|
291
|
+
tabIndex: -1,
|
|
292
|
+
className: "mx-auto flex min-h-full w-full max-w-[1120px] px-24 outline-none",
|
|
293
|
+
children: runData ? sourceSnapshot ? /*#__PURE__*/ _jsx(WorkflowSourceContent, {
|
|
294
|
+
source: sourceSnapshot,
|
|
295
|
+
highlightedLineRange: highlightedLineRange,
|
|
296
|
+
scrollHighlightedIntoView: true
|
|
297
|
+
}) : /*#__PURE__*/ _jsx(EmptyState, {
|
|
298
|
+
className: "min-h-160 w-full",
|
|
299
|
+
icon: "fileDamageLine",
|
|
300
|
+
title: "Source snapshot unavailable",
|
|
301
|
+
description: runData.isTemporary ? 'Temporary runs do not capture workflow source.' : 'This run was created before workflow source snapshots were captured.'
|
|
302
|
+
}) : tabState
|
|
303
|
+
})
|
|
304
|
+
]
|
|
258
305
|
})
|
|
259
306
|
]
|
|
260
307
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/workflow-run-view/workflow-run-view.tsx"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {RelativeTimeProvider} from '@shipfox/react-ui/relative-time';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {useNavigate} from '@tanstack/react-router';\nimport {useEffect, useId, useRef, useState} from 'react';\nimport {\n type JobExecution,\n resolveJobExecution,\n type StepSourceLocation,\n type WorkflowRunRerunMode,\n} from '#core/workflow-run.js';\nimport {\n type WorkflowRunSelectionInput,\n withoutWorkflowRunSelectionSearch,\n} from '#core/workflow-run-url-state.js';\nimport {\n useCancelWorkflowRunMutation,\n useRerunWorkflowRunMutation,\n useWorkflowRunAttemptQuery,\n} from '#hooks/api/workflow-runs.js';\nimport {JobGraph} from '../job-graph/index.js';\nimport {WorkflowRunSummary} from '../workflow-run-summary/index.js';\nimport {WorkflowSourcePanel} from '../workflow-source-panel/index.js';\nimport {JobCard} from './job-card.js';\nimport {resolveWorkflowRunSelection} from './workflow-run-selection.js';\nimport {\n WorkflowRunNotFound,\n WorkflowRunSkeleton,\n WorkflowRunStaleError,\n} from './workflow-run-states.js';\n\ninterface WorkflowSourceFocus {\n stepId: string;\n location: StepSourceLocation;\n}\n\nexport interface WorkflowRunViewProps {\n projectId: string;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n workflowRunId?: string | undefined;\n selection?: WorkflowRunSelectionInput | undefined;\n onSelectionChange?: ((selection: WorkflowRunSelectionInput) => void) | undefined;\n}\n\n/**\n * Renders the run for `workflowRunId`, or a skeleton while `workflowRunId` is still unknown (the page is\n * resolving which run to show) or the run is loading, so the page never branches on the\n * loading state itself.\n */\nexport function WorkflowRunView({\n projectId,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n selection,\n onSelectionChange,\n}: WorkflowRunViewProps) {\n const runQuery = useWorkflowRunAttemptQuery({workflowRunId, runAttempt: selection?.runAttempt});\n const rerunMutation = useRerunWorkflowRunMutation(projectId);\n\n return (\n <RelativeTimeProvider>\n <div className=\"flex min-h-0 min-w-0 flex-1 overflow-hidden\">\n <RunViewContent\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n query={runQuery}\n rerunMutation={rerunMutation}\n selection={selection}\n onSelectionChange={onSelectionChange}\n />\n </div>\n </RelativeTimeProvider>\n );\n}\n\nfunction RunViewContent({\n workspaceSlug,\n projectSlug,\n query,\n rerunMutation,\n selection,\n onSelectionChange,\n}: {\n workspaceSlug: string | undefined;\n projectSlug: string | undefined;\n query: ReturnType<typeof useWorkflowRunAttemptQuery>;\n rerunMutation: ReturnType<typeof useRerunWorkflowRunMutation>;\n selection: WorkflowRunSelectionInput | undefined;\n onSelectionChange: ((selection: WorkflowRunSelectionInput) => void) | undefined;\n}) {\n const navigate = useNavigate();\n const [selectedJobId, setSelectedJobId] = useState<string | undefined>();\n const [selectedJobExecutionId, setSelectedJobExecutionId] = useState<string | undefined>();\n const [sourcePanelOpen, setSourcePanelOpen] = useState(false);\n const [sourceFocus, setSourceFocus] = useState<WorkflowSourceFocus | null>(null);\n const sourcePanelId = useId();\n const sourceButtonRef = useRef<HTMLButtonElement>(null);\n // The button that last opened the panel (summary or a step detail), so Escape /\n // Close returns focus to whoever opened it.\n const lastSourceTriggerRef = useRef<HTMLButtonElement | null>(null);\n const selectionControlled = selection !== undefined;\n const sourceAvailable =\n query.data?.sourceSnapshot !== null && query.data?.sourceSnapshot !== undefined;\n const cancelMutation = useCancelWorkflowRunMutation(query.data);\n\n useEffect(() => {\n if (!sourceAvailable) {\n setSourcePanelOpen(false);\n setSourceFocus(null);\n }\n }, [sourceAvailable]);\n\n // If a refetch drops the focused step or its location, degrade to whole-workflow\n // focus so the panel never points at an unmounted Source button.\n useEffect(() => {\n if (!sourceFocus) return;\n const stillLocated = query.data?.jobs.some((job) =>\n job.jobExecutions.some((execution) =>\n execution.steps.some((step) => step.id === sourceFocus.stepId && step.sourceLocation),\n ),\n );\n if (!stillLocated) {\n setSourceFocus(null);\n lastSourceTriggerRef.current = sourceButtonRef.current;\n }\n }, [sourceFocus, query.data]);\n\n if (query.isPending) return <WorkflowRunSkeleton />;\n\n // Only show the full error placeholder when nothing ever loaded. A refetch that fails after\n // a prior success keeps the loaded run on screen (see below) instead of wiping the pane,\n // since active-run polling can hit a transient API error.\n if (query.isError && query.data === undefined) {\n if (query.error instanceof ApiError && query.error.status === 404) {\n return <WorkflowRunNotFound />;\n }\n return <QueryLoadError query={query} subject=\"workflow run\" icon=\"pulseLine\" />;\n }\n\n if (query.data === undefined) return <WorkflowRunSkeleton />;\n\n const runData = query.data;\n const resolvedSelection = selectionControlled\n ? resolveWorkflowRunSelection({run: runData, selection})\n : undefined;\n const selectedJob = selectionControlled\n ? resolvedSelection?.job\n : (runData.jobs.find((job) => job.id === selectedJobId) ?? runData.jobs.at(0));\n const selectedJobExecution = selectionControlled\n ? resolvedSelection?.jobExecution\n : selectedJob\n ? resolveJobExecution(selectedJob, selectedJobExecutionId)\n : undefined;\n const selectedAttemptId = selectionControlled\n ? (resolvedSelection?.selectedAttemptId ?? null)\n : undefined;\n // Explicit per-step focus wins; fall back to the URL-selected step so deep links\n // still pre-highlight when the summary opens the whole-workflow source.\n const highlightedLineRange =\n sourceFocus?.location ?? resolvedSelection?.step?.sourceLocation ?? null;\n const sourceSnapshot = runData.sourceSnapshot;\n async function rerun(mode: WorkflowRunRerunMode) {\n if (!workspaceSlug || !projectSlug) {\n toast.error('Could not start re-run from this route.');\n return;\n }\n try {\n const run = await rerunMutation.mutateAsync({workflowRunId: runData.id, mode});\n toast.success('Re-run started');\n await navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',\n params: {\n workspaceSlug,\n projectSlug,\n workflowRunId: run.id,\n },\n search: ((previous: Record<string, unknown>) =>\n withoutWorkflowRunSelectionSearch(previous)) as never,\n });\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : 'Could not start re-run');\n }\n }\n\n function selectJob(jobId: string | undefined) {\n if (!selectionControlled) {\n setSelectedJobId(jobId);\n setSelectedJobExecutionId(undefined);\n return;\n }\n\n onSelectionChange?.(\n jobId ? {jobId, runAttempt: selection?.runAttempt} : {runAttempt: selection?.runAttempt},\n );\n }\n\n function selectJobExecution(jobExecutionId: string | undefined) {\n if (!selectionControlled) {\n setSelectedJobExecutionId(jobExecutionId);\n return;\n }\n if (!selectedJob) return;\n\n onSelectionChange?.({\n jobId: selectedJob.id,\n jobExecutionId,\n runAttempt: selection?.runAttempt,\n });\n }\n\n function selectAttempt(attemptId: string | undefined) {\n if (!selectionControlled || !selectedJob || !selectedJobExecution) return;\n\n if (!attemptId) {\n onSelectionChange?.({\n jobId: selectedJob.id,\n jobExecutionId: selectedJobExecution.id,\n runAttempt: selection?.runAttempt,\n });\n return;\n }\n\n const match = findAttemptSelection(selectedJobExecution, attemptId);\n if (!match) return;\n\n onSelectionChange?.({\n jobId: selectedJob.id,\n jobExecutionId: selectedJobExecution.id,\n stepId: match.stepId,\n stepAttemptId: match.attemptId,\n runAttempt: selection?.runAttempt,\n });\n }\n\n function openWholeWorkflowSource() {\n setSourceFocus(null);\n lastSourceTriggerRef.current = sourceButtonRef.current;\n setSourcePanelOpen(true);\n }\n\n function toggleWholeWorkflowSource() {\n if (sourcePanelOpen && sourceFocus === null) {\n closeSourcePanel();\n return;\n }\n openWholeWorkflowSource();\n }\n\n function openStepSource(\n stepId: string,\n location: StepSourceLocation,\n trigger: HTMLButtonElement | null,\n ) {\n setSourceFocus({stepId, location});\n lastSourceTriggerRef.current = trigger;\n setSourcePanelOpen(true);\n }\n\n function closeSourcePanel() {\n const trigger = lastSourceTriggerRef.current;\n const fallbackTrigger = sourceButtonRef.current;\n setSourcePanelOpen(false);\n // Defer so focus lands after the panel unmounts; clear the focus only after\n // focusing so the opener button is still expanded (force-visible) on return.\n window.setTimeout(() => {\n const focusTarget = trigger?.isConnected ? trigger : fallbackTrigger;\n focusTarget?.focus();\n setSourceFocus(null);\n }, 0);\n }\n\n function cancelRun() {\n cancelMutation.mutate(undefined, {\n onError: (error) => {\n toast.error(cancelErrorMessage(error));\n },\n });\n }\n\n return (\n <>\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <WorkflowRunSummary\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n run={runData}\n sourceAvailable={sourceAvailable}\n sourceOpen={sourcePanelOpen && sourceFocus === null}\n sourcePanelId={sourcePanelId}\n sourceButtonRef={sourceButtonRef}\n onSourceToggle={toggleWholeWorkflowSource}\n cancelling={cancelMutation.isPending}\n onCancel={cancelRun}\n rerunPending={rerunMutation.isPending}\n onRerun={(mode) => void rerun(mode)}\n latestAttempt={runData.latestAttempt}\n />\n {query.isError ? <WorkflowRunStaleError query={query} /> : null}\n <div className=\"min-h-0 flex-1 overflow-auto bg-background-neutral-base p-16\">\n <div className=\"mx-auto flex w-full max-w-[1120px] flex-col gap-16\">\n <JobGraph\n run={runData}\n selectedJobId={selectedJob?.id}\n onSelectedJobChange={selectJob}\n />\n {selectedJob ? (\n <JobCard\n workspaceSlug={workspaceSlug}\n job={selectedJob}\n selectedJobExecution={selectedJobExecution}\n selectedAttemptId={selectedJob.carriedOver ? undefined : selectedAttemptId}\n onSelectedJobExecutionChange={selectJobExecution}\n onSelectedAttemptChange={selectionControlled ? selectAttempt : undefined}\n sourcePanelId={sourcePanelId}\n sourceAvailable={sourceAvailable}\n focusedSourceStepId={sourceFocus?.stepId ?? null}\n onOpenStepSource={openStepSource}\n />\n ) : null}\n </div>\n </div>\n </div>\n <WorkflowSourcePanel\n id={sourcePanelId}\n source={sourceSnapshot}\n open={sourcePanelOpen && sourceAvailable}\n onClose={closeSourcePanel}\n highlightedLineRange={highlightedLineRange}\n scrollHighlightedIntoView\n />\n </>\n );\n}\n\nfunction cancelErrorMessage(error: unknown): string {\n if (error instanceof ApiError && error.code === 'run-already-finished') {\n return 'This workflow run has already finished.';\n }\n return 'Could not cancel workflow run.';\n}\n\nfunction findAttemptSelection(jobExecution: JobExecution, attemptId: string) {\n for (const step of jobExecution.steps) {\n const attempt = step.attempts.find((candidate) => candidate.id === attemptId);\n if (attempt) return {stepId: step.id, attemptId: attempt.id};\n }\n return undefined;\n}\n"],"names":["ApiError","QueryLoadError","RelativeTimeProvider","toast","useNavigate","useEffect","useId","useRef","useState","resolveJobExecution","withoutWorkflowRunSelectionSearch","useCancelWorkflowRunMutation","useRerunWorkflowRunMutation","useWorkflowRunAttemptQuery","JobGraph","WorkflowRunSummary","WorkflowSourcePanel","JobCard","resolveWorkflowRunSelection","WorkflowRunNotFound","WorkflowRunSkeleton","WorkflowRunStaleError","WorkflowRunView","projectId","workspaceSlug","projectSlug","workflowRunId","selection","onSelectionChange","runQuery","runAttempt","rerunMutation","div","className","RunViewContent","query","navigate","selectedJobId","setSelectedJobId","selectedJobExecutionId","setSelectedJobExecutionId","sourcePanelOpen","setSourcePanelOpen","sourceFocus","setSourceFocus","sourcePanelId","sourceButtonRef","lastSourceTriggerRef","selectionControlled","undefined","sourceAvailable","data","sourceSnapshot","cancelMutation","stillLocated","jobs","some","job","jobExecutions","execution","steps","step","id","stepId","sourceLocation","current","isPending","isError","error","status","subject","icon","runData","resolvedSelection","run","selectedJob","find","at","selectedJobExecution","jobExecution","selectedAttemptId","highlightedLineRange","location","rerun","mode","mutateAsync","success","to","params","search","previous","message","selectJob","jobId","selectJobExecution","jobExecutionId","selectAttempt","attemptId","match","findAttemptSelection","stepAttemptId","openWholeWorkflowSource","toggleWholeWorkflowSource","closeSourcePanel","openStepSource","trigger","fallbackTrigger","window","setTimeout","focusTarget","isConnected","focus","cancelRun","mutate","onError","cancelErrorMessage","sourceOpen","onSourceToggle","cancelling","onCancel","rerunPending","onRerun","latestAttempt","onSelectedJobChange","carriedOver","onSelectedJobExecutionChange","onSelectedAttemptChange","focusedSourceStepId","onOpenStepSource","source","open","onClose","scrollHighlightedIntoView","code","attempt","attempts","candidate"],"mappings":";AAAA,SAAQA,QAAQ,QAAO,sBAAsB;AAC7C,SAAQC,cAAc,QAAO,qBAAqB;AAClD,SAAQC,oBAAoB,QAAO,kCAAkC;AACrE,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,WAAW,QAAO,yBAAyB;AACnD,SAAQC,SAAS,EAAEC,KAAK,EAAEC,MAAM,EAAEC,QAAQ,QAAO,QAAQ;AACzD,SAEEC,mBAAmB,QAGd,wBAAwB;AAC/B,SAEEC,iCAAiC,QAC5B,kCAAkC;AACzC,SACEC,4BAA4B,EAC5BC,2BAA2B,EAC3BC,0BAA0B,QACrB,8BAA8B;AACrC,SAAQC,QAAQ,QAAO,wBAAwB;AAC/C,SAAQC,kBAAkB,QAAO,mCAAmC;AACpE,SAAQC,mBAAmB,QAAO,oCAAoC;AACtE,SAAQC,OAAO,QAAO,gBAAgB;AACtC,SAAQC,2BAA2B,QAAO,8BAA8B;AACxE,SACEC,mBAAmB,EACnBC,mBAAmB,EACnBC,qBAAqB,QAChB,2BAA2B;AAgBlC;;;;CAIC,GACD,OAAO,SAASC,gBAAgB,EAC9BC,SAAS,EACTC,aAAa,EACbC,WAAW,EACXC,aAAa,EACbC,SAAS,EACTC,iBAAiB,EACI;IACrB,MAAMC,WAAWhB,2BAA2B;QAACa;QAAeI,YAAYH,WAAWG;IAAU;IAC7F,MAAMC,gBAAgBnB,4BAA4BW;IAElD,qBACE,KAACrB;kBACC,cAAA,KAAC8B;YAAIC,WAAU;sBACb,cAAA,KAACC;gBACCV,eAAeA;gBACfC,aAAaA;gBACbU,OAAON;gBACPE,eAAeA;gBACfJ,WAAWA;gBACXC,mBAAmBA;;;;AAK7B;AAEA,SAASM,eAAe,EACtBV,aAAa,EACbC,WAAW,EACXU,KAAK,EACLJ,aAAa,EACbJ,SAAS,EACTC,iBAAiB,EAQlB;IACC,MAAMQ,WAAWhC;IACjB,MAAM,CAACiC,eAAeC,iBAAiB,GAAG9B;IAC1C,MAAM,CAAC+B,wBAAwBC,0BAA0B,GAAGhC;IAC5D,MAAM,CAACiC,iBAAiBC,mBAAmB,GAAGlC,SAAS;IACvD,MAAM,CAACmC,aAAaC,eAAe,GAAGpC,SAAqC;IAC3E,MAAMqC,gBAAgBvC;IACtB,MAAMwC,kBAAkBvC,OAA0B;IAClD,gFAAgF;IAChF,4CAA4C;IAC5C,MAAMwC,uBAAuBxC,OAAiC;IAC9D,MAAMyC,sBAAsBrB,cAAcsB;IAC1C,MAAMC,kBACJf,MAAMgB,IAAI,EAAEC,mBAAmB,QAAQjB,MAAMgB,IAAI,EAAEC,mBAAmBH;IACxE,MAAMI,iBAAiB1C,6BAA6BwB,MAAMgB,IAAI;IAE9D9C,UAAU;QACR,IAAI,CAAC6C,iBAAiB;YACpBR,mBAAmB;YACnBE,eAAe;QACjB;IACF,GAAG;QAACM;KAAgB;IAEpB,iFAAiF;IACjF,iEAAiE;IACjE7C,UAAU;QACR,IAAI,CAACsC,aAAa;QAClB,MAAMW,eAAenB,MAAMgB,IAAI,EAAEI,KAAKC,KAAK,CAACC,MAC1CA,IAAIC,aAAa,CAACF,IAAI,CAAC,CAACG,YACtBA,UAAUC,KAAK,CAACJ,IAAI,CAAC,CAACK,OAASA,KAAKC,EAAE,KAAKnB,YAAYoB,MAAM,IAAIF,KAAKG,cAAc;QAGxF,IAAI,CAACV,cAAc;YACjBV,eAAe;YACfG,qBAAqBkB,OAAO,GAAGnB,gBAAgBmB,OAAO;QACxD;IACF,GAAG;QAACtB;QAAaR,MAAMgB,IAAI;KAAC;IAE5B,IAAIhB,MAAM+B,SAAS,EAAE,qBAAO,KAAC9C;IAE7B,4FAA4F;IAC5F,yFAAyF;IACzF,0DAA0D;IAC1D,IAAIe,MAAMgC,OAAO,IAAIhC,MAAMgB,IAAI,KAAKF,WAAW;QAC7C,IAAId,MAAMiC,KAAK,YAAYpE,YAAYmC,MAAMiC,KAAK,CAACC,MAAM,KAAK,KAAK;YACjE,qBAAO,KAAClD;QACV;QACA,qBAAO,KAAClB;YAAekC,OAAOA;YAAOmC,SAAQ;YAAeC,MAAK;;IACnE;IAEA,IAAIpC,MAAMgB,IAAI,KAAKF,WAAW,qBAAO,KAAC7B;IAEtC,MAAMoD,UAAUrC,MAAMgB,IAAI;IAC1B,MAAMsB,oBAAoBzB,sBACtB9B,4BAA4B;QAACwD,KAAKF;QAAS7C;IAAS,KACpDsB;IACJ,MAAM0B,cAAc3B,sBAChByB,mBAAmBhB,MAClBe,QAAQjB,IAAI,CAACqB,IAAI,CAAC,CAACnB,MAAQA,IAAIK,EAAE,KAAKzB,kBAAkBmC,QAAQjB,IAAI,CAACsB,EAAE,CAAC;IAC7E,MAAMC,uBAAuB9B,sBACzByB,mBAAmBM,eACnBJ,cACElE,oBAAoBkE,aAAapC,0BACjCU;IACN,MAAM+B,oBAAoBhC,sBACrByB,mBAAmBO,qBAAqB,OACzC/B;IACJ,iFAAiF;IACjF,wEAAwE;IACxE,MAAMgC,uBACJtC,aAAauC,YAAYT,mBAAmBZ,MAAMG,kBAAkB;IACtE,MAAMZ,iBAAiBoB,QAAQpB,cAAc;IAC7C,eAAe+B,MAAMC,IAA0B;QAC7C,IAAI,CAAC5D,iBAAiB,CAACC,aAAa;YAClCtB,MAAMiE,KAAK,CAAC;YACZ;QACF;QACA,IAAI;YACF,MAAMM,MAAM,MAAM3C,cAAcsD,WAAW,CAAC;gBAAC3D,eAAe8C,QAAQV,EAAE;gBAAEsB;YAAI;YAC5EjF,MAAMmF,OAAO,CAAC;YACd,MAAMlD,SAAS;gBACbmD,IAAI;gBACJC,QAAQ;oBACNhE;oBACAC;oBACAC,eAAegD,IAAIZ,EAAE;gBACvB;gBACA2B,QAAS,CAACC,WACRhF,kCAAkCgF;YACtC;QACF,EAAE,OAAOtB,OAAO;YACdjE,MAAMiE,KAAK,CAACA,iBAAiBpE,WAAWoE,MAAMuB,OAAO,GAAG;QAC1D;IACF;IAEA,SAASC,UAAUC,KAAyB;QAC1C,IAAI,CAAC7C,qBAAqB;YACxBV,iBAAiBuD;YACjBrD,0BAA0BS;YAC1B;QACF;QAEArB,oBACEiE,QAAQ;YAACA;YAAO/D,YAAYH,WAAWG;QAAU,IAAI;YAACA,YAAYH,WAAWG;QAAU;IAE3F;IAEA,SAASgE,mBAAmBC,cAAkC;QAC5D,IAAI,CAAC/C,qBAAqB;YACxBR,0BAA0BuD;YAC1B;QACF;QACA,IAAI,CAACpB,aAAa;QAElB/C,oBAAoB;YAClBiE,OAAOlB,YAAYb,EAAE;YACrBiC;YACAjE,YAAYH,WAAWG;QACzB;IACF;IAEA,SAASkE,cAAcC,SAA6B;QAClD,IAAI,CAACjD,uBAAuB,CAAC2B,eAAe,CAACG,sBAAsB;QAEnE,IAAI,CAACmB,WAAW;YACdrE,oBAAoB;gBAClBiE,OAAOlB,YAAYb,EAAE;gBACrBiC,gBAAgBjB,qBAAqBhB,EAAE;gBACvChC,YAAYH,WAAWG;YACzB;YACA;QACF;QAEA,MAAMoE,QAAQC,qBAAqBrB,sBAAsBmB;QACzD,IAAI,CAACC,OAAO;QAEZtE,oBAAoB;YAClBiE,OAAOlB,YAAYb,EAAE;YACrBiC,gBAAgBjB,qBAAqBhB,EAAE;YACvCC,QAAQmC,MAAMnC,MAAM;YACpBqC,eAAeF,MAAMD,SAAS;YAC9BnE,YAAYH,WAAWG;QACzB;IACF;IAEA,SAASuE;QACPzD,eAAe;QACfG,qBAAqBkB,OAAO,GAAGnB,gBAAgBmB,OAAO;QACtDvB,mBAAmB;IACrB;IAEA,SAAS4D;QACP,IAAI7D,mBAAmBE,gBAAgB,MAAM;YAC3C4D;YACA;QACF;QACAF;IACF;IAEA,SAASG,eACPzC,MAAc,EACdmB,QAA4B,EAC5BuB,OAAiC;QAEjC7D,eAAe;YAACmB;YAAQmB;QAAQ;QAChCnC,qBAAqBkB,OAAO,GAAGwC;QAC/B/D,mBAAmB;IACrB;IAEA,SAAS6D;QACP,MAAME,UAAU1D,qBAAqBkB,OAAO;QAC5C,MAAMyC,kBAAkB5D,gBAAgBmB,OAAO;QAC/CvB,mBAAmB;QACnB,4EAA4E;QAC5E,6EAA6E;QAC7EiE,OAAOC,UAAU,CAAC;YAChB,MAAMC,cAAcJ,SAASK,cAAcL,UAAUC;YACrDG,aAAaE;YACbnE,eAAe;QACjB,GAAG;IACL;IAEA,SAASoE;QACP3D,eAAe4D,MAAM,CAAChE,WAAW;YAC/BiE,SAAS,CAAC9C;gBACRjE,MAAMiE,KAAK,CAAC+C,mBAAmB/C;YACjC;QACF;IACF;IAEA,qBACE;;0BACE,MAACpC;gBAAIC,WAAU;;kCACb,KAAClB;wBACCS,eAAeA;wBACfC,aAAaA;wBACbiD,KAAKF;wBACLtB,iBAAiBA;wBACjBkE,YAAY3E,mBAAmBE,gBAAgB;wBAC/CE,eAAeA;wBACfC,iBAAiBA;wBACjBuE,gBAAgBf;wBAChBgB,YAAYjE,eAAea,SAAS;wBACpCqD,UAAUP;wBACVQ,cAAczF,cAAcmC,SAAS;wBACrCuD,SAAS,CAACrC,OAAS,KAAKD,MAAMC;wBAC9BsC,eAAelD,QAAQkD,aAAa;;oBAErCvF,MAAMgC,OAAO,iBAAG,KAAC9C;wBAAsBc,OAAOA;yBAAY;kCAC3D,KAACH;wBAAIC,WAAU;kCACb,cAAA,MAACD;4BAAIC,WAAU;;8CACb,KAACnB;oCACC4D,KAAKF;oCACLnC,eAAesC,aAAab;oCAC5B6D,qBAAqB/B;;gCAEtBjB,4BACC,KAAC1D;oCACCO,eAAeA;oCACfiC,KAAKkB;oCACLG,sBAAsBA;oCACtBE,mBAAmBL,YAAYiD,WAAW,GAAG3E,YAAY+B;oCACzD6C,8BAA8B/B;oCAC9BgC,yBAAyB9E,sBAAsBgD,gBAAgB/C;oCAC/DJ,eAAeA;oCACfK,iBAAiBA;oCACjB6E,qBAAqBpF,aAAaoB,UAAU;oCAC5CiE,kBAAkBxB;qCAElB;;;;;;0BAIV,KAACxF;gBACC8C,IAAIjB;gBACJoF,QAAQ7E;gBACR8E,MAAMzF,mBAAmBS;gBACzBiF,SAAS5B;gBACTtB,sBAAsBA;gBACtBmD,yBAAyB;;;;AAIjC;AAEA,SAASjB,mBAAmB/C,KAAc;IACxC,IAAIA,iBAAiBpE,YAAYoE,MAAMiE,IAAI,KAAK,wBAAwB;QACtE,OAAO;IACT;IACA,OAAO;AACT;AAEA,SAASlC,qBAAqBpB,YAA0B,EAAEkB,SAAiB;IACzE,KAAK,MAAMpC,QAAQkB,aAAanB,KAAK,CAAE;QACrC,MAAM0E,UAAUzE,KAAK0E,QAAQ,CAAC3D,IAAI,CAAC,CAAC4D,YAAcA,UAAU1E,EAAE,KAAKmC;QACnE,IAAIqC,SAAS,OAAO;YAACvE,QAAQF,KAAKC,EAAE;YAAEmC,WAAWqC,QAAQxE,EAAE;QAAA;IAC7D;IACA,OAAOb;AACT"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/workflow-run-view/workflow-run-view.tsx"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {RelativeTimeProvider} from '@shipfox/react-ui/relative-time';\nimport {Tabs, TabsContent, TabsContents} from '@shipfox/react-ui/tabs';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {useNavigate} from '@tanstack/react-router';\nimport {useEffect, useId, useRef, useState} from 'react';\nimport {\n type JobExecution,\n resolveJobExecution,\n type StepSourceLocation,\n type WorkflowRunRerunMode,\n} from '#core/workflow-run.js';\nimport type {RunAnnotationSummary} from '#core/workflow-run-tabs.js';\nimport {\n type WorkflowRunSelectionInput,\n withoutWorkflowRunSelectionSearch,\n} from '#core/workflow-run-url-state.js';\nimport {\n useCancelWorkflowRunMutation,\n useRerunWorkflowRunMutation,\n useWorkflowRunAttemptQuery,\n} from '#hooks/api/workflow-runs.js';\nimport {WORKFLOW_RUN_TABS, type WorkflowRunTab} from '#routes/inputs.js';\nimport {JobGraph} from '../job-graph/index.js';\nimport type {JobGraphSelectionSource} from '../job-graph/types.js';\nimport {WorkflowRunSummary} from '../workflow-run-summary/index.js';\nimport {\n RunAnnotationSummaryLine,\n RunAnnotationsEmpty,\n RunJobsList,\n RunTabStrip,\n} from '../workflow-run-tabs/index.js';\nimport {WorkflowSourceContent, WorkflowSourcePanel} from '../workflow-source-panel/index.js';\nimport {resolveWorkflowRunSelection} from './workflow-run-selection.js';\nimport {\n WorkflowRunContentSkeleton,\n WorkflowRunNotFound,\n WorkflowRunSkeleton,\n WorkflowRunStaleError,\n} from './workflow-run-states.js';\n\ninterface WorkflowSourceFocus {\n stepId: string;\n location: StepSourceLocation;\n}\n\nexport interface WorkflowRunViewProps {\n projectId: string;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n workflowRunId?: string | undefined;\n selection?: WorkflowRunSelectionInput | undefined;\n onSelectionChange?: ((selection: WorkflowRunSelectionInput) => void) | undefined;\n tab?: WorkflowRunTab | undefined;\n onTabChange?: ((tab: WorkflowRunTab) => void) | undefined;\n annotationSummary?: RunAnnotationSummary | undefined;\n}\n\n/**\n * Renders the run for `workflowRunId`, keeping the local tab strip mounted while the run\n * request is pending so polling never inserts navigation under the user's cursor.\n */\nexport function WorkflowRunView({\n projectId,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n selection,\n onSelectionChange,\n tab,\n onTabChange,\n annotationSummary,\n}: WorkflowRunViewProps) {\n const runQuery = useWorkflowRunAttemptQuery({workflowRunId, runAttempt: selection?.runAttempt});\n const rerunMutation = useRerunWorkflowRunMutation(projectId);\n\n return (\n <RelativeTimeProvider>\n <div className=\"flex min-h-0 min-w-0 flex-1 overflow-hidden\">\n <RunViewContent\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n query={runQuery}\n rerunMutation={rerunMutation}\n selection={selection}\n onSelectionChange={onSelectionChange}\n tab={tab}\n onTabChange={onTabChange}\n annotationSummary={annotationSummary}\n />\n </div>\n </RelativeTimeProvider>\n );\n}\n\nfunction RunViewContent({\n workspaceSlug,\n projectSlug,\n query,\n rerunMutation,\n selection,\n onSelectionChange,\n tab,\n onTabChange,\n annotationSummary,\n}: {\n workspaceSlug: string | undefined;\n projectSlug: string | undefined;\n query: ReturnType<typeof useWorkflowRunAttemptQuery>;\n rerunMutation: ReturnType<typeof useRerunWorkflowRunMutation>;\n selection: WorkflowRunSelectionInput | undefined;\n onSelectionChange: ((selection: WorkflowRunSelectionInput) => void) | undefined;\n tab: WorkflowRunTab | undefined;\n onTabChange: ((tab: WorkflowRunTab) => void) | undefined;\n annotationSummary: RunAnnotationSummary | undefined;\n}) {\n const navigate = useNavigate();\n const [selectedJobId, setSelectedJobId] = useState<string | undefined>();\n const [selectedJobExecutionId, setSelectedJobExecutionId] = useState<string | undefined>();\n const [localTab, setLocalTab] = useState<WorkflowRunTab>(tab ?? 'summary');\n const [sourcePanelOpen, setSourcePanelOpen] = useState(false);\n const [sourceFocus, setSourceFocus] = useState<WorkflowSourceFocus | null>(null);\n const sourcePanelId = useId();\n // The step Source button that last opened the panel, so Escape / Close returns focus to it.\n const lastSourceTriggerRef = useRef<HTMLButtonElement | null>(null);\n const selectionControlled = selection !== undefined;\n const tabControlled = onTabChange !== undefined;\n const activeTab = tabControlled ? (tab ?? 'summary') : localTab;\n const sourceAvailable =\n query.data?.sourceSnapshot !== null && query.data?.sourceSnapshot !== undefined;\n const cancelMutation = useCancelWorkflowRunMutation(query.data);\n\n useEffect(() => {\n if (!sourceAvailable) {\n setSourcePanelOpen(false);\n setSourceFocus(null);\n }\n }, [sourceAvailable]);\n\n // If a refetch drops the focused step or its location, close the panel so it never points at\n // an unmounted Source button.\n useEffect(() => {\n if (!sourceFocus) return;\n const stillLocated = query.data?.jobs.some((job) =>\n job.jobExecutions.some((execution) =>\n execution.steps.some((step) => step.id === sourceFocus.stepId && step.sourceLocation),\n ),\n );\n if (!stillLocated) {\n setSourceFocus(null);\n setSourcePanelOpen(false);\n }\n }, [sourceFocus, query.data]);\n\n const runData = query.data;\n const resolvedSelection =\n selectionControlled && runData\n ? resolveWorkflowRunSelection({\n run: runData,\n selection: selection as WorkflowRunSelectionInput,\n })\n : undefined;\n const hasExplicitJobSelection = Boolean(\n selection?.jobId || selection?.jobExecutionId || selection?.stepId || selection?.stepAttemptId,\n );\n const selectedJob = selectionControlled\n ? hasExplicitJobSelection\n ? resolvedSelection?.job\n : undefined\n : (runData?.jobs.find((job) => job.id === selectedJobId) ?? runData?.jobs.at(0));\n const selectedJobExecution = selectionControlled\n ? resolvedSelection?.jobExecution\n : selectedJob\n ? resolveJobExecution(selectedJob, selectedJobExecutionId)\n : undefined;\n const selectedAttemptId = selectionControlled\n ? (resolvedSelection?.selectedAttemptId ?? null)\n : undefined;\n // Explicit per-step focus wins; fall back to the URL-selected step so deep links still\n // pre-highlight source in the Source tab.\n const highlightedLineRange =\n sourceFocus?.location ?? resolvedSelection?.step?.sourceLocation ?? null;\n const sourceSnapshot = runData?.sourceSnapshot ?? null;\n\n function changeTab(nextTab: WorkflowRunTab) {\n if (!tabControlled) {\n setLocalTab(nextTab);\n } else if (activeTab !== nextTab) {\n onTabChange?.(nextTab);\n }\n\n if (nextTab !== 'jobs') {\n setSourcePanelOpen(false);\n setSourceFocus(null);\n }\n }\n\n function handleTabChange(nextTab: string) {\n if (!WORKFLOW_RUN_TABS.some((value) => value === nextTab)) return;\n changeTab(nextTab as WorkflowRunTab);\n }\n\n async function rerun(mode: WorkflowRunRerunMode) {\n if (!runData || !workspaceSlug || !projectSlug) {\n toast.error('Could not start re-run from this route.');\n return;\n }\n try {\n const run = await rerunMutation.mutateAsync({workflowRunId: runData.id, mode});\n toast.success('Re-run started');\n await navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',\n params: {\n workspaceSlug,\n projectSlug,\n workflowRunId: run.id,\n },\n search: ((previous: Record<string, unknown>) =>\n withoutWorkflowRunSelectionSearch(previous)) as never,\n });\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : 'Could not start re-run');\n }\n }\n\n function selectJob(jobId: string | undefined, source: JobGraphSelectionSource = 'pointer') {\n if (source === 'pointer') changeTab('jobs');\n if (!selectionControlled) {\n setSelectedJobId(jobId);\n setSelectedJobExecutionId(undefined);\n return;\n }\n // Keyboard roving focus is an interaction within the Summary graph. Keep that panel mounted\n // and let JobGraphView own the visual selection so focus is not moved to a different tab.\n if (source === 'keyboard') return;\n\n onSelectionChange?.(\n jobId ? {jobId, runAttempt: selection?.runAttempt} : {runAttempt: selection?.runAttempt},\n );\n }\n\n function selectJobExecution(jobExecutionId: string | undefined) {\n if (!selectionControlled) {\n setSelectedJobExecutionId(jobExecutionId);\n return;\n }\n if (!selectedJob) return;\n\n onSelectionChange?.({\n jobId: selectedJob.id,\n jobExecutionId,\n runAttempt: selection?.runAttempt,\n });\n }\n\n function selectAttempt(attemptId: string | undefined) {\n if (!selectionControlled || !selectedJob || !selectedJobExecution) return;\n\n if (!attemptId) {\n onSelectionChange?.({\n jobId: selectedJob.id,\n jobExecutionId: selectedJobExecution.id,\n runAttempt: selection?.runAttempt,\n });\n return;\n }\n\n const match = findAttemptSelection(selectedJobExecution, attemptId);\n if (!match) return;\n\n onSelectionChange?.({\n jobId: selectedJob.id,\n jobExecutionId: selectedJobExecution.id,\n stepId: match.stepId,\n stepAttemptId: match.attemptId,\n runAttempt: selection?.runAttempt,\n });\n }\n\n function openStepSource(\n stepId: string,\n location: StepSourceLocation,\n trigger: HTMLButtonElement | null,\n ) {\n setSourceFocus({stepId, location});\n lastSourceTriggerRef.current = trigger;\n setSourcePanelOpen(true);\n }\n\n function closeSourcePanel() {\n const trigger = lastSourceTriggerRef.current;\n setSourcePanelOpen(false);\n // Defer so focus lands after the panel unmounts; clear the focus only after focusing so the\n // opener button is still expanded (force-visible) on return.\n window.setTimeout(() => {\n if (trigger?.isConnected) trigger.focus();\n setSourceFocus(null);\n }, 0);\n }\n\n function cancelRun() {\n cancelMutation.mutate(undefined, {\n onError: (error) => {\n toast.error(cancelErrorMessage(error));\n },\n });\n }\n\n const fatalError = query.isError && runData === undefined;\n const tabState = fatalError ? (\n query.error instanceof ApiError && query.error.status === 404 ? (\n <WorkflowRunNotFound />\n ) : (\n <QueryLoadError query={query} subject=\"workflow run\" icon=\"pulseLine\" />\n )\n ) : query.isPending || runData === undefined ? (\n <WorkflowRunContentSkeleton />\n ) : null;\n\n return (\n <>\n <Tabs\n value={activeTab}\n onValueChange={handleTabChange}\n className=\"flex min-w-0 flex-1 flex-col gap-0\"\n >\n {runData ? (\n <WorkflowRunSummary\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n run={runData}\n cancelling={cancelMutation.isPending}\n onCancel={cancelRun}\n rerunPending={rerunMutation.isPending}\n onRerun={(mode) => void rerun(mode)}\n latestAttempt={runData.latestAttempt}\n />\n ) : (\n <WorkflowRunSkeleton />\n )}\n {runData && query.isError ? <WorkflowRunStaleError query={query} /> : null}\n <RunTabStrip\n jobCount={runData?.jobs.length}\n jobsFailed={runData?.jobs.filter((job) => job.status === 'failed').length}\n annotationSummary={annotationSummary}\n />\n <TabsContents className=\"min-h-0 flex-1 overflow-auto bg-background-neutral-background pb-24 pt-16\">\n <TabsContent\n value=\"summary\"\n tabIndex={-1}\n className=\"mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24 outline-none\"\n >\n {runData ? (\n <JobGraph\n run={runData}\n selectedJobId={selectedJob?.id}\n onSelectedJobChange={selectJob}\n />\n ) : (\n tabState\n )}\n </TabsContent>\n <TabsContent\n value=\"jobs\"\n tabIndex={-1}\n className=\"mx-auto w-full max-w-[1120px] px-24 outline-none\"\n >\n {runData ? (\n <RunJobsList\n jobs={runData.jobs}\n selectedJobId={selectedJob?.id}\n onSelectedJobChange={selectJob}\n workspaceSlug={workspaceSlug}\n selectedJobExecution={selectedJobExecution}\n selectedAttemptId={selectedAttemptId}\n onSelectedJobExecutionChange={selectJobExecution}\n onSelectedAttemptChange={selectionControlled ? selectAttempt : undefined}\n sourcePanelId={sourcePanelId}\n sourceAvailable={sourceAvailable}\n focusedSourceStepId={sourceFocus?.stepId ?? null}\n onOpenStepSource={openStepSource}\n />\n ) : (\n tabState\n )}\n </TabsContent>\n <TabsContent\n value=\"annotations\"\n tabIndex={-1}\n className=\"mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24 outline-none\"\n >\n {runData ? (\n <>\n <RunAnnotationSummaryLine\n summary={annotationSummary}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={runData.id}\n search={selection}\n />\n <RunAnnotationsEmpty />\n </>\n ) : (\n tabState\n )}\n </TabsContent>\n <TabsContent\n value=\"source\"\n keepMounted={sourceSnapshot !== null}\n tabIndex={-1}\n className=\"mx-auto flex min-h-full w-full max-w-[1120px] px-24 outline-none\"\n >\n {runData ? (\n sourceSnapshot ? (\n <WorkflowSourceContent\n source={sourceSnapshot}\n highlightedLineRange={highlightedLineRange}\n scrollHighlightedIntoView\n />\n ) : (\n <EmptyState\n className=\"min-h-160 w-full\"\n icon=\"fileDamageLine\"\n title=\"Source snapshot unavailable\"\n description={\n runData.isTemporary\n ? 'Temporary runs do not capture workflow source.'\n : 'This run was created before workflow source snapshots were captured.'\n }\n />\n )\n ) : (\n tabState\n )}\n </TabsContent>\n </TabsContents>\n </Tabs>\n <WorkflowSourcePanel\n id={sourcePanelId}\n source={sourceSnapshot}\n open={sourcePanelOpen && sourceAvailable}\n onClose={closeSourcePanel}\n highlightedLineRange={highlightedLineRange}\n scrollHighlightedIntoView\n />\n </>\n );\n}\n\nfunction cancelErrorMessage(error: unknown): string {\n if (error instanceof ApiError && error.code === 'run-already-finished') {\n return 'This workflow run has already finished.';\n }\n return 'Could not cancel workflow run.';\n}\n\nfunction findAttemptSelection(jobExecution: JobExecution, attemptId: string) {\n for (const step of jobExecution.steps) {\n const attempt = step.attempts.find((candidate) => candidate.id === attemptId);\n if (attempt) return {stepId: step.id, attemptId: attempt.id};\n }\n return undefined;\n}\n"],"names":["ApiError","QueryLoadError","EmptyState","RelativeTimeProvider","Tabs","TabsContent","TabsContents","toast","useNavigate","useEffect","useId","useRef","useState","resolveJobExecution","withoutWorkflowRunSelectionSearch","useCancelWorkflowRunMutation","useRerunWorkflowRunMutation","useWorkflowRunAttemptQuery","WORKFLOW_RUN_TABS","JobGraph","WorkflowRunSummary","RunAnnotationSummaryLine","RunAnnotationsEmpty","RunJobsList","RunTabStrip","WorkflowSourceContent","WorkflowSourcePanel","resolveWorkflowRunSelection","WorkflowRunContentSkeleton","WorkflowRunNotFound","WorkflowRunSkeleton","WorkflowRunStaleError","WorkflowRunView","projectId","workspaceSlug","projectSlug","workflowRunId","selection","onSelectionChange","tab","onTabChange","annotationSummary","runQuery","runAttempt","rerunMutation","div","className","RunViewContent","query","navigate","selectedJobId","setSelectedJobId","selectedJobExecutionId","setSelectedJobExecutionId","localTab","setLocalTab","sourcePanelOpen","setSourcePanelOpen","sourceFocus","setSourceFocus","sourcePanelId","lastSourceTriggerRef","selectionControlled","undefined","tabControlled","activeTab","sourceAvailable","data","sourceSnapshot","cancelMutation","stillLocated","jobs","some","job","jobExecutions","execution","steps","step","id","stepId","sourceLocation","runData","resolvedSelection","run","hasExplicitJobSelection","Boolean","jobId","jobExecutionId","stepAttemptId","selectedJob","find","at","selectedJobExecution","jobExecution","selectedAttemptId","highlightedLineRange","location","changeTab","nextTab","handleTabChange","value","rerun","mode","error","mutateAsync","success","to","params","search","previous","message","selectJob","source","selectJobExecution","selectAttempt","attemptId","match","findAttemptSelection","openStepSource","trigger","current","closeSourcePanel","window","setTimeout","isConnected","focus","cancelRun","mutate","onError","cancelErrorMessage","fatalError","isError","tabState","status","subject","icon","isPending","onValueChange","cancelling","onCancel","rerunPending","onRerun","latestAttempt","jobCount","length","jobsFailed","filter","tabIndex","onSelectedJobChange","onSelectedJobExecutionChange","onSelectedAttemptChange","focusedSourceStepId","onOpenStepSource","summary","keepMounted","scrollHighlightedIntoView","title","description","isTemporary","open","onClose","code","attempt","attempts","candidate"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-source-panel/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-source-panel/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/workflow-source-panel/index.ts"],"sourcesContent":["export * from './workflow-source-panel.js';\n"],"names":[],"mappings":"AAAA,cAAc,6BAA6B"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/workflow-source-panel/index.ts"],"sourcesContent":["export * from './workflow-source-content.js';\nexport * from './workflow-source-panel.js';\n"],"names":[],"mappings":"AAAA,cAAc,+BAA+B;AAC7C,cAAc,6BAA6B"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type CodeBlockHighlightedLineRange } from '@shipfox/react-ui/code-block';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import type { WorkflowSourceSnapshot } from '#core/workflow-run.js';
|
|
4
|
+
export interface WorkflowSourceContentProps {
|
|
5
|
+
source: WorkflowSourceSnapshot;
|
|
6
|
+
highlightedLineRange?: CodeBlockHighlightedLineRange | null | undefined;
|
|
7
|
+
scrollHighlightedIntoView?: boolean | undefined;
|
|
8
|
+
headerAction?: ReactNode;
|
|
9
|
+
className?: string | undefined;
|
|
10
|
+
}
|
|
11
|
+
export declare function WorkflowSourceContent({ source, highlightedLineRange, scrollHighlightedIntoView, headerAction, className, }: WorkflowSourceContentProps): import("react").JSX.Element;
|
|
12
|
+
//# sourceMappingURL=workflow-source-content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-source-content.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-source-panel/workflow-source-content.tsx"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,6BAA6B,EAEnC,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AACrC,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,uBAAuB,CAAC;AAQlE,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,sBAAsB,CAAC;IAC/B,oBAAoB,CAAC,EAAE,6BAA6B,GAAG,IAAI,GAAG,SAAS,CAAC;IACxE,yBAAyB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAChD,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,wBAAgB,qBAAqB,CAAC,EACpC,MAAM,EACN,oBAAoB,EACpB,yBAAyB,EACzB,YAAY,EACZ,SAAS,GACV,EAAE,0BAA0B,+BAgD5B"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { CodeBlock, CodeBlockBody, CodeBlockContent, CodeBlockCopyButton, CodeBlockFilename, CodeBlockFiles, CodeBlockHeader, CodeBlockItem } from '@shipfox/react-ui/code-block';
|
|
3
|
+
import { cn } from '@shipfox/react-ui/utils';
|
|
4
|
+
const WORKFLOW_SOURCE_FILENAME = 'workflow.yaml';
|
|
5
|
+
const WORKFLOW_SOURCE_CODE_THEMES = {
|
|
6
|
+
light: 'vitesse-dark',
|
|
7
|
+
dark: 'vitesse-dark'
|
|
8
|
+
};
|
|
9
|
+
export function WorkflowSourceContent({ source, highlightedLineRange, scrollHighlightedIntoView, headerAction, className }) {
|
|
10
|
+
const data = [
|
|
11
|
+
{
|
|
12
|
+
language: 'yaml',
|
|
13
|
+
filename: WORKFLOW_SOURCE_FILENAME,
|
|
14
|
+
code: source.content
|
|
15
|
+
}
|
|
16
|
+
];
|
|
17
|
+
return /*#__PURE__*/ _jsxs(CodeBlock, {
|
|
18
|
+
data: data,
|
|
19
|
+
className: cn('flex size-full flex-col rounded-none bg-background-contrast-base shadow-none', className),
|
|
20
|
+
children: [
|
|
21
|
+
/*#__PURE__*/ _jsxs(CodeBlockHeader, {
|
|
22
|
+
className: "shrink-0 border-b border-border-contrast-base bg-background-contrast-base",
|
|
23
|
+
children: [
|
|
24
|
+
/*#__PURE__*/ _jsx(CodeBlockFiles, {
|
|
25
|
+
children: (item)=>/*#__PURE__*/ _jsx(CodeBlockFilename, {
|
|
26
|
+
value: item.filename,
|
|
27
|
+
children: item.filename
|
|
28
|
+
})
|
|
29
|
+
}),
|
|
30
|
+
/*#__PURE__*/ _jsx(CodeBlockCopyButton, {}),
|
|
31
|
+
headerAction
|
|
32
|
+
]
|
|
33
|
+
}),
|
|
34
|
+
/*#__PURE__*/ _jsx(CodeBlockBody, {
|
|
35
|
+
className: "flex min-h-0 flex-1 overflow-auto scrollbar",
|
|
36
|
+
children: (item)=>/*#__PURE__*/ _jsx(CodeBlockItem, {
|
|
37
|
+
value: item.filename,
|
|
38
|
+
className: cn('min-h-full px-0 pb-0', '[&>div]:rounded-none [&>div]:border-0 [&>div]:bg-background-contrast-base [&>div]:dark:bg-background-contrast-base', '[&_code]:!text-sm [&_code]:!text-foreground-neutral-on-inverted [&_.line]:!text-sm [&_.line]:before:!text-sm [&_.line]:before:!text-foreground-neutral-muted'),
|
|
39
|
+
children: /*#__PURE__*/ _jsx(CodeBlockContent, {
|
|
40
|
+
language: "yaml",
|
|
41
|
+
themes: WORKFLOW_SOURCE_CODE_THEMES,
|
|
42
|
+
syntaxHighlighting: true,
|
|
43
|
+
highlightedLineRange: highlightedLineRange,
|
|
44
|
+
scrollHighlightedIntoView: scrollHighlightedIntoView,
|
|
45
|
+
children: item.code
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
]
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=workflow-source-content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/workflow-source-panel/workflow-source-content.tsx"],"sourcesContent":["import {\n CodeBlock,\n CodeBlockBody,\n CodeBlockContent,\n CodeBlockCopyButton,\n CodeBlockFilename,\n CodeBlockFiles,\n CodeBlockHeader,\n type CodeBlockHighlightedLineRange,\n CodeBlockItem,\n} from '@shipfox/react-ui/code-block';\nimport {cn} from '@shipfox/react-ui/utils';\nimport type {ReactNode} from 'react';\nimport type {WorkflowSourceSnapshot} from '#core/workflow-run.js';\n\nconst WORKFLOW_SOURCE_FILENAME = 'workflow.yaml';\nconst WORKFLOW_SOURCE_CODE_THEMES = {\n light: 'vitesse-dark',\n dark: 'vitesse-dark',\n};\n\nexport interface WorkflowSourceContentProps {\n source: WorkflowSourceSnapshot;\n highlightedLineRange?: CodeBlockHighlightedLineRange | null | undefined;\n scrollHighlightedIntoView?: boolean | undefined;\n headerAction?: ReactNode;\n className?: string | undefined;\n}\n\nexport function WorkflowSourceContent({\n source,\n highlightedLineRange,\n scrollHighlightedIntoView,\n headerAction,\n className,\n}: WorkflowSourceContentProps) {\n const data = [\n {\n language: 'yaml',\n filename: WORKFLOW_SOURCE_FILENAME,\n code: source.content,\n },\n ];\n\n return (\n <CodeBlock\n data={data}\n className={cn(\n 'flex size-full flex-col rounded-none bg-background-contrast-base shadow-none',\n className,\n )}\n >\n <CodeBlockHeader className=\"shrink-0 border-b border-border-contrast-base bg-background-contrast-base\">\n <CodeBlockFiles>\n {(item) => <CodeBlockFilename value={item.filename}>{item.filename}</CodeBlockFilename>}\n </CodeBlockFiles>\n <CodeBlockCopyButton />\n {headerAction}\n </CodeBlockHeader>\n <CodeBlockBody className=\"flex min-h-0 flex-1 overflow-auto scrollbar\">\n {(item) => (\n <CodeBlockItem\n value={item.filename}\n className={cn(\n 'min-h-full px-0 pb-0',\n '[&>div]:rounded-none [&>div]:border-0 [&>div]:bg-background-contrast-base [&>div]:dark:bg-background-contrast-base',\n '[&_code]:!text-sm [&_code]:!text-foreground-neutral-on-inverted [&_.line]:!text-sm [&_.line]:before:!text-sm [&_.line]:before:!text-foreground-neutral-muted',\n )}\n >\n <CodeBlockContent\n language=\"yaml\"\n themes={WORKFLOW_SOURCE_CODE_THEMES}\n syntaxHighlighting\n highlightedLineRange={highlightedLineRange}\n scrollHighlightedIntoView={scrollHighlightedIntoView}\n >\n {item.code}\n </CodeBlockContent>\n </CodeBlockItem>\n )}\n </CodeBlockBody>\n </CodeBlock>\n );\n}\n"],"names":["CodeBlock","CodeBlockBody","CodeBlockContent","CodeBlockCopyButton","CodeBlockFilename","CodeBlockFiles","CodeBlockHeader","CodeBlockItem","cn","WORKFLOW_SOURCE_FILENAME","WORKFLOW_SOURCE_CODE_THEMES","light","dark","WorkflowSourceContent","source","highlightedLineRange","scrollHighlightedIntoView","headerAction","className","data","language","filename","code","content","item","value","themes","syntaxHighlighting"],"mappings":";AAAA,SACEA,SAAS,EACTC,aAAa,EACbC,gBAAgB,EAChBC,mBAAmB,EACnBC,iBAAiB,EACjBC,cAAc,EACdC,eAAe,EAEfC,aAAa,QACR,+BAA+B;AACtC,SAAQC,EAAE,QAAO,0BAA0B;AAI3C,MAAMC,2BAA2B;AACjC,MAAMC,8BAA8B;IAClCC,OAAO;IACPC,MAAM;AACR;AAUA,OAAO,SAASC,sBAAsB,EACpCC,MAAM,EACNC,oBAAoB,EACpBC,yBAAyB,EACzBC,YAAY,EACZC,SAAS,EACkB;IAC3B,MAAMC,OAAO;QACX;YACEC,UAAU;YACVC,UAAUZ;YACVa,MAAMR,OAAOS,OAAO;QACtB;KACD;IAED,qBACE,MAACvB;QACCmB,MAAMA;QACND,WAAWV,GACT,gFACAU;;0BAGF,MAACZ;gBAAgBY,WAAU;;kCACzB,KAACb;kCACE,CAACmB,qBAAS,KAACpB;gCAAkBqB,OAAOD,KAAKH,QAAQ;0CAAGG,KAAKH,QAAQ;;;kCAEpE,KAAClB;oBACAc;;;0BAEH,KAAChB;gBAAciB,WAAU;0BACtB,CAACM,qBACA,KAACjB;wBACCkB,OAAOD,KAAKH,QAAQ;wBACpBH,WAAWV,GACT,wBACA,sHACA;kCAGF,cAAA,KAACN;4BACCkB,UAAS;4BACTM,QAAQhB;4BACRiB,kBAAkB;4BAClBZ,sBAAsBA;4BACtBC,2BAA2BA;sCAE1BQ,KAAKF,IAAI;;;;;;AAOxB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { CodeBlockHighlightedLineRange } from '@shipfox/react-ui/code-block';
|
|
2
2
|
import type { WorkflowSourceSnapshot } from '#core/workflow-run.js';
|
|
3
3
|
export interface WorkflowSourcePanelProps {
|
|
4
4
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-source-panel.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-source-panel/workflow-source-panel.tsx"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"workflow-source-panel.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-source-panel/workflow-source-panel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,6BAA6B,EAAC,MAAM,8BAA8B,CAAC;AAGhF,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,uBAAuB,CAAC;AAGlE,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACtC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,oBAAoB,CAAC,EAAE,6BAA6B,GAAG,IAAI,GAAG,SAAS,CAAC;IACxE,yBAAyB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAChD,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,wBAAgB,mBAAmB,CAAC,EAClC,EAAE,EACF,MAAM,EACN,IAAI,EACJ,OAAO,EACP,oBAAoB,EACpB,yBAAyB,EACzB,SAAS,GACV,EAAE,wBAAwB,sCA2C1B"}
|