@shipfox/client-workflows 13.0.0 → 14.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/.storybook/main.ts +16 -1
  2. package/.storybook/workspace-source.ts +31 -0
  3. package/.turbo/turbo-build.log +1 -1
  4. package/CHANGELOG.md +23 -0
  5. package/dist/components/job-graph/job-duration-label.js +1 -1
  6. package/dist/components/job-graph/job-duration-label.js.map +1 -1
  7. package/dist/components/job-graph/job-graph-content.d.ts +2 -1
  8. package/dist/components/job-graph/job-graph-content.d.ts.map +1 -1
  9. package/dist/components/job-graph/job-graph-content.js +3 -3
  10. package/dist/components/job-graph/job-graph-content.js.map +1 -1
  11. package/dist/components/job-graph/job-graph-view.d.ts +2 -1
  12. package/dist/components/job-graph/job-graph-view.d.ts.map +1 -1
  13. package/dist/components/job-graph/job-graph-view.js +2 -2
  14. package/dist/components/job-graph/job-graph-view.js.map +1 -1
  15. package/dist/components/job-graph/job-node.js +5 -5
  16. package/dist/components/job-graph/job-node.js.map +1 -1
  17. package/dist/components/job-graph/types.d.ts +2 -1
  18. package/dist/components/job-graph/types.d.ts.map +1 -1
  19. package/dist/components/job-graph/types.js.map +1 -1
  20. package/dist/components/workflow-run-duration-label.js +1 -1
  21. package/dist/components/workflow-run-duration-label.js.map +1 -1
  22. package/dist/components/workflow-run-number-label.js +1 -1
  23. package/dist/components/workflow-run-number-label.js.map +1 -1
  24. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +2 -2
  25. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js.map +1 -1
  26. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts +2 -7
  27. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
  28. package/dist/components/workflow-run-summary/workflow-run-summary.js +92 -68
  29. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
  30. package/dist/components/workflow-run-tabs/index.d.ts +6 -0
  31. package/dist/components/workflow-run-tabs/index.d.ts.map +1 -0
  32. package/dist/components/workflow-run-tabs/index.js +7 -0
  33. package/dist/components/workflow-run-tabs/index.js.map +1 -0
  34. package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts +11 -0
  35. package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts.map +1 -0
  36. package/dist/components/workflow-run-tabs/run-annotation-summary-line.js +71 -0
  37. package/dist/components/workflow-run-tabs/run-annotation-summary-line.js.map +1 -0
  38. package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts +2 -0
  39. package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts.map +1 -0
  40. package/dist/components/workflow-run-tabs/run-annotations-empty.js +11 -0
  41. package/dist/components/workflow-run-tabs/run-annotations-empty.js.map +1 -0
  42. package/dist/components/workflow-run-tabs/run-jobs-list.d.ts +18 -0
  43. package/dist/components/workflow-run-tabs/run-jobs-list.d.ts.map +1 -0
  44. package/dist/components/workflow-run-tabs/run-jobs-list.js +107 -0
  45. package/dist/components/workflow-run-tabs/run-jobs-list.js.map +1 -0
  46. package/dist/components/workflow-run-tabs/run-tab-count.d.ts +6 -0
  47. package/dist/components/workflow-run-tabs/run-tab-count.d.ts.map +1 -0
  48. package/dist/components/workflow-run-tabs/run-tab-count.js +27 -0
  49. package/dist/components/workflow-run-tabs/run-tab-count.js.map +1 -0
  50. package/dist/components/workflow-run-tabs/run-tab-strip.d.ts +8 -0
  51. package/dist/components/workflow-run-tabs/run-tab-strip.d.ts.map +1 -0
  52. package/dist/components/workflow-run-tabs/run-tab-strip.js +82 -0
  53. package/dist/components/workflow-run-tabs/run-tab-strip.js.map +1 -0
  54. package/dist/components/workflow-run-view/workflow-run-selection.d.ts.map +1 -1
  55. package/dist/components/workflow-run-view/workflow-run-selection.js +40 -1
  56. package/dist/components/workflow-run-view/workflow-run-selection.js.map +1 -1
  57. package/dist/components/workflow-run-view/workflow-run-states.d.ts +1 -0
  58. package/dist/components/workflow-run-view/workflow-run-states.d.ts.map +1 -1
  59. package/dist/components/workflow-run-view/workflow-run-states.js +10 -1
  60. package/dist/components/workflow-run-view/workflow-run-states.js.map +1 -1
  61. package/dist/components/workflow-run-view/workflow-run-view.d.ts +8 -4
  62. package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
  63. package/dist/components/workflow-run-view/workflow-run-view.js +125 -78
  64. package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
  65. package/dist/components/workflow-source-panel/index.d.ts +1 -0
  66. package/dist/components/workflow-source-panel/index.d.ts.map +1 -1
  67. package/dist/components/workflow-source-panel/index.js +1 -0
  68. package/dist/components/workflow-source-panel/index.js.map +1 -1
  69. package/dist/components/workflow-source-panel/workflow-source-content.d.ts +12 -0
  70. package/dist/components/workflow-source-panel/workflow-source-content.d.ts.map +1 -0
  71. package/dist/components/workflow-source-panel/workflow-source-content.js +53 -0
  72. package/dist/components/workflow-source-panel/workflow-source-content.js.map +1 -0
  73. package/dist/components/workflow-source-panel/workflow-source-panel.d.ts +1 -1
  74. package/dist/components/workflow-source-panel/workflow-source-panel.d.ts.map +1 -1
  75. package/dist/components/workflow-source-panel/workflow-source-panel.js +7 -54
  76. package/dist/components/workflow-source-panel/workflow-source-panel.js.map +1 -1
  77. package/dist/core/entities/step.d.ts +1 -1
  78. package/dist/core/entities/step.d.ts.map +1 -1
  79. package/dist/core/entities/step.js +2 -0
  80. package/dist/core/entities/step.js.map +1 -1
  81. package/dist/core/workflow-run-tabs.d.ts +9 -0
  82. package/dist/core/workflow-run-tabs.d.ts.map +1 -0
  83. package/dist/core/workflow-run-tabs.js +3 -0
  84. package/dist/core/workflow-run-tabs.js.map +1 -0
  85. package/dist/pages/workflow-run-detail-page.d.ts.map +1 -1
  86. package/dist/pages/workflow-run-detail-page.js +28 -6
  87. package/dist/pages/workflow-run-detail-page.js.map +1 -1
  88. package/dist/routes/inputs.d.ts +33 -0
  89. package/dist/routes/inputs.d.ts.map +1 -1
  90. package/dist/routes/inputs.js +49 -0
  91. package/dist/routes/inputs.js.map +1 -1
  92. package/dist/tsconfig.test.tsbuildinfo +1 -1
  93. package/package.json +9 -9
  94. package/src/components/job-graph/job-duration-label.tsx +5 -1
  95. package/src/components/job-graph/job-graph-content.tsx +7 -4
  96. package/src/components/job-graph/job-graph-view.tsx +6 -3
  97. package/src/components/job-graph/job-graph.stories.tsx +1 -1
  98. package/src/components/job-graph/job-node.stories.tsx +1 -1
  99. package/src/components/job-graph/job-node.test.tsx +27 -2
  100. package/src/components/job-graph/job-node.tsx +6 -6
  101. package/src/components/job-graph/types.ts +5 -1
  102. package/src/components/workflow-run-duration-label.tsx +1 -1
  103. package/src/components/workflow-run-number-label.tsx +1 -1
  104. package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +2 -2
  105. package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +23 -30
  106. package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +3 -22
  107. package/src/components/workflow-run-summary/workflow-run-summary.tsx +84 -69
  108. package/src/components/workflow-run-tabs/index.ts +5 -0
  109. package/src/components/workflow-run-tabs/run-annotation-summary-line.test.tsx +69 -0
  110. package/src/components/workflow-run-tabs/run-annotation-summary-line.tsx +97 -0
  111. package/src/components/workflow-run-tabs/run-annotations-empty.tsx +11 -0
  112. package/src/components/workflow-run-tabs/run-jobs-list.tsx +151 -0
  113. package/src/components/workflow-run-tabs/run-tab-count.tsx +28 -0
  114. package/src/components/workflow-run-tabs/run-tab-strip.stories.tsx +52 -0
  115. package/src/components/workflow-run-tabs/run-tab-strip.test.tsx +117 -0
  116. package/src/components/workflow-run-tabs/run-tab-strip.tsx +97 -0
  117. package/src/components/workflow-run-view/workflow-run-selection.test.ts +48 -0
  118. package/src/components/workflow-run-view/workflow-run-selection.ts +48 -1
  119. package/src/components/workflow-run-view/workflow-run-states.tsx +12 -1
  120. package/src/components/workflow-run-view/workflow-run-view.test.tsx +64 -64
  121. package/src/components/workflow-run-view/workflow-run-view.tsx +199 -85
  122. package/src/components/workflow-source-panel/index.ts +1 -0
  123. package/src/components/workflow-source-panel/workflow-source-content.tsx +84 -0
  124. package/src/components/workflow-source-panel/workflow-source-panel.tsx +14 -81
  125. package/src/core/entities/step.ts +4 -0
  126. package/src/core/workflow-run-tabs.ts +8 -0
  127. package/src/pages/workflow-run-detail-page.tsx +31 -4
  128. package/src/pages/workflow-run-pages.test.tsx +185 -45
  129. package/src/routes/inputs.test.ts +47 -0
  130. package/src/routes/inputs.ts +45 -0
  131. package/src/storybook-config.test.ts +27 -0
  132. package/tsconfig.build.tsbuildinfo +1 -1
  133. 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 { WorkflowSourcePanel } from '../workflow-source-panel/index.js';
14
- import { JobCard } from './job-card.js';
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`, or a skeleton while `workflowRunId` is still unknown (the page is
19
- * resolving which run to show) or the run is loading, so the page never branches on the
20
- * loading state itself.
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
- const sourceButtonRef = useRef(null);
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, degrade to whole-workflow
64
- // focus so the panel never points at an unmounted Source button.
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
- lastSourceTriggerRef.current = sourceButtonRef.current;
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 selectedJob = selectionControlled ? resolvedSelection?.job : runData.jobs.find((job)=>job.id === selectedJobId) ?? runData.jobs.at(0);
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
- // still pre-highlight when the summary opens the whole-workflow source.
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.sourceSnapshot;
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
- // focusing so the opener button is still expanded (force-visible) on return.
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
- const focusTarget = trigger?.isConnected ? trigger : fallbackTrigger;
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("div", {
214
- className: "flex min-w-0 flex-1 flex-col",
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("div", {
235
- className: "min-h-0 flex-1 overflow-auto bg-background-neutral-base p-16",
236
- children: /*#__PURE__*/ _jsxs("div", {
237
- className: "mx-auto flex w-full max-w-[1120px] flex-col gap-16",
238
- children: [
239
- /*#__PURE__*/ _jsx(JobGraph, {
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
- selectedJob ? /*#__PURE__*/ _jsx(JobCard, {
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: selectedJob.carriedOver ? undefined : 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
- }) : null
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":"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"}
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,2 +1,3 @@
1
+ export * from './workflow-source-content.js';
1
2
  export * from './workflow-source-panel.js';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -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,3 +1,4 @@
1
+ export * from './workflow-source-content.js';
1
2
  export * from './workflow-source-panel.js';
2
3
 
3
4
  //# sourceMappingURL=index.js.map
@@ -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 { type CodeBlockHighlightedLineRange } from '@shipfox/react-ui/code-block';
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,EAQL,KAAK,6BAA6B,EAEnC,MAAM,8BAA8B,CAAC;AAGtC,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,uBAAuB,CAAC;AAQlE,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,sCAgC1B"}
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"}